Skip to content

Commit c90d820

Browse files
committed
CRMF: add the RFC 4211 pkiPublicationInfo and oldCertID controls, a getControlValue() that reads the raw value of any control present, and JCA conveniences for the protocolEncrKey and oldCertID controls.
1 parent dc6822a commit c90d820

7 files changed

Lines changed: 390 additions & 0 deletions

File tree

‎docs/releasenotes.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Date: 2026, TBD
3737
### 2.1.3 Additional Features and Functionality
3838

3939
- The CRMF certificate request message controls now include the RFC 4211 sec. 6.6 protocolEncrKey control, which names the key a CA is to encrypt its response with: org.bouncycastle.cert.crmf.ProtocolEncrKeyControl carries the SubjectPublicKeyInfo the control is defined to take, and CertificateRequestMessage.getControl() recognises id-regCtrl-protocolEncrKey alongside the regToken, authenticator and pkiArchiveOptions controls it already returned (github PR #2443).
40+
- The CRMF certificate request message controls now cover all six RFC 4211 sec. 6 registration controls: org.bouncycastle.cert.crmf.PKIPublicationInfoControl (sec. 6.3), which says whether and where the CA should publish the certificate it issues, and OldCertIDControl (sec. 6.5), which names the certificate a request is asking to have replaced, join the regToken, authenticator, pkiArchiveOptions and protocolEncrKey controls, and CertificateRequestMessage.getControl() recognises both. CertificateRequestMessage.getControlValue() returns the raw value of any control present, including one the library has no Control implementation for - getControl() answers those with the same null it uses for a control that is absent - and org.bouncycastle.cert.crmf.jcajce.JcaProtocolEncrKeyControl and JcaOldCertIDControl build the two key- and certificate-bearing controls from a java.security.PublicKey and from the X509Certificate being replaced.
4041

4142
- The OpenPGP smart card API (bcpgsc) can now create signatures on a card as well as decrypt with one. The raw sign and decrypt operations moved onto OpenPGPSmartCard, so a backend can be emulated in software and the simulator exercises the same paths a card does; AbstractOpenPGPDocumentSignatureGenerator detects an external key and routes it to the card, with the content signer supplied through a pluggable PGPContentSignerBuilderProviderFactory; the YubiKey-specific decryptor factories are replaced by generic smart-card ones, ExternalOpenPGPKeyUtils converts an OpenPGPKey or OpenPGPCertificate into an external key, and the YubiKit dependency moves to 3.2.0. YubiKit remains compileOnly, so bcpgsc still ships with no third-party dependencies (github PR #2430).
4243

‎pkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessage.java‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22

33
import java.io.IOException;
44

5+
import org.bouncycastle.asn1.ASN1Encodable;
56
import org.bouncycastle.asn1.ASN1Integer;
67
import org.bouncycastle.asn1.ASN1Object;
78
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
89
import org.bouncycastle.asn1.ASN1Primitive;
910
import org.bouncycastle.asn1.ASN1UTF8String;
1011
import org.bouncycastle.asn1.crmf.AttributeTypeAndValue;
1112
import org.bouncycastle.asn1.crmf.CRMFObjectIdentifiers;
13+
import org.bouncycastle.asn1.crmf.CertId;
1214
import org.bouncycastle.asn1.crmf.CertReqMsg;
1315
import org.bouncycastle.asn1.crmf.CertTemplate;
1416
import org.bouncycastle.asn1.crmf.Controls;
1517
import org.bouncycastle.asn1.crmf.PKIArchiveOptions;
18+
import org.bouncycastle.asn1.crmf.PKIPublicationInfo;
1619
import org.bouncycastle.asn1.crmf.PKMACValue;
1720
import org.bouncycastle.asn1.crmf.POPOSigningKey;
1821
import org.bouncycastle.asn1.crmf.ProofOfPossession;
@@ -151,11 +154,35 @@ public Control getControl(ASN1ObjectIdentifier type)
151154
{
152155
return new ProtocolEncrKeyControl(SubjectPublicKeyInfo.getInstance(found.getValue()));
153156
}
157+
if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_pkiPublicationInfo))
158+
{
159+
return new PKIPublicationInfoControl(PKIPublicationInfo.getInstance(found.getValue()));
160+
}
161+
if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_oldCertID))
162+
{
163+
return new OldCertIDControl(CertId.getInstance(found.getValue()));
164+
}
154165
}
155166

