22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
44using System ;
5+ using System . Security . Cryptography . X509Certificates ;
56using NuGet . Services . Validation ;
67
78namespace Validation . PackageSigning . ValidateCertificate
@@ -13,39 +14,166 @@ namespace Validation.PackageSigning.ValidateCertificate
1314 public class CertificateVerificationResult
1415 {
1516 /// <summary>
16- /// Create a new non-revoked certificate verification result.
17+ /// Create a new verification result.
1718 /// </summary>
18- /// <param name="status">The status of the <see cref="X509Certificate2"/></param>
19- /// <param name="revocationTime">The time of </param>
20- public CertificateVerificationResult ( EndCertificateStatus status )
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 )
2128 {
22- if ( status == EndCertificateStatus . Revoked )
29+ if ( status != EndCertificateStatus . Revoked && revocationTime . HasValue )
2330 {
24- throw new ArgumentException ( "Provide a revocation date for a revoked certificate result." , nameof ( status ) ) ;
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 ) ) ;
2579 }
2680
2781 Status = status ;
82+ StatusFlags = statusFlags ;
83+ StatusUpdateTime = statusUpdateTime ;
84+ RevocationTime = revocationTime ;
2885 }
2986
3087 /// <summary>
31- /// Create a new revoked certificate verification result .
88+ /// The status of the end <see cref="X509Certificate2"/> .
3289 /// </summary>
33- /// <param name="revocationTime">The start of the certificate's invalidity period.</param>
34- public CertificateVerificationResult ( DateTime revocationTime )
35- {
36- Status = EndCertificateStatus . Revoked ;
37- RevocationTime = revocationTime ;
38- }
90+ public EndCertificateStatus Status { get ; }
3991
4092 /// <summary>
41- /// The status of the <see cref="X509Certificate2"/>.
93+ /// The flattened flags for the <see cref="X509Certificate2"/>'s entire chain .
4294 /// </summary>
43- public EndCertificateStatus Status { get ; }
95+ public X509ChainStatusFlags StatusFlags { get ; }
96+
97+ /// <summary>
98+ /// The time that the end <see cref="X509Certificate2"/>'s status was last updated, according to the
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.
101+ /// </summary>
102+ public DateTime ? StatusUpdateTime { get ; }
44103
45104 /// <summary>
46- /// The time at which the <see cref="X509Certificate2"/> was revoked. Null unless
47- /// <see cref="Status"/> is <see cref="CertificateStatus.Revoked"/>.
105+ /// The time at which the end <see cref="X509Certificate2"/> was revoked. If <see cref="Status"/>
106+ /// is not <see cref="CertificateStatus.Revoked"/>, this will have a value of <c>null</c >.
48107 /// </summary>
49108 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+ }
50178 }
51179}
0 commit comments