@@ -13,27 +13,167 @@ namespace Validation.PackageSigning.ValidateCertificate
1313 /// </summary>
1414 public class CertificateVerificationResult
1515 {
16+ /// <summary>
17+ /// Create a new verification result.
18+ /// </summary>
19+ /// <param name="status">The determined status of the verified certificate.</param>
20+ /// <param name="statusFlags">The flags explaining the certificate's status.</param>
21+ /// <param name="statusUpdateTime">The time the revocation info was published.</param>
22+ /// <param name="revocationTime">The date the certificate was revoked, if applicable.</param>
23+ public CertificateVerificationResult (
24+ EndCertificateStatus status ,
25+ X509ChainStatusFlags statusFlags ,
26+ DateTime ? statusUpdateTime = null ,
27+ DateTime ? revocationTime = null )
28+ {
29+ if ( status != EndCertificateStatus . Revoked && revocationTime . HasValue )
30+ {
31+ throw new ArgumentException (
32+ $ "End certificate revoked at { revocationTime } but status isn't { nameof ( EndCertificateStatus . Revoked ) } ",
33+ nameof ( status ) ) ;
34+ }
35+
36+ switch ( status )
37+ {
38+ case EndCertificateStatus . Good :
39+ if ( statusFlags != X509ChainStatusFlags . NoError )
40+ {
41+ throw new ArgumentException (
42+ $ "Invalid flags '{ statusFlags } ' for status '{ status } '",
43+ nameof ( statusFlags ) ) ;
44+ }
45+
46+ break ;
47+
48+ case EndCertificateStatus . Invalid :
49+ if ( statusFlags == X509ChainStatusFlags . NoError )
50+ {
51+ throw new ArgumentException (
52+ $ "Invalid flags '{ statusFlags } ' for status '{ status } '",
53+ nameof ( statusFlags ) ) ;
54+ }
55+
56+ break ;
57+
58+ case EndCertificateStatus . Revoked :
59+ if ( ( statusFlags & X509ChainStatusFlags . Revoked ) == 0 )
60+ {
61+ throw new ArgumentException (
62+ $ "Invalid flags '{ statusFlags } ' for status '{ status } '",
63+ nameof ( statusFlags ) ) ;
64+ }
65+ break ;
66+
67+ case EndCertificateStatus . Unknown :
68+ if ( ( statusFlags & ( X509ChainStatusFlags . RevocationStatusUnknown | X509ChainStatusFlags . OfflineRevocation ) ) == 0 )
69+ {
70+ throw new ArgumentException (
71+ $ "Invalid flags '{ statusFlags } ' for status '{ status } '",
72+ nameof ( statusFlags ) ) ;
73+ }
74+
75+ break ;
76+
77+ default :
78+ throw new ArgumentException ( $ "Unknown status '{ status } '", nameof ( status ) ) ;
79+ }
80+
81+ Status = status ;
82+ StatusFlags = statusFlags ;
83+ StatusUpdateTime = statusUpdateTime ;
84+ RevocationTime = revocationTime ;
85+ }
86+
1687 /// <summary>
1788 /// The status of the end <see cref="X509Certificate2"/>.
1889 /// </summary>
19- public EndCertificateStatus Status { get ; set ; }
90+ public EndCertificateStatus Status { get ; }
2091
2192 /// <summary>
22- /// The flattened flags for the <see cref="X509Certificate2"/> and its entire chain.
93+ /// The flattened flags for the <see cref="X509Certificate2"/>'s entire chain.
2394 /// </summary>
24- public X509ChainStatusFlags StatusFlags { get ; set ; }
95+ public X509ChainStatusFlags StatusFlags { get ; }
2596
2697 /// <summary>
2798 /// The time that the end <see cref="X509Certificate2"/>'s status was last updated, according to the
28- /// Certificate Authority. If <see cref="Status"/> is <see cref="EndCertificateStatus.Revoked "/>
29- /// or <see cref="EndCertificateStatus.Unknown"/>, this will have a value of <c>null</c> .
99+ /// Certificate Authority. This value may be <c>null</c> if the <see cref="Status "/> is
100+ /// <see cref="EndCertificateStatus.Unknown"/> or if the status could not be determined .
30101 /// </summary>
31- public DateTime ? StatusUpdateTime { get ; set ; }
102+ public DateTime ? StatusUpdateTime { get ; }
32103
33104 /// <summary>
34105 /// The time at which the end <see cref="X509Certificate2"/> was revoked. If <see cref="Status"/>
35106 /// is not <see cref="CertificateStatus.Revoked"/>, this will have a value of <c>null</c>.
36107 /// </summary>
37- public DateTime ? RevocationTime { get ; set ; }
108+ public DateTime ? RevocationTime { get ; }
109+
110+ /// <summary>
111+ /// Convert a verification to a human readable string.
112+ /// </summary>
113+ /// <returns>A human readable string that summarizes the verification result.</returns>
114+ public override string ToString ( )
115+ {
116+ switch ( Status )
117+ {
118+ case EndCertificateStatus . Good :
119+ return $ "Good (StatusUpdateTime = { StatusUpdateTime } )";
120+
121+ case EndCertificateStatus . Invalid :
122+ return $ "Invalid (Flags = { StatusFlags } , StatusUpdateTime = { StatusUpdateTime } )";
123+
124+ case EndCertificateStatus . Revoked :
125+ return $ "Revoked (Flags = { StatusFlags } , RevocationTime = { RevocationTime } , StatusUpdateTime = { StatusUpdateTime } )";
126+
127+ case EndCertificateStatus . Unknown :
128+ return $ "Unknown (Flags = { StatusFlags } , StatusUpdateTime = { StatusUpdateTime } )";
129+
130+ default :
131+ throw new InvalidOperationException ( $ "Unknown status { Status } ") ;
132+ }
133+ }
134+
135+ /// <summary>
136+ /// Helper used to create valid <see cref="CertificateVerificationResult"/>s.
137+ /// </summary>
138+ public class Builder
139+ {
140+ private EndCertificateStatus _status ;
141+ private X509ChainStatusFlags _statusFlags ;
142+ private DateTime ? _statusUpdateTime ;
143+ private DateTime ? _revocationTime ;
144+
145+ public Builder WithStatus ( EndCertificateStatus value )
146+ {
147+ _status = value ;
148+ return this ;
149+ }
150+
151+ public Builder WithStatusFlags ( X509ChainStatusFlags value )
152+ {
153+ _statusFlags = value ;
154+ return this ;
155+ }
156+
157+ public Builder WithStatusUpdateTime ( DateTime ? value )
158+ {
159+ _statusUpdateTime = value ;
160+ return this ;
161+ }
162+
163+ public Builder WithRevocationTime ( DateTime ? value )
164+ {
165+ _revocationTime = value ;
166+ return this ;
167+ }
168+
169+ public CertificateVerificationResult Build ( )
170+ {
171+ return new CertificateVerificationResult (
172+ _status ,
173+ _statusFlags ,
174+ _statusUpdateTime ,
175+ _revocationTime ) ;
176+ }
177+ }
38178 }
39179}
0 commit comments