@@ -46,14 +46,19 @@ This file is part of the iText (R) project.
46
46
47
47
import java .io .IOException ;
48
48
import java .security .GeneralSecurityException ;
49
+ import java .security .KeyStoreException ;
50
+ import java .security .cert .CRL ;
49
51
import java .security .cert .Certificate ;
52
+ import java .security .cert .CertificateParsingException ;
53
+ import java .security .cert .X509CRL ;
50
54
import java .security .cert .X509Certificate ;
51
55
import java .text .MessageFormat ;
52
56
import java .util .ArrayList ;
53
57
import java .util .Date ;
54
58
import java .util .Enumeration ;
55
59
import java .util .List ;
56
60
61
+ import org .bouncycastle .asn1 .ocsp .OCSPObjectIdentifiers ;
57
62
import org .bouncycastle .cert .X509CertificateHolder ;
58
63
import org .bouncycastle .cert .jcajce .JcaX509CertificateConverter ;
59
64
import org .bouncycastle .cert .ocsp .BasicOCSPResp ;
@@ -76,6 +81,8 @@ public class OCSPVerifier extends RootStoreVerifier {
76
81
/** The Logger instance */
77
82
protected static final Logger LOGGER = LoggerFactory .getLogger (OCSPVerifier .class );
78
83
84
+ protected final static String id_kp_OCSPSigning = "1.3.6.1.5.5.7.3.9" ;
85
+
79
86
/** The list of OCSP responses. */
80
87
protected List <BasicOCSPResp > ocsps ;
81
88
@@ -97,7 +104,7 @@ public OCSPVerifier(CertificateVerifier verifier, List<BasicOCSPResp> ocsps) {
97
104
* @param issuerCert its issuer
98
105
* @return a list of <code>VerificationOK</code> objects.
99
106
* The list will be empty if the certificate couldn't be verified.
100
- * @see com.itextpdf.text.pdf.security .RootStoreVerifier#verify(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.util.Date)
107
+ * @see com.itextpdf.signatures .RootStoreVerifier#verify(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.util.Date)
101
108
*/
102
109
public List <VerificationOK > verify (X509Certificate signCert ,
103
110
X509Certificate issuerCert , Date signDate )
@@ -132,11 +139,11 @@ public List<VerificationOK> verify(X509Certificate signCert,
132
139
133
140
/**
134
141
* Verifies a certificate against a single OCSP response
135
- * @param ocspResp the OCSP response
136
- * @param serialNumber the serial number of the certificate that needs to be checked
137
- * @param issuerCert
138
- * @param signDate
139
- * @return
142
+ * @param ocspResp the OCSP response
143
+ * @param signCert the certificate that needs to be checked
144
+ * @param issuerCert the certificate of CA
145
+ * @param signDate sign date
146
+ * @return {@code true}, in case successful check, otherwise false.
140
147
* @throws GeneralSecurityException
141
148
* @throws IOException
142
149
*/
@@ -183,65 +190,126 @@ public boolean verify(BasicOCSPResp ocspResp, X509Certificate signCert, X509Cert
183
190
184
191
/**
185
192
* Verifies if an OCSP response is genuine
186
- * @param ocspResp the OCSP response
187
- * @param issuerCert the issuer certificate
193
+ * If it doesn't verify against the issuer certificate and response's certificates, it may verify
194
+ * using a trusted anchor or cert.
195
+ * @param ocspResp the OCSP response
196
+ * @param issuerCert the issuer certificate
188
197
* @throws GeneralSecurityException
189
198
* @throws IOException
190
199
*/
191
200
public void isValidResponse (BasicOCSPResp ocspResp , X509Certificate issuerCert ) throws GeneralSecurityException , IOException {
192
- // by default the OCSP responder certificate is the issuer certificate
193
- X509Certificate responderCert = issuerCert ;
194
- // check if there's a responder certificate
195
- X509CertificateHolder [] certHolders = ocspResp .getCerts ();
196
- if (certHolders .length > 0 ) {
197
- responderCert = new JcaX509CertificateConverter ().setProvider ( "BC" ).getCertificate (certHolders [0 ]);
201
+ //OCSP response might be signed by the issuer certificate or
202
+ //the Authorized OCSP responder certificate containing the id-kp-OCSPSigning extended key usage extension
203
+ X509Certificate responderCert = null ;
204
+
205
+ //first check if the issuer certificate signed the response
206
+ //since it is expected to be the most common case
207
+ if (isSignatureValid (ocspResp , issuerCert )) {
208
+ responderCert = issuerCert ;
209
+ }
210
+
211
+ //if the issuer certificate didn't sign the ocsp response, look for authorized ocsp responses
212
+ // from properties or from certificate chain received with response
213
+ if (responderCert == null ) {
214
+ if (ocspResp .getCerts () != null ) {
215
+ //look for existence of Authorized OCSP responder inside the cert chain in ocsp response
216
+ X509CertificateHolder [] certs = ocspResp .getCerts ();
217
+ for (X509CertificateHolder cert : certs ) {
218
+ X509Certificate tempCert ;
219
+ try {
220
+ tempCert = new JcaX509CertificateConverter ().getCertificate (cert );
221
+ } catch (Exception ex ) {
222
+ continue ;
223
+ }
224
+ List <String > keyPurposes = null ;
225
+ try {
226
+ keyPurposes = tempCert .getExtendedKeyUsage ();
227
+ if ((keyPurposes != null ) && keyPurposes .contains (id_kp_OCSPSigning ) && isSignatureValid (ocspResp , tempCert )) {
228
+ responderCert = tempCert ;
229
+ break ;
230
+ }
231
+ } catch (CertificateParsingException ignored ) {
232
+ }
233
+ }
234
+ // Certificate signing the ocsp response is not found in ocsp response's certificate chain received
235
+ // and is not signed by the issuer certificate.
236
+ if (responderCert == null ) {
237
+ throw new VerificationException (issuerCert , "OCSP response could not be verified" );
238
+ }
239
+ } else {
240
+ //certificate chain is not present in response received
241
+ //try to verify using rootStore
242
+ if (rootStore != null ) {
243
+ try {
244
+ for (Enumeration <String > aliases = rootStore .aliases (); aliases .hasMoreElements (); ) {
245
+ String alias = aliases .nextElement ();
246
+ try {
247
+ if (!rootStore .isCertificateEntry (alias ))
248
+ continue ;
249
+ X509Certificate anchor = (X509Certificate ) rootStore .getCertificate (alias );
250
+ if (isSignatureValid (ocspResp , anchor )) {
251
+ responderCert = anchor ;
252
+ break ;
253
+ }
254
+ } catch (GeneralSecurityException ignored ) {
255
+ }
256
+ }
257
+ } catch (KeyStoreException e ) {
258
+ responderCert = null ;
259
+ }
260
+ }
261
+
262
+ // OCSP Response does not contain certificate chain, and response is not signed by any
263
+ // of the rootStore or the issuer certificate.
264
+ if (responderCert == null ) {
265
+ throw new VerificationException (issuerCert , "OCSP response could not be verified" );
266
+ }
267
+ }
268
+ }
269
+
270
+ //check "This certificate MUST be issued directly by the CA that issued the certificate in question".
271
+ responderCert .verify (issuerCert .getPublicKey ());
272
+
273
+ // validating ocsp signers certificate
274
+ // Check if responders certificate has id-pkix-ocsp-nocheck extension,
275
+ // in which case we do not validate (perform revocation check on) ocsp certs for lifetime of certificate
276
+ if (responderCert .getExtensionValue (OCSPObjectIdentifiers .id_pkix_ocsp_nocheck .getId ()) == null ) {
277
+ CRL crl ;
198
278
try {
199
- responderCert .verify (issuerCert .getPublicKey ());
279
+ crl = CertificateUtil .getCRL (responderCert );
280
+ } catch (Exception ignored ) {
281
+ crl = null ;
200
282
}
201
- catch (GeneralSecurityException e ) {
202
- if (super .verify (responderCert , issuerCert , null ).size () == 0 )
203
- throw new VerificationException (responderCert , "Responder certificate couldn't be verified" );
283
+ if (crl != null && crl instanceof X509CRL ) {
284
+ CRLVerifier crlVerifier = new CRLVerifier (null , null );
285
+ crlVerifier .setRootStore (rootStore );
286
+ crlVerifier .setOnlineCheckingAllowed (onlineCheckingAllowed );
287
+ crlVerifier .verify ((X509CRL )crl , responderCert , issuerCert , new Date ());
288
+ return ;
204
289
}
205
290
}
206
- // verify if the signature of the response is valid
207
- if (! verifyResponse ( ocspResp , responderCert ))
208
- throw new VerificationException ( responderCert , "OCSP response could not be verified" );
291
+
292
+ //check if lifetime of certificate is ok
293
+ responderCert . checkValidity ( );
209
294
}
210
295
211
296
/**
212
- * Verifies if the signature of the response is valid.
213
- * If it doesn't verify against the responder certificate, it may verify
214
- * using a trusted anchor.
297
+ * Verifies if the response is valid.
298
+ * If it doesn't verify against the issuer certificate and response's certificates, it may verify
299
+ * using a trusted anchor or cert.
300
+ * NOTE. Use {@code isValidResponse()} instead.
215
301
* @param ocspResp the response object
216
- * @param responderCert the certificate that may be used to sign the response
302
+ * @param issuerCert the issuer certificate
217
303
* @return true if the response can be trusted
218
304
*/
219
- public boolean verifyResponse (BasicOCSPResp ocspResp , X509Certificate responderCert ) {
220
- // testing using the responder certificate
221
- if (isSignatureValid (ocspResp , responderCert ))
222
- return true ;
223
- // testing using trusted anchors
224
- if (rootStore == null )
225
- return false ;
305
+ @ Deprecated
306
+ public boolean verifyResponse (BasicOCSPResp ocspResp , X509Certificate issuerCert ) {
226
307
try {
227
- // loop over the certificates in the root store
228
- for (Enumeration <String > aliases = rootStore .aliases (); aliases .hasMoreElements ();) {
229
- String alias = aliases .nextElement ();
230
- try {
231
- if (!rootStore .isCertificateEntry (alias ))
232
- continue ;
233
- X509Certificate anchor = (X509Certificate )rootStore .getCertificate (alias );
234
- if (isSignatureValid (ocspResp , anchor ))
235
- return true ;
236
- } catch (GeneralSecurityException e ) {
237
- continue ;
238
- }
239
- }
240
- }
241
- catch (GeneralSecurityException e ) {
308
+ isValidResponse (ocspResp , issuerCert );
309
+ return true ;
310
+ } catch (Exception e ) {
242
311
return false ;
243
312
}
244
- return false ;
245
313
}
246
314
247
315
/**
@@ -252,7 +320,8 @@ public boolean verifyResponse(BasicOCSPResp ocspResp, X509Certificate responderC
252
320
*/
253
321
public boolean isSignatureValid (BasicOCSPResp ocspResp , Certificate responderCert ) {
254
322
try {
255
- ContentVerifierProvider verifierProvider = new JcaContentVerifierProviderBuilder ().setProvider ("BC" ).build (responderCert .getPublicKey ());
323
+ ContentVerifierProvider verifierProvider = new JcaContentVerifierProviderBuilder ()
324
+ .setProvider ("BC" ).build (responderCert .getPublicKey ());
256
325
return ocspResp .isSignatureValid (verifierProvider );
257
326
} catch (OperatorCreationException e ) {
258
327
return false ;
@@ -263,7 +332,7 @@ public boolean isSignatureValid(BasicOCSPResp ocspResp, Certificate responderCer
263
332
264
333
/**
265
334
* Gets an OCSP response online and returns it if the status is GOOD
266
- * (without further checking).
335
+ * (without further checking! ).
267
336
* @param signCert the signing certificate
268
337
* @param issuerCert the issuer certificate
269
338
* @return an OCSP response
@@ -272,14 +341,14 @@ public BasicOCSPResp getOcspResponse(X509Certificate signCert, X509Certificate i
272
341
if (signCert == null && issuerCert == null ) {
273
342
return null ;
274
343
}
275
- OcspClientBouncyCastle ocsp = new OcspClientBouncyCastle ();
344
+ OcspClientBouncyCastle ocsp = new OcspClientBouncyCastle (null );
276
345
BasicOCSPResp ocspResp = ocsp .getBasicOCSPResp (signCert , issuerCert , null );
277
346
if (ocspResp == null ) {
278
347
return null ;
279
348
}
280
- SingleResp [] resp = ocspResp .getResponses ();
281
- for (int i = 0 ; i < resp . length ; i ++ ) {
282
- Object status = resp [ i ] .getCertStatus ();
349
+ SingleResp [] resps = ocspResp .getResponses ();
350
+ for (SingleResp resp : resps ) {
351
+ Object status = resp .getCertStatus ();
283
352
if (status == CertificateStatus .GOOD ) {
284
353
return ocspResp ;
285
354
}
0 commit comments