156167
return null;
157168
}
158169

170+
/**
171+
* Return the raw value of the control of passed in type, if present. Unlike
172+
* {@link #getControl(ASN1ObjectIdentifier)} this makes no attempt to interpret the value, so a
173+
* control this class has no {@link Control} implementation for can still be read, and a null
174+
* return means the control is absent rather than unrecognised.
175+
*
176+
* @param type the type OID of the control to look for.
177+
* @return the value of the control of the passed in type, null if the control is not present.
178+
*/
179+
public ASN1Encodable getControlValue(ASN1ObjectIdentifier type)
180+
{
181+
AttributeTypeAndValue found = findControl(type);
182+
183+
return found != null ? found.getValue() : null;
184+
}
185+
159186
private AttributeTypeAndValue findControl(ASN1ObjectIdentifier type)
160187
{
161188
if (controls == null)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package org.bouncycastle.cert.crmf;
2+
3+
import java.math.BigInteger;
4+
5+
import org.bouncycastle.asn1.ASN1Encodable;
6+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
7+
import org.bouncycastle.asn1.crmf.CRMFObjectIdentifiers;
8+
import org.bouncycastle.asn1.crmf.CertId;
9+
import org.bouncycastle.asn1.x500.X500Name;
10+
import org.bouncycastle.asn1.x509.GeneralName;
11+
12+
/**
13+
* Carrier for an old certificate ID control, naming the certificate a request is asking to have
14+
* replaced - RFC 4211 sec. 6.5.
15+
*/
16+
public class OldCertIDControl
17+
implements Control
18+
{
19+
private static final ASN1ObjectIdentifier type = CRMFObjectIdentifiers.id_regCtrl_oldCertID;
20+
21+
private final CertId certId;
22+
23+
/**
24+
* Basic constructor - build from the ID of the certificate to be replaced.
25+
*
26+
* @param certId the issuer and serial number of the certificate to be replaced.
27+
*/
28+
public OldCertIDControl(CertId certId)
29+
{
30+
this.certId = certId;
31+
}
32+
33+
/**
34+
* Basic constructor - build from the issuer and serial number of the certificate to be
35+
* replaced.
36+
*
37+
* @param issuer the issuer of the certificate to be replaced.
38+
* @param serialNumber the serial number of the certificate to be replaced.
39+
*/
40+
public OldCertIDControl(X500Name issuer, BigInteger serialNumber)
41+
{
42+
this(new CertId(new GeneralName(issuer), serialNumber));
43+
}
44+
45+
/**
46+
* Return the type of this control.
47+
*
48+
* @return CRMFObjectIdentifiers.id_regCtrl_oldCertID
49+
*/
50+
public ASN1ObjectIdentifier getType()
51+
{
52+
return type;
53+
}
54+
55+
/**
56+
* Return the certificate ID associated with this control.
57+
*
58+
* @return a CertId structure.
59+
*/
60+
public ASN1Encodable getValue()
61+
{
62+
return certId;
63+
}
64+
65+
/**
66+
* Return the certificate ID associated with this control.
67+
*
68+
* @return a CertId structure.
69+
*/
70+
public CertId getCertId()
71+
{
72+
return certId;
73+
}
74+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package org.bouncycastle.cert.crmf;
2+
3+
import org.bouncycastle.asn1.ASN1Encodable;
4+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
5+
import org.bouncycastle.asn1.crmf.CRMFObjectIdentifiers;
6+
import org.bouncycastle.asn1.crmf.PKIPublicationInfo;
7+
import org.bouncycastle.asn1.crmf.SinglePubInfo;
8+
9+
/**
10+
* Carrier for a publication information control, saying whether and where the CA should publish
11+
* the certificate it issues - RFC 4211 sec. 6.3.
12+
*/
13+
public class PKIPublicationInfoControl
14+
implements Control
15+
{
16+
private static final ASN1ObjectIdentifier type = CRMFObjectIdentifiers.id_regCtrl_pkiPublicationInfo;
17+
18+
private final PKIPublicationInfo publicationInfo;
19+
20+
/**
21+
* Basic constructor - build from the publication information to use.
22+
*
23+
* @param publicationInfo the publication information for the certificate.
24+
*/
25+
public PKIPublicationInfoControl(PKIPublicationInfo publicationInfo)
26+
{
27+
this.publicationInfo = publicationInfo;
28+
}
29+
30+
/**
31+
* Basic constructor - build from the locations the certificate is to be published at,
32+
* which asks for publication (pleasePublish). A null or empty array asks for publication
33+
* without saying where ("dontCare"); use {@link #dontPublish()} to ask for none.
34+
*
35+
* @param pubInfos the locations to publish the certificate at.
36+
*/
37+
public PKIPublicationInfoControl(SinglePubInfo[] pubInfos)
38+
{
39+
this(new PKIPublicationInfo(pubInfos == null || pubInfos.length == 0 ? null : pubInfos));
40+
}
41+
42+
/**
43+
* Return a control asking the CA not to publish the certificate.
44+
*
45+
* @return a control carrying the dontPublish action.
46+
*/
47+
public static PKIPublicationInfoControl dontPublish()
48+
{
49+
return new PKIPublicationInfoControl(new PKIPublicationInfo(PKIPublicationInfo.dontPublish));
50+
}
51+
52+
/**
53+
* Return the type of this control.
54+
*
55+
* @return CRMFObjectIdentifiers.id_regCtrl_pkiPublicationInfo
56+
*/
57+
public ASN1ObjectIdentifier getType()
58+
{
59+
return type;
60+
}
61+
62+
/**
63+
* Return the publication information associated with this control.
64+
*
65+
* @return a PKIPublicationInfo structure.
66+
*/
67+
public ASN1Encodable getValue()
68+
{
69+
return publicationInfo;
70+
}
71+
72+
/**
73+
* Return the publication information associated with this control.
74+
*
75+
* @return a PKIPublicationInfo structure.
76+
*/
77+
public PKIPublicationInfo getPublicationInfo()
78+
{
79+
return publicationInfo;
80+
}
81+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.bouncycastle.cert.crmf.jcajce;
2+
3+
import java.math.BigInteger;
4+
import java.security.cert.X509Certificate;
5+
6+
import javax.security.auth.x500.X500Principal;
7+
8+
import org.bouncycastle.asn1.x500.X500Name;
9+
import org.bouncycastle.cert.crmf.OldCertIDControl;
10+
11+
/**
12+
* JCA convenience for the old certificate ID control - builds the control from the certificate
13+
* being replaced, or from its issuer and serial number, rather than from a CertId.
14+
*/
15+
public class JcaOldCertIDControl
16+
extends OldCertIDControl
17+
{
18+
public JcaOldCertIDControl(X509Certificate certificate)
19+
{
20+
this(certificate.getIssuerX500Principal(), certificate.getSerialNumber());
21+
}
22+
23+
public JcaOldCertIDControl(X500Principal issuer, BigInteger serialNumber)
24+
{
25+
super(X500Name.getInstance(issuer.getEncoded()), serialNumber);
26+
}
27+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.bouncycastle.cert.crmf.jcajce;
2+
3+
import java.security.PublicKey;
4+
5+
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
6+
import org.bouncycastle.cert.crmf.ProtocolEncrKeyControl;
7+
8+
/**
9+
* JCA convenience for the protocol encryption key control - builds the control from a
10+
* {@link PublicKey} rather than from its SubjectPublicKeyInfo encoding.
11+
*/
12+
public class JcaProtocolEncrKeyControl
13+
extends ProtocolEncrKeyControl
14+
{
15+
public JcaProtocolEncrKeyControl(PublicKey publicKey)
16+
{
17+
super(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
18+
}
19+
}

0 commit comments

Comments
 (0)