@@ -24,6 +24,8 @@ This file is part of the iText (R) project.
24
24
25
25
import com .itextpdf .bouncycastleconnector .BouncyCastleFactoryCreator ;
26
26
import com .itextpdf .commons .bouncycastle .IBouncyCastleFactory ;
27
+ import com .itextpdf .commons .bouncycastle .asn1 .IASN1EncodableVector ;
28
+ import com .itextpdf .commons .bouncycastle .asn1 .IASN1Enumerated ;
27
29
import com .itextpdf .commons .bouncycastle .asn1 .IASN1InputStream ;
28
30
import com .itextpdf .commons .bouncycastle .asn1 .IASN1ObjectIdentifier ;
29
31
import com .itextpdf .commons .bouncycastle .asn1 .IASN1OctetString ;
@@ -32,11 +34,17 @@ This file is part of the iText (R) project.
32
34
import com .itextpdf .commons .bouncycastle .asn1 .IASN1TaggedObject ;
33
35
import com .itextpdf .commons .bouncycastle .asn1 .IDERIA5String ;
34
36
import com .itextpdf .commons .bouncycastle .asn1 .IDEROctetString ;
37
+ import com .itextpdf .commons .bouncycastle .asn1 .IDERSet ;
38
+ import com .itextpdf .commons .bouncycastle .asn1 .ocsp .IBasicOCSPResponse ;
39
+ import com .itextpdf .commons .bouncycastle .asn1 .ocsp .IOCSPObjectIdentifiers ;
35
40
import com .itextpdf .commons .bouncycastle .asn1 .x509 .ICRLDistPoint ;
36
41
import com .itextpdf .commons .bouncycastle .asn1 .x509 .IDistributionPoint ;
37
42
import com .itextpdf .commons .bouncycastle .asn1 .x509 .IDistributionPointName ;
38
43
import com .itextpdf .commons .bouncycastle .asn1 .x509 .IGeneralName ;
39
44
import com .itextpdf .commons .bouncycastle .asn1 .x509 .IGeneralNames ;
45
+ import com .itextpdf .signatures .logs .SignLogMessageConstant ;
46
+ import org .slf4j .Logger ;
47
+ import org .slf4j .LoggerFactory ;
40
48
41
49
import java .io .ByteArrayInputStream ;
42
50
import java .io .IOException ;
@@ -48,6 +56,9 @@ This file is part of the iText (R) project.
48
56
import java .security .cert .Certificate ;
49
57
import java .security .cert .CertificateException ;
50
58
import java .security .cert .X509Certificate ;
59
+ import java .security .cert .X509CRL ;
60
+ import java .util .Collection ;
61
+ import java .util .Enumeration ;
51
62
52
63
/**
53
64
* This class contains a series of static methods that
@@ -56,6 +67,7 @@ This file is part of the iText (R) project.
56
67
public class CertificateUtil {
57
68
58
69
private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator .getFactory ();
70
+ private static final Logger LOGGER = LoggerFactory .getLogger (CertificateUtil .class );
59
71
60
72
// Certificate Revocation Lists
61
73
@@ -224,6 +236,119 @@ public static Certificate generateCertificate(InputStream data) throws Certifica
224
236
return SignUtils .generateCertificate (data , FACTORY .getProvider ());
225
237
}
226
238
239
+ /**
240
+ * Try to retrieve CRL and OCSP responses from the signed data crls field.
241
+ *
242
+ * @param taggedObj signed data crls field as {@link IASN1TaggedObject}.
243
+ *
244
+ * @param crls collection to store retrieved CRL responses.
245
+ * @param ocsps collection of {@link IBasicOCSPResponse} wrappers to store retrieved
246
+ * OCSP responses.
247
+ * @param otherRevocationInfoFormats collection of revocation info other than OCSP and CRL responses,
248
+ * e.g. SCVP Request and Response, stored as {@link IASN1Sequence}.
249
+ *
250
+ * @throws IOException if some I/O error occurred.
251
+ * @throws CertificateException if CertificateFactory instance wasn't created.
252
+ */
253
+ public static void retrieveRevocationInfoFromSignedData (IASN1TaggedObject taggedObj , Collection <CRL > crls ,
254
+ Collection <IBasicOCSPResponse > ocsps ,
255
+ Collection <IASN1Sequence > otherRevocationInfoFormats )
256
+ throws IOException , CertificateException {
257
+ Enumeration revInfo = FACTORY .createASN1Set (taggedObj , false ).getObjects ();
258
+ while (revInfo .hasMoreElements ()) {
259
+ IASN1Sequence s = FACTORY .createASN1Sequence (revInfo .nextElement ());
260
+ IASN1ObjectIdentifier o = FACTORY .createASN1ObjectIdentifier (s .getObjectAt (0 ));
261
+ if (o != null && SecurityIDs .ID_RI_OCSP_RESPONSE .equals (o .getId ())) {
262
+ IASN1Sequence ocspResp = FACTORY .createASN1Sequence (s .getObjectAt (1 ));
263
+ IASN1Enumerated respStatus = FACTORY .createASN1Enumerated (ocspResp .getObjectAt (0 ));
264
+ if (respStatus .intValueExact () == FACTORY .createOCSPRespBuilderInstance ().getSuccessful ()) {
265
+ IASN1Sequence responseBytes = FACTORY .createASN1Sequence (ocspResp .getObjectAt (1 ));
266
+ if (responseBytes != null ) {
267
+ ocsps .add (CertificateUtil .createOcsp (responseBytes ));
268
+ }
269
+ }
270
+ } else {
271
+ try {
272
+ crls .addAll (SignUtils .readAllCRLs (s .getEncoded ()));
273
+ } catch (CRLException ignored ) {
274
+ LOGGER .warn (SignLogMessageConstant .UNABLE_TO_PARSE_REV_INFO );
275
+ otherRevocationInfoFormats .add (s );
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Creates the revocation info (crls field) for SignedData structure:
283
+ * RevocationInfoChoices ::= SET OF RevocationInfoChoice
284
+ *
285
+ * RevocationInfoChoice ::= CHOICE {
286
+ * crl CertificateList,
287
+ * other [1] IMPLICIT OtherRevocationInfoFormat }
288
+ *
289
+ * OtherRevocationInfoFormat ::= SEQUENCE {
290
+ * otherRevInfoFormat OBJECT IDENTIFIER,
291
+ * otherRevInfo ANY DEFINED BY otherRevInfoFormat }
292
+ *
293
+ * CertificateList ::= SEQUENCE {
294
+ * tbsCertList TBSCertList,
295
+ * signatureAlgorithm AlgorithmIdentifier,
296
+ * signatureValue BIT STRING }
297
+ *
298
+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.1">RFC 5652 §10.2.1</a>
299
+ *
300
+ * @param crls collection of CRL revocation status information.
301
+ * @param ocsps collection of OCSP revocation status information.
302
+ * @param otherRevocationInfoFormats collection of revocation info other than OCSP and CRL responses,
303
+ * e.g. SCVP Request and Response, stored as {@link IASN1Sequence}.
304
+ *
305
+ * @return {@code crls [1] RevocationInfoChoices} field of SignedData structure. Null if SignedData has
306
+ * no revocation data.
307
+ *
308
+ * @throws CRLException if an encoding error occurs.
309
+ * @throws IOException if an I/O error occurs.
310
+ */
311
+ public static IDERSet createRevocationInfoChoices (Collection <CRL > crls , Collection <IBasicOCSPResponse > ocsps ,
312
+ Collection <IASN1Sequence > otherRevocationInfoFormats )
313
+ throws CRLException , IOException {
314
+ if (crls .size () == 0 && ocsps .size () == 0 ) {
315
+ return null ;
316
+ }
317
+ IASN1EncodableVector revocationInfoChoices = FACTORY .createASN1EncodableVector ();
318
+
319
+ // Add CRLs
320
+ for (CRL element : crls ) {
321
+ // Add crl CertificateList (crl RevocationInfoChoice)
322
+ revocationInfoChoices .add (FACTORY .createASN1Sequence (((X509CRL ) element ).getEncoded ()));
323
+ }
324
+
325
+ // Add OCSPs
326
+ for (IBasicOCSPResponse element : ocsps ) {
327
+ IASN1EncodableVector ocspResponseRevInfo = FACTORY .createASN1EncodableVector ();
328
+ // Add otherRevInfoFormat (ID_RI_OCSP_RESPONSE)
329
+ ocspResponseRevInfo .add (FACTORY .createASN1ObjectIdentifier (SecurityIDs .ID_RI_OCSP_RESPONSE ));
330
+
331
+ IASN1EncodableVector ocspResponse = FACTORY .createASN1EncodableVector ();
332
+ ocspResponse .add (FACTORY .createOCSPResponseStatus (
333
+ FACTORY .createOCSPRespBuilderInstance ().getSuccessful ()).toASN1Primitive ());
334
+ ocspResponse .add (FACTORY .createResponseBytes (
335
+ FACTORY .createOCSPObjectIdentifiers ().getIdPkixOcspBasic (),
336
+ FACTORY .createDEROctetString (element .toASN1Primitive ().getEncoded ())).toASN1Primitive ());
337
+ // Add otherRevInfo (ocspResponse)
338
+ ocspResponseRevInfo .add (FACTORY .createDERSequence (ocspResponse ));
339
+
340
+ // Add other [1] IMPLICIT OtherRevocationInfoFormat (ocsp RevocationInfoChoice)
341
+ revocationInfoChoices .add (FACTORY .createDERSequence (ocspResponseRevInfo ));
342
+ }
343
+
344
+ // Add other RevocationInfo formats
345
+ for (IASN1Sequence revInfo : otherRevocationInfoFormats ) {
346
+ revocationInfoChoices .add (revInfo );
347
+ }
348
+
349
+ return FACTORY .createDERSet (revocationInfoChoices );
350
+ }
351
+
227
352
/**
228
353
* Checks if the certificate is signed by provided issuer certificate.
229
354
*
@@ -330,4 +455,27 @@ private static String getValueFromAIAExtension(IASN1Primitive extensionValue, St
330
455
}
331
456
return null ;
332
457
}
458
+
459
+ /**
460
+ * Helper method that creates the {@link IBasicOCSPResponse} object from the response bytes.
461
+ *
462
+ * @param seq response bytes.
463
+ *
464
+ * @return {@link IBasicOCSPResponse} object.
465
+ *
466
+ * @throws IOException if some I/O error occurred.
467
+ */
468
+ private static IBasicOCSPResponse createOcsp (IASN1Sequence seq ) throws IOException {
469
+ IASN1ObjectIdentifier objectIdentifier = FACTORY .createASN1ObjectIdentifier (
470
+ seq .getObjectAt (0 ));
471
+ IOCSPObjectIdentifiers ocspObjectIdentifiers = FACTORY .createOCSPObjectIdentifiers ();
472
+ if (objectIdentifier != null
473
+ && objectIdentifier .getId ().equals (ocspObjectIdentifiers .getIdPkixOcspBasic ().getId ())) {
474
+ IASN1OctetString os = FACTORY .createASN1OctetString (seq .getObjectAt (1 ));
475
+ try (IASN1InputStream inp = FACTORY .createASN1InputStream (os .getOctets ())) {
476
+ return FACTORY .createBasicOCSPResponse (inp .readObject ());
477
+ }
478
+ }
479
+ return null ;
480
+ }
333
481
}
0 commit comments