Class RelatedCertificateTool


  • public class RelatedCertificateTool
    extends java.lang.Object
    Operator-style helpers for building and verifying the two wire-format pieces defined by RFC 9763 ("Related Certificates for Use in Multiple Authentications within a Protocol"):
    • the RelatedCertificate certificate extension carried on an end-entity certificate (OID X509ObjectIdentifiers.id_pe_relatedCert / Extension.relatedCertificate), and
    • the RequesterCertificate CSR attribute value the requester includes in the CSR to prove they hold the private key of the related certificate (attribute OID PKCSObjectIdentifiers.id_aa_relatedCertRequest).

    The intended use case is post-quantum migration: an end entity that already holds a traditional certificate requests a parallel post-quantum certificate by including a id-aa-relatedCertRequest attribute in the new CSR; the CA verifies the requester controls both private keys, then issues the new certificate carrying a RelatedCertificate extension that pins the traditional certificate by digest. A verifier seeing both certificates can then assert with assurance that they identify the same principal.

    This class is JCA-free and lightweight-crypto-free: it consumes DigestCalculator / DigestCalculatorProvider / ContentSigner / ContentVerifier from org.bouncycastle.operator, so both the lightweight (BC) and JCA bindings of those operator interfaces are equally usable. Wrapping / unwrapping the value as a PKCS#9 Attribute lives on the value class itself — see RequesterCertificate#toAttribute() and RequesterCertificate#fromAttribute(org.bouncycastle.asn1.pkcs.Attribute).

    • Method Summary

      All Methods Static Methods Concrete Methods 
      Modifier and Type Method Description
      static org.bouncycastle.asn1.x509.RelatedCertificate createRelatedCertificate​(X509CertificateHolder relatedCert, DigestCalculator digestCalculator)
      Compute the RelatedCertificate extension value identifying the supplied certificate by digest.
      static org.bouncycastle.asn1.cms.RequesterCertificate createRequesterCertificate​(org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID, org.bouncycastle.asn1.cms.BinaryTime requestTime, java.lang.String[] locationInfo, ContentSigner signer)
      Build a fully-signed RequesterCertificate value.
      static org.bouncycastle.asn1.cms.RequesterCertificate fromAttribute​(org.bouncycastle.asn1.pkcs.Attribute attribute)
      Extract a RequesterCertificate value from a PKCS#9 Attribute.
      static boolean isRelatedCertificate​(org.bouncycastle.asn1.x509.RelatedCertificate extensionValue, X509CertificateHolder relatedCert, DigestCalculatorProvider digestProvider)
      Recompute the digest specified in a RelatedCertificate extension value over the supplied candidate certificate and report whether it matches the stored hash.
      static org.bouncycastle.asn1.pkcs.Attribute toAttribute​(org.bouncycastle.asn1.cms.RequesterCertificate value)
      Wrap a RequesterCertificate value as a PKCS#9 Attribute carrying PKCSObjectIdentifiers.id_aa_relatedCertRequest, ready to drop into a CertificationRequestInfo attributes set.
      static boolean verifyRequesterCertificate​(org.bouncycastle.asn1.cms.RequesterCertificate value, ContentVerifier verifier)
      Verify the signature carried in value using the supplied ContentVerifier.
      static void writeSignatureInput​(java.io.OutputStream out, org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID, org.bouncycastle.asn1.cms.BinaryTime requestTime)
      Write the bytes the signature field must cover straight into out: the DER encoding of certID followed by the DER encoding of requestTime, per RFC 9763 sec. 4.1 ("concatenation of DER-encoded IssuerAndSerialNumber and BinaryTime").
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Method Detail

      • createRelatedCertificate

        public static org.bouncycastle.asn1.x509.RelatedCertificate createRelatedCertificate​(X509CertificateHolder relatedCert,
                                                                                             DigestCalculator digestCalculator)
                                                                                      throws java.io.IOException
        Compute the RelatedCertificate extension value identifying the supplied certificate by digest. Per RFC 9763 sec. 3.2 the digest input is the DER encoding of the entire Certificate structure (i.e. the value returned by X509CertificateHolder.getEncoded()).
        Parameters:
        relatedCert - the related end-entity certificate to bind.
        digestCalculator - a calculator configured for the desired digest algorithm; its AlgorithmIdentifier is copied verbatim into the extension's hashAlgorithm field.
        Throws:
        java.io.IOException - if the related certificate cannot be encoded or the digest calculator's output stream rejects bytes.
      • isRelatedCertificate

        public static boolean isRelatedCertificate​(org.bouncycastle.asn1.x509.RelatedCertificate extensionValue,
                                                   X509CertificateHolder relatedCert,
                                                   DigestCalculatorProvider digestProvider)
                                            throws OperatorCreationException,
                                                   java.io.IOException
        Recompute the digest specified in a RelatedCertificate extension value over the supplied candidate certificate and report whether it matches the stored hash.
        Parameters:
        extensionValue - the parsed RelatedCertificate extension value, e.g. via RelatedCertificate.getInstance(ext.getParsedValue()).
        relatedCert - the candidate related certificate.
        digestProvider - a provider able to instantiate a DigestCalculator for the hashAlgorithm carried by extensionValue.
        Throws:
        OperatorCreationException
        java.io.IOException
      • writeSignatureInput

        public static void writeSignatureInput​(java.io.OutputStream out,
                                               org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID,
                                               org.bouncycastle.asn1.cms.BinaryTime requestTime)
                                        throws java.io.IOException
        Write the bytes the signature field must cover straight into out: the DER encoding of certID followed by the DER encoding of requestTime, per RFC 9763 sec. 4.1 ("concatenation of DER-encoded IssuerAndSerialNumber and BinaryTime"). This is NOT wrapped in an outer SEQUENCE — implementations that hash a SEQUENCE will fail to interoperate. The two structures are streamed directly so no intermediate byte[] is materialised; pass a ContentSigner / ContentVerifier output stream (or a ByteArrayOutputStream if you need the bytes).
        Throws:
        java.io.IOException
      • createRequesterCertificate

        public static org.bouncycastle.asn1.cms.RequesterCertificate createRequesterCertificate​(org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID,
                                                                                                org.bouncycastle.asn1.cms.BinaryTime requestTime,
                                                                                                java.lang.String[] locationInfo,
                                                                                                ContentSigner signer)
                                                                                         throws java.io.IOException
        Build a fully-signed RequesterCertificate value. The supplied ContentSigner must be configured with the private key of the certificate identified by certID.
        Throws:
        java.io.IOException
      • verifyRequesterCertificate

        public static boolean verifyRequesterCertificate​(org.bouncycastle.asn1.cms.RequesterCertificate value,
                                                         ContentVerifier verifier)
                                                  throws java.io.IOException
        Verify the signature carried in value using the supplied ContentVerifier. The verifier must be configured with the public key of the certificate identified by value.getCertID() and the signature algorithm the CSR signer used (RFC 9763 carries no AlgorithmIdentifier with the signature, so the caller must derive it from the related certificate's SPKI plus any local policy).
        Throws:
        java.io.IOException
      • toAttribute

        public static org.bouncycastle.asn1.pkcs.Attribute toAttribute​(org.bouncycastle.asn1.cms.RequesterCertificate value)
        Wrap a RequesterCertificate value as a PKCS#9 Attribute carrying PKCSObjectIdentifiers.id_aa_relatedCertRequest, ready to drop into a CertificationRequestInfo attributes set.
      • fromAttribute

        public static org.bouncycastle.asn1.cms.RequesterCertificate fromAttribute​(org.bouncycastle.asn1.pkcs.Attribute attribute)
        Extract a RequesterCertificate value from a PKCS#9 Attribute.
        Throws:
        java.lang.IllegalArgumentException - if the attribute is not of type PKCSObjectIdentifiers.id_aa_relatedCertRequest or does not carry exactly one value.