@@ -24,6 +24,8 @@ This file is part of the iText (R) project.
2424
2525import com .itextpdf .bouncycastleconnector .BouncyCastleFactoryCreator ;
2626import com .itextpdf .commons .bouncycastle .IBouncyCastleFactory ;
27+ import com .itextpdf .commons .bouncycastle .asn1 .IASN1EncodableVector ;
28+ import com .itextpdf .commons .bouncycastle .asn1 .IASN1Enumerated ;
2729import com .itextpdf .commons .bouncycastle .asn1 .IASN1InputStream ;
2830import com .itextpdf .commons .bouncycastle .asn1 .IASN1ObjectIdentifier ;
2931import com .itextpdf .commons .bouncycastle .asn1 .IASN1OctetString ;
@@ -32,11 +34,17 @@ This file is part of the iText (R) project.
3234import com .itextpdf .commons .bouncycastle .asn1 .IASN1TaggedObject ;
3335import com .itextpdf .commons .bouncycastle .asn1 .IDERIA5String ;
3436import 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 ;
3540import com .itextpdf .commons .bouncycastle .asn1 .x509 .ICRLDistPoint ;
3641import com .itextpdf .commons .bouncycastle .asn1 .x509 .IDistributionPoint ;
3742import com .itextpdf .commons .bouncycastle .asn1 .x509 .IDistributionPointName ;
3843import com .itextpdf .commons .bouncycastle .asn1 .x509 .IGeneralName ;
3944import com .itextpdf .commons .bouncycastle .asn1 .x509 .IGeneralNames ;
45+ import com .itextpdf .signatures .logs .SignLogMessageConstant ;
46+ import org .slf4j .Logger ;
47+ import org .slf4j .LoggerFactory ;
4048
4149import java .io .ByteArrayInputStream ;
4250import java .io .IOException ;
@@ -48,6 +56,9 @@ This file is part of the iText (R) project.
4856import java .security .cert .Certificate ;
4957import java .security .cert .CertificateException ;
5058import java .security .cert .X509Certificate ;
59+ import java .security .cert .X509CRL ;
60+ import java .util .Collection ;
61+ import java .util .Enumeration ;
5162
5263/**
5364 * This class contains a series of static methods that
@@ -56,6 +67,7 @@ This file is part of the iText (R) project.
5667public class CertificateUtil {
5768
5869 private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator .getFactory ();
70+ private static final Logger LOGGER = LoggerFactory .getLogger (CertificateUtil .class );
5971
6072 // Certificate Revocation Lists
6173
@@ -224,6 +236,119 @@ public static Certificate generateCertificate(InputStream data) throws Certifica
224236 return SignUtils .generateCertificate (data , FACTORY .getProvider ());
225237 }
226238
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+
227352 /**
228353 * Checks if the certificate is signed by provided issuer certificate.
229354 *
@@ -330,4 +455,27 @@ private static String getValueFromAIAExtension(IASN1Primitive extensionValue, St
330455 }
331456 return null ;
332457 }
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+ }
333481}
0 commit comments