Skip to content

Commit e210bea

Browse files
authored
Merge pull request #258 from cconlon/bcMigrationPathLen
BC Migration Guide: update Chapter 5 with CA cert gen with pathLen constraint
2 parents f02ba84 + 6b3c1de commit e210bea

1 file changed

Lines changed: 138 additions & 1 deletion

File tree

BouncyCastle-Migration/src/chapter05.md

Lines changed: 138 additions & 1 deletion
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

@@ -1048,14 +1053,15 @@ import java.util.Date;
10481053
/* Load issuer certificate for setting issuer name and AKID */
10491054
WolfSSLCertificate issuerWolfCert = null;
10501055
WolfSSLCertificate cert = null;
1056+
WolfSSLX509Name subject = null;
10511057
try {
10521058
issuerWolfCert =
10531059
new WolfSSLCertificate(issuerCert.getEncoded());
10541060

10551061
/* Create empty certificate for generation */
10561062
cert = new WolfSSLCertificate();
10571063
/* Set subject and issuer names */
1058-
WolfSSLX509Name subject = new WolfSSLX509Name();
1064+
subject = new WolfSSLX509Name();
10591065
subject.setCommonName("Example");
10601066
cert.setSubjectName(subject);
10611067
cert.setIssuerName(issuerWolfCert);
@@ -1080,6 +1086,9 @@ try {
10801086
/* Basic Constraints (non-CA) */
10811087
cert.addExtension(WolfSSL.NID_basic_constraints, false, false);
10821088

1089+
/* Or for a CA certificate with pathLen constraint set to 0 */
1090+
/* cert.addExtension(WolfSSL.NID_basic_constraints, true, 0, true); */
1091+
10831092
/* Key Usage */
10841093
cert.addExtension(WolfSSL.NID_key_usage,
10851094
"digitalSignature,keyEncipherment", true);
@@ -1105,6 +1114,9 @@ try {
11051114
byte[] certPem = cert.getPem();
11061115

11071116
} finally {
1117+
if (subject != null) {
1118+
subject.free();
1119+
}
11081120
if (cert != null) {
11091121
cert.free();
11101122
}
@@ -1127,6 +1139,7 @@ try {
11271139
| Subject Key Identifier | `extUtils.createSubjectKeyIdentifier(...)` | `setSubjectKeyIdEx()` or `setSubjectKeyId(byte[])` |
11281140
| Authority Key Identifier | `extUtils.createAuthorityKeyIdentifier(...)` | `setAuthorityKeyIdEx(WolfSSLCertificate)` or `setAuthorityKeyId(byte[])` |
11291141
| Basic Constraints | `new BasicConstraints(boolean)` | `addExtension(NID_basic_constraints, boolean, boolean)` |
1142+
| Basic Constraints (CA + pathLen) | `new BasicConstraints(int)` | `addExtension(NID_basic_constraints, boolean, int, boolean)` |
11301143
| Key Usage | `new KeyUsage(int)` | `addExtension(NID_key_usage, String, boolean)` |
11311144
| Extended Key Usage | `new ExtendedKeyUsage(KeyPurposeId[])` | `addExtension(NID_ext_key_usage, String, boolean)` |
11321145
| SAN (IP address) | `new GeneralName(iPAddress, ...)` | `addAltNameIP(String)` |
@@ -1139,6 +1152,130 @@ try {
11391152
| Get PEM encoding | Manual conversion needed | `getPem()` |
11401153
| Free resources | Garbage collected | `free()` |
11411154

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

11441281
Bouncy Castle provides `X509CertificateHolder` and related classes for

0 commit comments

Comments
 (0)