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 ;
2630import eu .webeid .security .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 ;
@@ -71,30 +80,33 @@ public final class SubjectCertificateNotRevokedValidator {
7180 Security .addProvider (new BouncyCastleProvider ());
7281 }
7382
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 ;
83+ public DefaultOcspCertificateRevocationChecker (OcspClient ocspClient ,
84+ OcspServiceProvider ocspServiceProvider ,
85+ Duration allowedOcspResponseTimeSkew ,
86+ Duration maxOcspResponseThisUpdateAge ) {
87+ this .ocspClient = requireNonNull (ocspClient , "ocspClient" );
88+ this .ocspServiceProvider = requireNonNull (ocspServiceProvider , "ocspServiceProvider" );
89+ this .allowedOcspResponseTimeSkew = requirePositiveDuration (allowedOcspResponseTimeSkew , "allowedOcspResponseTimeSkew" );
90+ this .maxOcspResponseThisUpdateAge = requirePositiveDuration (maxOcspResponseThisUpdateAge , "maxOcspResponseThisUpdateAge" );
8491 }
8592
8693 /**
87- * Validates that the user certificate from the authentication token is not revoked with OCSP .
94+ * Validates with OCSP that the user certificate from the authentication token is not revoked.
8895 *
8996 * @param subjectCertificate user certificate to be validated
9097 * @throws AuthTokenException when user certificate is revoked or revocation check fails.
9198 */
92- public void validateCertificateNotRevoked (X509Certificate subjectCertificate ) throws AuthTokenException {
99+ @ Override
100+ public List <RevocationInfo > validateCertificateNotRevoked (X509Certificate subjectCertificate , X509Certificate issuerCertificate ) throws AuthTokenException {
101+ requireNonNull (subjectCertificate , "subjectCertificate" );
102+ requireNonNull (issuerCertificate , "issuerCertificate" );
103+
104+ URI ocspResponderUri = null ;
93105 try {
94106 OcspService ocspService = ocspServiceProvider .getService (subjectCertificate );
107+ ocspResponderUri = requireNonNull (ocspService .getAccessLocation (), "ocspResponderUri" );
95108
96- final CertificateID certificateId = getCertificateId (subjectCertificate ,
97- Objects .requireNonNull (trustValidator .getSubjectCertificateIssuerCertificate ()));
109+ final CertificateID certificateId = getCertificateId (subjectCertificate , issuerCertificate );
98110
99111 final OCSPReq request = new OcspRequestBuilder ()
100112 .withCertificateId (certificateId )
@@ -106,21 +118,27 @@ public void validateCertificateNotRevoked(X509Certificate subjectCertificate) th
106118 }
107119
108120 LOG .debug ("Sending OCSP request" );
109- final OCSPResp response = Objects . requireNonNull (ocspClient .request (ocspService . getAccessLocation () , request ));
121+ final OCSPResp response = requireNonNull (ocspClient .request (ocspResponderUri , request ), "OCSPResp" );
110122 if (response .getStatus () != OCSPResponseStatus .SUCCESSFUL ) {
111- throw new UserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()));
123+ throw new UserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()), ocspResponderUri );
112124 }
113125
114126 final BasicOCSPResp basicResponse = (BasicOCSPResp ) response .getResponseObject ();
115127 if (basicResponse == null ) {
116- throw new UserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" );
128+ throw new UserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" , ocspResponderUri );
117129 }
130+ LOG .debug ("OCSP response received successfully" );
131+
118132 verifyOcspResponse (basicResponse , ocspService , certificateId );
119133 if (ocspService .doesSupportNonce ()) {
120- checkNonce (request , basicResponse );
134+ checkNonce (request , basicResponse , ocspResponderUri );
121135 }
136+ LOG .debug ("OCSP response verified successfully" );
137+
138+ return List .of (new RevocationInfo (ocspResponderUri , Map .of (RevocationInfo .KEY_OCSP_RESPONSE , response )));
139+
122140 } catch (OCSPException | CertificateException | OperatorCreationException | IOException e ) {
123- throw new UserCertificateOCSPCheckFailedException (e );
141+ throw new UserCertificateOCSPCheckFailedException (e , ocspResponderUri );
124142 }
125143 }
126144
@@ -137,11 +155,12 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
137155 // As we sent the request for only a single certificate, we expect only a single response.
138156 if (basicResponse .getResponses ().length != 1 ) {
139157 throw new UserCertificateOCSPCheckFailedException ("OCSP response must contain one response, "
140- + "received " + basicResponse .getResponses ().length + " responses instead" );
158+ + "received " + basicResponse .getResponses ().length + " responses instead" , ocspService . getAccessLocation () );
141159 }
142160 final SingleResp certStatusResponse = basicResponse .getResponses ()[0 ];
143161 if (!requestCertificateId .equals (certStatusResponse .getCertID ())) {
144- throw new UserCertificateOCSPCheckFailedException ("OCSP responded with certificate ID that differs from the requested ID" );
162+ throw new UserCertificateOCSPCheckFailedException ("OCSP responded with certificate ID that differs from the requested ID" ,
163+ ocspService .getAccessLocation ());
145164 }
146165
147166 // 2. The signature on the response is valid.
@@ -151,11 +170,11 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
151170 // is standard practice.
152171 if (basicResponse .getCerts ().length < 1 ) {
153172 throw new UserCertificateOCSPCheckFailedException ("OCSP response must contain the responder certificate, "
154- + "but none was provided" );
173+ + "but none was provided" , ocspService . getAccessLocation () );
155174 }
156175 // The first certificate is the responder certificate, other certificates, if given, are the certificate's chain.
157176 final X509CertificateHolder responderCert = basicResponse .getCerts ()[0 ];
158- OcspResponseValidator .validateResponseSignature (basicResponse , responderCert );
177+ OcspResponseValidator .validateResponseSignature (basicResponse , responderCert , ocspService . getAccessLocation () );
159178
160179 // 3. The identity of the signer matches the intended recipient of the
161180 // request.
@@ -174,23 +193,23 @@ private void verifyOcspResponse(BasicOCSPResp basicResponse, OcspService ocspSer
174193 // be available about the status of the certificate (nextUpdate) is
175194 // greater than the current time.
176195
177- OcspResponseValidator .validateCertificateStatusUpdateTime (certStatusResponse , allowedOcspResponseTimeSkew , maxOcspResponseThisUpdateAge );
196+ OcspResponseValidator .validateCertificateStatusUpdateTime (certStatusResponse , allowedOcspResponseTimeSkew , maxOcspResponseThisUpdateAge , ocspService . getAccessLocation () );
178197
179198 // Now we can accept the signed response as valid and validate the certificate status.
180- OcspResponseValidator .validateSubjectCertificateStatus (certStatusResponse );
199+ OcspResponseValidator .validateSubjectCertificateStatus (certStatusResponse , ocspService . getAccessLocation () );
181200 LOG .debug ("OCSP check result is GOOD" );
182201 }
183202
184- private static void checkNonce (OCSPReq request , BasicOCSPResp response ) throws UserCertificateOCSPCheckFailedException {
203+ private static void checkNonce (OCSPReq request , BasicOCSPResp response , URI ocspResponderUri ) throws UserCertificateOCSPCheckFailedException {
185204 final Extension requestNonce = request .getExtension (OCSPObjectIdentifiers .id_pkix_ocsp_nonce );
186205 final Extension responseNonce = response .getExtension (OCSPObjectIdentifiers .id_pkix_ocsp_nonce );
187206 if (requestNonce == null || responseNonce == null ) {
188207 throw new UserCertificateOCSPCheckFailedException ("OCSP request or response nonce extension missing, " +
189- "possible replay attack" );
208+ "possible replay attack" , ocspResponderUri );
190209 }
191210 if (!requestNonce .equals (responseNonce )) {
192211 throw new UserCertificateOCSPCheckFailedException ("OCSP request and response nonces differ, " +
193- "possible replay attack" );
212+ "possible replay attack" , ocspResponderUri );
194213 }
195214 }
196215
@@ -202,20 +221,14 @@ private static CertificateID getCertificateId(X509Certificate subjectCertificate
202221 }
203222
204223 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- }
224+ return switch (status ) {
225+ case OCSPResp .MALFORMED_REQUEST -> "malformed request" ;
226+ case OCSPResp .INTERNAL_ERROR -> "internal error" ;
227+ case OCSPResp .TRY_LATER -> "service unavailable" ;
228+ case OCSPResp .SIG_REQUIRED -> "request signature missing" ;
229+ case OCSPResp .UNAUTHORIZED -> "unauthorized" ;
230+ default -> "unknown" ;
231+ };
219232 }
220233
221234}
0 commit comments