Class RelatedCertificateTool
- the
RelatedCertificatecertificate extension carried on an end-entity certificate (OIDX509ObjectIdentifiers.id_pe_relatedCert/Extension.relatedCertificate), and - the
RequesterCertificateCSR attribute value the requester includes in the CSR to prove they hold the private key of the related certificate (attribute OIDPKCSObjectIdentifiers.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
invalid reference
RequesterCertificate#toAttribute()invalid reference
RequesterCertificate#fromAttribute(org.bouncycastle.asn1.pkcs.Attribute)
-
Method Summary
Modifier and TypeMethodDescriptionstatic org.bouncycastle.asn1.x509.RelatedCertificatecreateRelatedCertificate(X509CertificateHolder relatedCert, DigestCalculator digestCalculator) Compute theRelatedCertificateextension value identifying the supplied certificate by digest.static org.bouncycastle.asn1.cms.RequesterCertificatecreateRequesterCertificate(org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID, org.bouncycastle.asn1.cms.BinaryTime requestTime, String[] locationInfo, ContentSigner signer) Build a fully-signedRequesterCertificatevalue.static org.bouncycastle.asn1.cms.RequesterCertificatefromAttribute(org.bouncycastle.asn1.pkcs.Attribute attribute) Extract aRequesterCertificatevalue from a PKCS#9Attribute.static booleanisRelatedCertificate(org.bouncycastle.asn1.x509.RelatedCertificate extensionValue, X509CertificateHolder relatedCert, DigestCalculatorProvider digestProvider) Recompute the digest specified in aRelatedCertificateextension value over the supplied candidate certificate and report whether it matches the stored hash.static org.bouncycastle.asn1.pkcs.AttributetoAttribute(org.bouncycastle.asn1.cms.RequesterCertificate value) Wrap aRequesterCertificatevalue as a PKCS#9AttributecarryingPKCSObjectIdentifiers.id_aa_relatedCertRequest, ready to drop into aCertificationRequestInfoattributes set.static booleanverifyRequesterCertificate(org.bouncycastle.asn1.cms.RequesterCertificate value, ContentVerifier verifier) Verify the signature carried invalueusing the suppliedContentVerifier.static voidwriteSignatureInput(OutputStream out, org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID, org.bouncycastle.asn1.cms.BinaryTime requestTime) Write the bytes thesignaturefield must cover straight intoout: the DER encoding ofcertIDfollowed by the DER encoding ofrequestTime, per RFC 9763 sec. 4.1 ("concatenation of DER-encoded IssuerAndSerialNumber and BinaryTime").
-
Method Details
-
createRelatedCertificate
public static org.bouncycastle.asn1.x509.RelatedCertificate createRelatedCertificate(X509CertificateHolder relatedCert, DigestCalculator digestCalculator) throws IOException Compute theRelatedCertificateextension value identifying the supplied certificate by digest. Per RFC 9763 sec. 3.2 the digest input is the DER encoding of the entireCertificatestructure (i.e. the value returned byX509CertificateHolder.getEncoded()).- Parameters:
relatedCert- the related end-entity certificate to bind.digestCalculator- a calculator configured for the desired digest algorithm; itsAlgorithmIdentifieris copied verbatim into the extension'shashAlgorithmfield.- Throws:
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, IOException Recompute the digest specified in aRelatedCertificateextension value over the supplied candidate certificate and report whether it matches the stored hash.- Parameters:
extensionValue- the parsedRelatedCertificateextension value, e.g. viaRelatedCertificate.getInstance(ext.getParsedValue()).relatedCert- the candidate related certificate.digestProvider- a provider able to instantiate aDigestCalculatorfor thehashAlgorithmcarried byextensionValue.- Throws:
OperatorCreationExceptionIOException
-
writeSignatureInput
public static void writeSignatureInput(OutputStream out, org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID, org.bouncycastle.asn1.cms.BinaryTime requestTime) throws IOException Write the bytes thesignaturefield must cover straight intoout: the DER encoding ofcertIDfollowed by the DER encoding ofrequestTime, 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 intermediatebyte[]is materialised; pass aContentSigner/ContentVerifieroutput stream (or aByteArrayOutputStreamif you need the bytes).- Throws:
IOException
-
createRequesterCertificate
public static org.bouncycastle.asn1.cms.RequesterCertificate createRequesterCertificate(org.bouncycastle.asn1.cms.IssuerAndSerialNumber certID, org.bouncycastle.asn1.cms.BinaryTime requestTime, String[] locationInfo, ContentSigner signer) throws IOException Build a fully-signedRequesterCertificatevalue. The suppliedContentSignermust be configured with the private key of the certificate identified bycertID.- Throws:
IOException
-
verifyRequesterCertificate
public static boolean verifyRequesterCertificate(org.bouncycastle.asn1.cms.RequesterCertificate value, ContentVerifier verifier) throws IOException Verify the signature carried invalueusing the suppliedContentVerifier. The verifier must be configured with the public key of the certificate identified byvalue.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:
IOException
-
toAttribute
public static org.bouncycastle.asn1.pkcs.Attribute toAttribute(org.bouncycastle.asn1.cms.RequesterCertificate value) Wrap aRequesterCertificatevalue as a PKCS#9AttributecarryingPKCSObjectIdentifiers.id_aa_relatedCertRequest, ready to drop into aCertificationRequestInfoattributes set. -
fromAttribute
public static org.bouncycastle.asn1.cms.RequesterCertificate fromAttribute(org.bouncycastle.asn1.pkcs.Attribute attribute) Extract aRequesterCertificatevalue from a PKCS#9Attribute.- Throws:
IllegalArgumentException- if the attribute is not of typePKCSObjectIdentifiers.id_aa_relatedCertRequestor does not carry exactly one value.
-