2020 * SOFTWARE.
2121 */
2222
23- package eu .webeid .security . validator . certvalidators ;
23+ package eu .webeid .ocsp ;
2424
25+ import eu .webeid .ocsp .client .OcspClient ;
26+ import eu .webeid .ocsp .protocol .DigestCalculatorImpl ;
27+ import eu .webeid .ocsp .protocol .OcspRequestBuilder ;
28+ import eu .webeid .ocsp .protocol .OcspResponseValidator ;
2529import eu .webeid .security .exceptions .AuthTokenException ;
26- import eu .webeid .security .exceptions .UserCertificateOCSPCheckFailedException ;
30+ import eu .webeid .ocsp .exceptions .UserCertificateOCSPCheckFailedException ;
2731import eu .webeid .security .util .DateAndTime ;
28- import eu .webeid .security .validator .ocsp .DigestCalculatorImpl ;
29- import eu .webeid .security .validator .ocsp .OcspClient ;
30- import eu .webeid .security .validator .ocsp .OcspRequestBuilder ;
31- import eu .webeid .security .validator .ocsp .OcspResponseValidator ;
32- import eu .webeid .security .validator .ocsp .OcspServiceProvider ;
33- import eu .webeid .security .validator .ocsp .service .OcspService ;
32+ import eu .webeid .ocsp .service .OcspServiceProvider ;
33+ import eu .webeid .ocsp .service .OcspService ;
34+ import eu .webeid .security .validator .revocationcheck .OcspCertificateRevocationChecker ;
35+ import eu .webeid .security .validator .revocationcheck .RevocationInfo ;
3436import org .bouncycastle .asn1 .ocsp .OCSPObjectIdentifiers ;
3537import org .bouncycastle .asn1 .ocsp .OCSPResponseStatus ;
3638import org .bouncycastle .asn1 .x509 .Extension ;
4951
5052import java .io .IOException ;
5153import java .math .BigInteger ;
54+ import java .net .URI ;
5255import java .security .Security ;
5356import java .security .cert .CertificateEncodingException ;
5457import java .security .cert .CertificateException ;
5558import java .security .cert .X509Certificate ;
5659import java .time .Duration ;
5760import java .util .Date ;
58- import java .util .Objects ;
61+ import java .util .List ;
62+ import java .util .Map ;
5963
60- public final class SubjectCertificateNotRevokedValidator {
64+ import static eu .webeid .security .util .DateAndTime .requirePositiveDuration ;
65+ import static java .util .Objects .requireNonNull ;
6166
62- private static final Logger LOG = LoggerFactory .getLogger (SubjectCertificateNotRevokedValidator .class );
67+ public final class DefaultOcspCertificateRevocationChecker implements OcspCertificateRevocationChecker {
68+
69+ public static final Duration DEFAULT_TIME_SKEW = Duration .ofMinutes (15 );
70+ public static final Duration DEFAULT_THIS_UPDATE_AGE = Duration .ofMinutes (2 );
71+
72+ private static final Logger LOG = LoggerFactory .getLogger (DefaultOcspCertificateRevocationChecker .class );
6373
64- private final SubjectCertificateTrustedValidator trustValidator ;
6574 private final OcspClient ocspClient ;
6675 private final OcspServiceProvider ocspServiceProvider ;
6776 private final Duration allowedOcspResponseTimeSkew ;
6877 private final Duration maxOcspResponseThisUpdateAge ;
6978
7079 static {
71- Security .addProvider (new BouncyCastleProvider ());
80+ if (Security .getProvider (BouncyCastleProvider .PROVIDER_NAME ) == null ) {
81+ Security .addProvider (new BouncyCastleProvider ());
82+ }
7283 }
7384
74- public SubjectCertificateNotRevokedValidator (SubjectCertificateTrustedValidator trustValidator ,
75- OcspClient ocspClient ,
76- OcspServiceProvider ocspServiceProvider ,
77- Duration allowedOcspResponseTimeSkew ,
78- Duration maxOcspResponseThisUpdateAge ) {
79- this .trustValidator = trustValidator ;
80- this .ocspClient = ocspClient ;
81- this .ocspServiceProvider = ocspServiceProvider ;
82- this .allowedOcspResponseTimeSkew = allowedOcspResponseTimeSkew ;
83- this .maxOcspResponseThisUpdateAge = maxOcspResponseThisUpdateAge ;
85+ public DefaultOcspCertificateRevocationChecker (OcspClient ocspClient ,
86+ OcspServiceProvider ocspServiceProvider ,
87+ Duration allowedOcspResponseTimeSkew ,
88+ Duration maxOcspResponseThisUpdateAge ) {
89+ this .ocspClient = requireNonNull (ocspClient , "ocspClient" );
90+ this .ocspServiceProvider = requireNonNull (ocspServiceProvider , "ocspServiceProvider" );
91+ this .allowedOcspResponseTimeSkew = requirePositiveDuration (allowedOcspResponseTimeSkew , "allowedOcspResponseTimeSkew" );
92+ this .maxOcspResponseThisUpdateAge = requirePositiveDuration (maxOcspResponseThisUpdateAge , "maxOcspResponseThisUpdateAge" );
8493 }
8594
8695 /**
87- * Validates that the user certificate from the authentication token is not revoked with OCSP .
96+ * Validates with OCSP that the user certificate from the authentication token is not revoked.
8897 *
8998 * @param subjectCertificate user certificate to be validated
9099 * @throws AuthTokenException when user certificate is revoked or revocation check fails.
91100 */
92- public void validateCertificateNotRevoked (X509Certificate subjectCertificate ) throws AuthTokenException {
101+ @ Override
102+ public List <RevocationInfo > validateCertificateNotRevoked (X509Certificate subjectCertificate , X509Certificate issuerCertificate ) throws AuthTokenException {
103+ requireNonNull (subjectCertificate , "subjectCertificate" );
104+ requireNonNull (issuerCertificate , "issuerCertificate" );
105+
106+ URI ocspResponderUri = null ;
93107 try {
94108 OcspService ocspService = ocspServiceProvider .getService (subjectCertificate );
109+ ocspResponderUri = requireNonNull (ocspService .getAccessLocation (), "ocspResponderUri" );
95110
96- final CertificateID certificateId = getCertificateId (subjectCertificate ,
97- Objects .requireNonNull (trustValidator .getSubjectCertificateIssuerCertificate ()));
111+ final CertificateID certificateId = getCertificateId (subjectCertificate , issuerCertificate );
98112
99113 final OCSPReq request = new OcspRequestBuilder ()
100114 .withCertificateId (certificateId )
@@ -106,21 +120,27 @@ public void validateCertificateNotRevoked(X509Certificate subjectCertificate) th
106120 }
107121
108122 LOG .debug ("Sending OCSP request" );
109- final OCSPResp response = Objects . requireNonNull (ocspClient .request (ocspService . getAccessLocation () , request ));
123+ final OCSPResp response = requireNonNull (ocspClient .request (ocspResponderUri , request ), "OCSPResp" );
110124 if (response .getStatus () != OCSPResponseStatus .SUCCESSFUL ) {
111- throw new UserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()));
125+ throw new UserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()), ocspResponderUri );
112126 }
113127
114128 final BasicOCSPResp basicResponse = (BasicOCSPResp ) response .getResponseObject ();
115129 if (basicResponse == null ) {
116- throw new UserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" );
130+ throw new UserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" , ocspResponderUri );
117131 }
132+ LOG .debug ("OCSP response received successfully" );
133+
118134 verifyOcspResponse (basicResponse , ocspService , certificateId );
119135 if (ocspService .doesSupportNonce ()) {
120- checkNonce (request , basicResponse );
136+ checkNonce (request , basicResponse , ocspResponderUri );
121137 }
138+ LOG .debug ("OCSP response verified successfully" );
139+
140+ return List .of (new RevocationInfo (ocspResponderUri , Map .of (RevocationInfo .KEY_OCSP_RESPONSE , response )));
141+
122142 } catch (OCSPException | CertificateException | OperatorCreationException | IOException e ) {
123- throw new UserCertificateOCSPCheckFailedException (e );
143+ throw new UserCertificateOCSPCheckFailedException (e , ocspResponderUri );
124144 }
125145 }
126146
@@ -137,11 +157,12 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
137157 // As we sent the request for only a single certificate, we expect only a single response.
138158 if (basicResponse .getResponses ().length != 1 ) {
139159 throw new UserCertificateOCSPCheckFailedException ("OCSP response must contain one response, "
140- + "received " + basicResponse .getResponses ().length + " responses instead" );
160+ + "received " + basicResponse .getResponses ().length + " responses instead" , ocspService . getAccessLocation () );
141161 }
142162 final SingleResp certStatusResponse = basicResponse .getResponses ()[0 ];
143163 if (!requestCertificateId .equals (certStatusResponse .getCertID ())) {
144- throw new UserCertificateOCSPCheckFailedException ("OCSP responded with certificate ID that differs from the requested ID" );
164+ throw new UserCertificateOCSPCheckFailedException ("OCSP responded with certificate ID that differs from the requested ID" ,
165+ ocspService .getAccessLocation ());
145166 }
146167
147168 // 2. The signature on the response is valid.
@@ -151,11 +172,11 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
151172 // is standard practice.
152173 if (basicResponse .getCerts ().length < 1 ) {
153174 throw new UserCertificateOCSPCheckFailedException ("OCSP response must contain the responder certificate, "
154- + "but none was provided" );
175+ + "but none was provided" , ocspService . getAccessLocation () );
155176 }
156177 // The first certificate is the responder certificate, other certificates, if given, are the certificate's chain.
157178 final X509CertificateHolder responderCert = basicResponse .getCerts ()[0 ];
158- OcspResponseValidator .validateResponseSignature (basicResponse , responderCert );
179+ OcspResponseValidator .validateResponseSignature (basicResponse , responderCert , ocspService . getAccessLocation () );
159180
160181 // 3. The identity of the signer matches the intended recipient of the
161182 // request.
@@ -174,23 +195,23 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
174195 // be available about the status of the certificate (nextUpdate) is
175196 // greater than the current time.
176197
177- OcspResponseValidator .validateCertificateStatusUpdateTime (certStatusResponse , allowedOcspResponseTimeSkew , maxOcspResponseThisUpdateAge );
198+ OcspResponseValidator .validateCertificateStatusUpdateTime (certStatusResponse , allowedOcspResponseTimeSkew , maxOcspResponseThisUpdateAge , ocspService . getAccessLocation () );
178199
179200 // Now we can accept the signed response as valid and validate the certificate status.
180- OcspResponseValidator .validateSubjectCertificateStatus (certStatusResponse );
201+ OcspResponseValidator .validateSubjectCertificateStatus (certStatusResponse , ocspService . getAccessLocation () );
181202 LOG .debug ("OCSP check result is GOOD" );
182203 }
183204
184- private static void checkNonce (OCSPReq request , BasicOCSPResp response ) throws UserCertificateOCSPCheckFailedException {
205+ private static void checkNonce (OCSPReq request , BasicOCSPResp response , URI ocspResponderUri ) throws UserCertificateOCSPCheckFailedException {
185206 final Extension requestNonce = request .getExtension (OCSPObjectIdentifiers .id_pkix_ocsp_nonce );
186207 final Extension responseNonce = response .getExtension (OCSPObjectIdentifiers .id_pkix_ocsp_nonce );
187208 if (requestNonce == null || responseNonce == null ) {
188209 throw new UserCertificateOCSPCheckFailedException ("OCSP request or response nonce extension missing, " +
189- "possible replay attack" );
210+ "possible replay attack" , ocspResponderUri );
190211 }
191212 if (!requestNonce .equals (responseNonce )) {
192213 throw new UserCertificateOCSPCheckFailedException ("OCSP request and response nonces differ, " +
193- "possible replay attack" );
214+ "possible replay attack" , ocspResponderUri );
194215 }
195216 }
196217
@@ -202,20 +223,14 @@ private static CertificateID getCertificateId(X509Certificate subjectCertificate
202223 }
203224
204225 private static String ocspStatusToString (int status ) {
205- switch (status ) {
206- case OCSPResp .MALFORMED_REQUEST :
207- return "malformed request" ;
208- case OCSPResp .INTERNAL_ERROR :
209- return "internal error" ;
210- case OCSPResp .TRY_LATER :
211- return "service unavailable" ;
212- case OCSPResp .SIG_REQUIRED :
213- return "request signature missing" ;
214- case OCSPResp .UNAUTHORIZED :
215- return "unauthorized" ;
216- default :
217- return "unknown" ;
218- }
226+ return switch (status ) {
227+ case OCSPResp .MALFORMED_REQUEST -> "malformed request" ;
228+ case OCSPResp .INTERNAL_ERROR -> "internal error" ;
229+ case OCSPResp .TRY_LATER -> "service unavailable" ;
230+ case OCSPResp .SIG_REQUIRED -> "request signature missing" ;
231+ case OCSPResp .UNAUTHORIZED -> "unauthorized" ;
232+ default -> "unknown" ;
233+ };
219234 }
220235
221236}
0 commit comments