11// SPDX-FileCopyrightText: Estonian Information System Authority
22// SPDX-License-Identifier: MIT
33
4- package eu .webeid .security . validator . certvalidators ;
4+ package eu .webeid .ocsp ;
55
6+ import eu .webeid .ocsp .client .OcspClient ;
7+ import eu .webeid .ocsp .protocol .DigestCalculatorImpl ;
8+ import eu .webeid .ocsp .protocol .OcspRequestBuilder ;
9+ import eu .webeid .ocsp .protocol .OcspResponseValidator ;
610import eu .webeid .security .exceptions .AuthTokenException ;
7- import eu .webeid .security .exceptions .UserCertificateOCSPCheckFailedException ;
11+ import eu .webeid .ocsp .exceptions .UserCertificateOCSPCheckFailedException ;
812import eu .webeid .security .util .DateAndTime ;
9- import eu .webeid .security .validator .ocsp .DigestCalculatorImpl ;
10- import eu .webeid .security .validator .ocsp .OcspClient ;
11- import eu .webeid .security .validator .ocsp .OcspRequestBuilder ;
12- import eu .webeid .security .validator .ocsp .OcspResponseValidator ;
13- import eu .webeid .security .validator .ocsp .OcspServiceProvider ;
14- import eu .webeid .security .validator .ocsp .service .OcspService ;
13+ import eu .webeid .ocsp .service .OcspServiceProvider ;
14+ import eu .webeid .ocsp .service .OcspService ;
15+ import eu .webeid .security .validator .revocationcheck .CertificateRevocationChecker ;
16+ import eu .webeid .security .validator .revocationcheck .RevocationInfo ;
1517import org .bouncycastle .asn1 .ocsp .OCSPObjectIdentifiers ;
1618import org .bouncycastle .asn1 .ocsp .OCSPResponseStatus ;
1719import org .bouncycastle .asn1 .x509 .Extension ;
3032
3133import java .io .IOException ;
3234import java .math .BigInteger ;
35+ import java .net .URI ;
3336import java .security .Security ;
3437import java .security .cert .CertificateEncodingException ;
3538import java .security .cert .CertificateException ;
3639import java .security .cert .X509Certificate ;
3740import java .time .Duration ;
3841import java .util .Date ;
39- import java .util .Objects ;
42+ import java .util .List ;
43+ import java .util .Map ;
4044
41- public final class SubjectCertificateNotRevokedValidator {
45+ import static eu .webeid .security .util .DateAndTime .requirePositiveDuration ;
46+ import static java .util .Objects .requireNonNull ;
4247
43- private static final Logger LOG = LoggerFactory .getLogger (SubjectCertificateNotRevokedValidator .class );
48+ public final class OcspCertificateRevocationChecker implements CertificateRevocationChecker {
49+
50+ public static final Duration DEFAULT_TIME_SKEW = Duration .ofMinutes (15 );
51+ public static final Duration DEFAULT_THIS_UPDATE_AGE = Duration .ofMinutes (2 );
52+
53+ private static final Logger LOG = LoggerFactory .getLogger (OcspCertificateRevocationChecker .class );
4454
45- private final SubjectCertificateTrustedValidator trustValidator ;
4655 private final OcspClient ocspClient ;
4756 private final OcspServiceProvider ocspServiceProvider ;
4857 private final Duration allowedOcspResponseTimeSkew ;
4958 private final Duration maxOcspResponseThisUpdateAge ;
5059
5160 static {
52- Security .addProvider (new BouncyCastleProvider ());
61+ if (Security .getProvider (BouncyCastleProvider .PROVIDER_NAME ) == null ) {
62+ Security .addProvider (new BouncyCastleProvider ());
63+ }
5364 }
5465
55- public SubjectCertificateNotRevokedValidator (SubjectCertificateTrustedValidator trustValidator ,
56- OcspClient ocspClient ,
57- OcspServiceProvider ocspServiceProvider ,
58- Duration allowedOcspResponseTimeSkew ,
59- Duration maxOcspResponseThisUpdateAge ) {
60- this .trustValidator = trustValidator ;
61- this .ocspClient = ocspClient ;
62- this .ocspServiceProvider = ocspServiceProvider ;
63- this .allowedOcspResponseTimeSkew = allowedOcspResponseTimeSkew ;
64- this .maxOcspResponseThisUpdateAge = maxOcspResponseThisUpdateAge ;
66+ public OcspCertificateRevocationChecker (OcspClient ocspClient ,
67+ OcspServiceProvider ocspServiceProvider ,
68+ Duration allowedOcspResponseTimeSkew ,
69+ Duration maxOcspResponseThisUpdateAge ) {
70+ this .ocspClient = requireNonNull (ocspClient , "ocspClient" );
71+ this .ocspServiceProvider = requireNonNull (ocspServiceProvider , "ocspServiceProvider" );
72+ this .allowedOcspResponseTimeSkew = requirePositiveDuration (allowedOcspResponseTimeSkew , "allowedOcspResponseTimeSkew" );
73+ this .maxOcspResponseThisUpdateAge = requirePositiveDuration (maxOcspResponseThisUpdateAge , "maxOcspResponseThisUpdateAge" );
6574 }
6675
6776 /**
68- * Validates that the user certificate from the authentication token is not revoked with OCSP .
77+ * Validates with OCSP that the user certificate from the authentication token is not revoked.
6978 *
7079 * @param subjectCertificate user certificate to be validated
7180 * @throws AuthTokenException when user certificate is revoked or revocation check fails.
7281 */
73- public void validateCertificateNotRevoked (X509Certificate subjectCertificate ) throws AuthTokenException {
82+ @ Override
83+ public List <RevocationInfo > validateCertificateNotRevoked (X509Certificate subjectCertificate , X509Certificate issuerCertificate ) throws AuthTokenException {
84+ requireNonNull (subjectCertificate , "subjectCertificate" );
85+ requireNonNull (issuerCertificate , "issuerCertificate" );
86+
87+ URI ocspResponderUri = null ;
7488 try {
7589 OcspService ocspService = ocspServiceProvider .getService (subjectCertificate );
90+ ocspResponderUri = requireNonNull (ocspService .getAccessLocation (), "ocspResponderUri" );
7691
77- final CertificateID certificateId = getCertificateId (subjectCertificate ,
78- Objects .requireNonNull (trustValidator .getSubjectCertificateIssuerCertificate ()));
92+ final CertificateID certificateId = getCertificateId (subjectCertificate , issuerCertificate );
7993
8094 final OCSPReq request = new OcspRequestBuilder ()
8195 .withCertificateId (certificateId )
@@ -87,21 +101,27 @@ public void validateCertificateNotRevoked(X509Certificate subjectCertificate) th
87101 }
88102
89103 LOG .debug ("Sending OCSP request" );
90- final OCSPResp response = Objects . requireNonNull (ocspClient .request (ocspService . getAccessLocation () , request ));
104+ final OCSPResp response = requireNonNull (ocspClient .request (ocspResponderUri , request ), "OCSPResp" );
91105 if (response .getStatus () != OCSPResponseStatus .SUCCESSFUL ) {
92- throw new UserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()));
106+ throw new UserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()), ocspResponderUri );
93107 }
94108
95109 final BasicOCSPResp basicResponse = (BasicOCSPResp ) response .getResponseObject ();
96110 if (basicResponse == null ) {
97- throw new UserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" );
111+ throw new UserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" , ocspResponderUri );
98112 }
113+ LOG .debug ("OCSP response received successfully" );
114+
99115 verifyOcspResponse (basicResponse , ocspService , certificateId );
100116 if (ocspService .doesSupportNonce ()) {
101- checkNonce (request , basicResponse );
117+ checkNonce (request , basicResponse , ocspResponderUri );
102118 }
119+ LOG .debug ("OCSP response verified successfully" );
120+
121+ return List .of (new RevocationInfo (ocspResponderUri , Map .of (RevocationInfo .KEY_OCSP_RESPONSE , response )));
122+
103123 } catch (OCSPException | CertificateException | OperatorCreationException | IOException e ) {
104- throw new UserCertificateOCSPCheckFailedException (e );
124+ throw new UserCertificateOCSPCheckFailedException (e , ocspResponderUri );
105125 }
106126 }
107127
@@ -118,11 +138,12 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
118138 // As we sent the request for only a single certificate, we expect only a single response.
119139 if (basicResponse .getResponses ().length != 1 ) {
120140 throw new UserCertificateOCSPCheckFailedException ("OCSP response must contain one response, "
121- + "received " + basicResponse .getResponses ().length + " responses instead" );
141+ + "received " + basicResponse .getResponses ().length + " responses instead" , ocspService . getAccessLocation () );
122142 }
123143 final SingleResp certStatusResponse = basicResponse .getResponses ()[0 ];
124144 if (!requestCertificateId .equals (certStatusResponse .getCertID ())) {
125- throw new UserCertificateOCSPCheckFailedException ("OCSP responded with certificate ID that differs from the requested ID" );
145+ throw new UserCertificateOCSPCheckFailedException ("OCSP responded with certificate ID that differs from the requested ID" ,
146+ ocspService .getAccessLocation ());
126147 }
127148
128149 // 2. The signature on the response is valid.
@@ -132,11 +153,11 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
132153 // is standard practice.
133154 if (basicResponse .getCerts ().length < 1 ) {
134155 throw new UserCertificateOCSPCheckFailedException ("OCSP response must contain the responder certificate, "
135- + "but none was provided" );
156+ + "but none was provided" , ocspService . getAccessLocation () );
136157 }
137158 // The first certificate is the responder certificate, other certificates, if given, are the certificate's chain.
138159 final X509CertificateHolder responderCert = basicResponse .getCerts ()[0 ];
139- OcspResponseValidator .validateResponseSignature (basicResponse , responderCert );
160+ OcspResponseValidator .validateResponseSignature (basicResponse , responderCert , ocspService . getAccessLocation () );
140161
141162 // 3. The identity of the signer matches the intended recipient of the
142163 // request.
@@ -155,23 +176,23 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
155176 // be available about the status of the certificate (nextUpdate) is
156177 // greater than the current time.
157178
158- OcspResponseValidator .validateCertificateStatusUpdateTime (certStatusResponse , allowedOcspResponseTimeSkew , maxOcspResponseThisUpdateAge );
179+ OcspResponseValidator .validateCertificateStatusUpdateTime (certStatusResponse , allowedOcspResponseTimeSkew , maxOcspResponseThisUpdateAge , ocspService . getAccessLocation () );
159180
160181 // Now we can accept the signed response as valid and validate the certificate status.
161- OcspResponseValidator .validateSubjectCertificateStatus (certStatusResponse );
182+ OcspResponseValidator .validateSubjectCertificateStatus (certStatusResponse , ocspService . getAccessLocation () );
162183 LOG .debug ("OCSP check result is GOOD" );
163184 }
164185
165- private static void checkNonce (OCSPReq request , BasicOCSPResp response ) throws UserCertificateOCSPCheckFailedException {
186+ private static void checkNonce (OCSPReq request , BasicOCSPResp response , URI ocspResponderUri ) throws UserCertificateOCSPCheckFailedException {
166187 final Extension requestNonce = request .getExtension (OCSPObjectIdentifiers .id_pkix_ocsp_nonce );
167188 final Extension responseNonce = response .getExtension (OCSPObjectIdentifiers .id_pkix_ocsp_nonce );
168189 if (requestNonce == null || responseNonce == null ) {
169190 throw new UserCertificateOCSPCheckFailedException ("OCSP request or response nonce extension missing, " +
170- "possible replay attack" );
191+ "possible replay attack" , ocspResponderUri );
171192 }
172193 if (!requestNonce .equals (responseNonce )) {
173194 throw new UserCertificateOCSPCheckFailedException ("OCSP request and response nonces differ, " +
174- "possible replay attack" );
195+ "possible replay attack" , ocspResponderUri );
175196 }
176197 }
177198
@@ -183,20 +204,14 @@ private static CertificateID getCertificateId(X509Certificate subjectCertificate
183204 }
184205
185206 private static String ocspStatusToString (int status ) {
186- switch (status ) {
187- case OCSPResp .MALFORMED_REQUEST :
188- return "malformed request" ;
189- case OCSPResp .INTERNAL_ERROR :
190- return "internal error" ;
191- case OCSPResp .TRY_LATER :
192- return "service unavailable" ;
193- case OCSPResp .SIG_REQUIRED :
194- return "request signature missing" ;
195- case OCSPResp .UNAUTHORIZED :
196- return "unauthorized" ;
197- default :
198- return "unknown" ;
199- }
207+ return switch (status ) {
208+ case OCSPResp .MALFORMED_REQUEST -> "malformed request" ;
209+ case OCSPResp .INTERNAL_ERROR -> "internal error" ;
210+ case OCSPResp .TRY_LATER -> "service unavailable" ;
211+ case OCSPResp .SIG_REQUIRED -> "request signature missing" ;
212+ case OCSPResp .UNAUTHORIZED -> "unauthorized" ;
213+ default -> "unknown" ;
214+ };
200215 }
201216
202217}
0 commit comments