Skip to content

Commit 5e06f04

Browse files
committed
BC Migration Guide: update Chapter 5 with CA cert gen with pathLen constraint
1 parent f02ba84 commit 5e06f04

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

BouncyCastle-Migration/src/chapter05.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,9 +994,14 @@ certBuilder.addExtension(Extension.subjectKeyIdentifier, false,
994994
certBuilder.addExtension(Extension.authorityKeyIdentifier, false,
995995
extUtils.createAuthorityKeyIdentifier(issuerCert));
996996

997+
/* Add Basic Constraints (non-CA) */
997998
certBuilder.addExtension(Extension.basicConstraints, true,
998999
new BasicConstraints(false));
9991000

1001+
/* Or for a CA certificate (isCA:true), with path length constraint of 0 */
1002+
/* certBuilder.addExtension(Extension.basicConstraints, true,
1003+
new BasicConstraints(0)); */
1004+
10001005
certBuilder.addExtension(Extension.keyUsage, true,
10011006
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
10021007

@@ -1080,6 +1085,9 @@ try {
10801085
/* Basic Constraints (non-CA) */
10811086
cert.addExtension(WolfSSL.NID_basic_constraints, false, false);
10821087

1088+
/* Or for a CA certificate with pathLen constraint set to 0 */
1089+
/* cert.addExtension(WolfSSL.NID_basic_constraints, true, 0, true); */
1090+
10831091
/* Key Usage */
10841092
cert.addExtension(WolfSSL.NID_key_usage,
10851093
"digitalSignature,keyEncipherment", true);
@@ -1127,6 +1135,7 @@ try {
11271135
| Subject Key Identifier | `extUtils.createSubjectKeyIdentifier(...)` | `setSubjectKeyIdEx()` or `setSubjectKeyId(byte[])` |
11281136
| Authority Key Identifier | `extUtils.createAuthorityKeyIdentifier(...)` | `setAuthorityKeyIdEx(WolfSSLCertificate)` or `setAuthorityKeyId(byte[])` |
11291137
| Basic Constraints | `new BasicConstraints(boolean)` | `addExtension(NID_basic_constraints, boolean, boolean)` |
1138+
| Basic Constraints (CA + pathLen) | `new BasicConstraints(int)` | `addExtension(NID_basic_constraints, boolean, int, boolean)` |
11301139
| Key Usage | `new KeyUsage(int)` | `addExtension(NID_key_usage, String, boolean)` |
11311140
| Extended Key Usage | `new ExtendedKeyUsage(KeyPurposeId[])` | `addExtension(NID_ext_key_usage, String, boolean)` |
11321141
| SAN (IP address) | `new GeneralName(iPAddress, ...)` | `addAltNameIP(String)` |
@@ -1139,6 +1148,131 @@ try {
11391148
| Get PEM encoding | Manual conversion needed | `getPem()` |
11401149
| Free resources | Garbage collected | `free()` |
11411150

1151+
### CA Certificate Generation with Path Length Constraint
1152+
1153+
A common Bouncy Castle pattern is generating a self-signed CA certificate
1154+
with a `BasicConstraints` path length constraint. In Bouncy Castle,
1155+
`new BasicConstraints(int)` creates a CA constraint where the integer argument
1156+
specifies the maximum number of intermediate CA certificates allowed below this
1157+
CA in a certification path. wolfSSL JNI supports this through an overloaded
1158+
`addExtension()` method that accepts a `pathLen` parameter.
1159+
1160+
#### Bouncy Castle Approach
1161+
1162+
```java
1163+
import org.bouncycastle.asn1.x509.BasicConstraints;
1164+
import org.bouncycastle.asn1.x509.Extension;
1165+
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
1166+
import org.bouncycastle.asn1.x509.KeyPurposeId;
1167+
import org.bouncycastle.asn1.x509.KeyUsage;
1168+
import org.bouncycastle.asn1.x500.X500Name;
1169+
import org.bouncycastle.asn1.x500.style.RFC4519Style;
1170+
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
1171+
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
1172+
import org.bouncycastle.asn1.ASN1Encoding;
1173+
import org.bouncycastle.cert.X509v3CertificateBuilder;
1174+
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
1175+
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
1176+
1177+
KeyPair keyPair = keyPairGenerator.generateKeyPair();
1178+
SubjectPublicKeyInfo subPubKeyInfo =
1179+
SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
1180+
1181+
/* Compute Subject Key Identifier from public key hash */
1182+
SubjectKeyIdentifier ski = new SubjectKeyIdentifier(
1183+
MessageDigest.getInstance("SHA-1")
1184+
.digest(subPubKeyInfo.getPublicKeyData().getBytes()));
1185+
1186+
/* Build extensions list */
1187+
KeyUsage keyUsage = new KeyUsage(
1188+
KeyUsage.digitalSignature | KeyUsage.cRLSign | KeyUsage.keyCertSign);
1189+
1190+
List<Extension> extensions = new ArrayList<>();
1191+
extensions.add(new Extension(Extension.keyUsage, true,
1192+
keyUsage.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
1193+
extensions.add(new Extension(Extension.extendedKeyUsage, false,
1194+
new ExtendedKeyUsage(new KeyPurposeId[]{
1195+
KeyPurposeId.id_kp_clientAuth,
1196+
KeyPurposeId.id_kp_serverAuth,
1197+
KeyPurposeId.id_kp_OCSPSigning})
1198+
.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
1199+
1200+
/* BasicConstraints(0) = CA true, pathLen 0 */
1201+
extensions.add(new Extension(Extension.basicConstraints, true,
1202+
new BasicConstraints(0).toASN1Primitive()
1203+
.getEncoded(ASN1Encoding.DER)));
1204+
1205+
extensions.add(new Extension(Extension.subjectKeyIdentifier,
1206+
false, ski.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
1207+
1208+
/* Build and sign the certificate */
1209+
X500Name issuer = new X500Name(RFC4519Style.INSTANCE, subject);
1210+
X509v3CertificateBuilder builder =
1211+
new X509v3CertificateBuilder(
1212+
issuer, BigInteger.valueOf(System.currentTimeMillis()),
1213+
Date.from(startDate), Date.from(expiryDate),
1214+
issuer, subPubKeyInfo);
1215+
for (Extension ext : extensions) {
1216+
builder.addExtension(ext);
1217+
}
1218+
ContentSigner signer = new JcaContentSignerBuilder("SHA512withRSA")
1219+
.build(keyPair.getPrivate());
1220+
X509Certificate caCert = new JcaX509CertificateConverter()
1221+
.getCertificate(builder.build(signer));
1222+
```
1223+
1224+
#### wolfSSL Approach
1225+
1226+
```java
1227+
import com.wolfssl.WolfSSLCertificate;
1228+
import com.wolfssl.WolfSSLX509Name;
1229+
import com.wolfssl.WolfSSL;
1230+
1231+
KeyPair keyPair = keyPairGenerator.generateKeyPair();
1232+
1233+
WolfSSLCertificate cert = new WolfSSLCertificate();
1234+
try {
1235+
/* Set subject name (self-signed: issuer = subject) */
1236+
WolfSSLX509Name subjectName = new WolfSSLX509Name();
1237+
subjectName.setCommonName(subject);
1238+
cert.setSubjectName(subjectName);
1239+
1240+
/* Set serial number and validity dates */
1241+
cert.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
1242+
cert.setNotBefore(Date.from(startDate));
1243+
cert.setNotAfter(Date.from(expiryDate));
1244+
1245+
/* Set public key */
1246+
cert.setPublicKey(keyPair.getPublic());
1247+
1248+
/* Subject Key Identifier (SHA-1 hash computed internally) */
1249+
cert.setSubjectKeyIdEx();
1250+
1251+
/* Key Usage (critical) */
1252+
cert.addExtension(WolfSSL.NID_key_usage,
1253+
"digitalSignature,cRLSign,keyCertSign", true);
1254+
1255+
/* Extended Key Usage */
1256+
cert.addExtension(WolfSSL.NID_ext_key_usage,
1257+
"clientAuth,serverAuth,OCSPSigning", false);
1258+
1259+
/* Basic Constraints: CA=true, pathLen=0 (critical)
1260+
* BC equivalent: new BasicConstraints(0) */
1261+
cert.addExtension(WolfSSL.NID_basic_constraints, true, 0, true);
1262+
1263+
/* Sign (self-signed with own private key) */
1264+
cert.signCert(keyPair.getPrivate(), "SHA512");
1265+
1266+
byte[] certDer = cert.getDer();
1267+
byte[] certPem = cert.getPem();
1268+
1269+
subjectName.free();
1270+
1271+
} finally {
1272+
cert.free();
1273+
}
1274+
```
1275+
11421276
## Certificate Verification with CertManager
11431277

11441278
Bouncy Castle provides `X509CertificateHolder` and related classes for

0 commit comments

Comments
 (0)