24.3 Certificate Generation and Signing
Certificate generation includes creating self-signed certificates, generating Certificate Signing Requests (CSRs), and signing certificates with a CA.
Self-Signed Certificate Generation
// Generating Self-Signed Certificates
public class SelfSignedCertificateGenerator {
// Generate self-signed certificate
public static java.security.cert.X509Certificate generateSelfSignedCertificate(
KeyPair keyPair, String subjectDN, int validityDays) throws Exception {
// Certificate builder (requires BouncyCastle or manual ASN.1 encoding)
// This is a simplified example showing the concept
X500Name subject = new X500Name(subjectDN);
X500Name issuer = subject; // Self-signed: issuer = subject
BigInteger serialNumber = new BigInteger(Long.toString(
System.currentTimeMillis()));
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() +
(long) validityDays * 24 * 60 * 60 * 1000);
// Using BouncyCastle (org.bouncycastle:bcpkix-jdk18on)
org.bouncycastle.cert.X509v3CertificateBuilder certBuilder =
new org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder(
issuer,
serialNumber,
notBefore,
notAfter,
subject,
keyPair.getPublic()
);
// Add extensions
org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils extUtils =
new org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils();
// Subject Key Identifier
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.subjectKeyIdentifier,
false,
extUtils.createSubjectKeyIdentifier(keyPair.getPublic())
);
// Authority Key Identifier (same as subject for self-signed)
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.authorityKeyIdentifier,
false,
extUtils.createAuthorityKeyIdentifier(keyPair.getPublic())
);
// Basic Constraints: CA = true
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.basicConstraints,
true,
new org.bouncycastle.asn1.x509.BasicConstraints(true)
);
// Sign the certificate
org.bouncycastle.operator.ContentSigner signer =
new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder(
"SHA256WithRSA")
.build(keyPair.getPrivate());
org.bouncycastle.cert.X509CertificateHolder certHolder =
certBuilder.build(signer);
// Convert to X509Certificate
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (java.security.cert.X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(certHolder.getEncoded()));
}
// Generate self-signed certificate with SANs
public static java.security.cert.X509Certificate generateSelfSignedWithSAN(
KeyPair keyPair, String subjectDN, List<String> dnsNames,
int validityDays) throws Exception {
X500Name subject = new X500Name(subjectDN);
BigInteger serialNumber = new BigInteger(Long.toString(
System.currentTimeMillis()));
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() +
(long) validityDays * 24 * 60 * 60 * 1000);
org.bouncycastle.cert.X509v3CertificateBuilder certBuilder =
new org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder(
subject,
serialNumber,
notBefore,
notAfter,
subject,
keyPair.getPublic()
);
// Add Subject Alternative Names
org.bouncycastle.asn1.x509.GeneralName[] generalNames =
new org.bouncycastle.asn1.x509.GeneralName[dnsNames.size()];
for (int i = 0; i < dnsNames.size(); i++) {
generalNames[i] = new org.bouncycastle.asn1.x509.GeneralName(
org.bouncycastle.asn1.x509.GeneralName.dNSName,
dnsNames.get(i)
);
}
org.bouncycastle.asn1.x509.GeneralNames subjectAltNames =
new org.bouncycastle.asn1.x509.GeneralNames(generalNames);
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.subjectAlternativeName,
false,
subjectAltNames
);
// Add standard extensions
org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils extUtils =
new org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils();
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.subjectKeyIdentifier,
false,
extUtils.createSubjectKeyIdentifier(keyPair.getPublic())
);
// Key Usage for server certificate
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.keyUsage,
true,
new org.bouncycastle.asn1.x509.KeyUsage(
org.bouncycastle.asn1.x509.KeyUsage.digitalSignature |
org.bouncycastle.asn1.x509.KeyUsage.keyEncipherment
)
);
// Extended Key Usage: Server Authentication
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.extendedKeyUsage,
true,
new org.bouncycastle.asn1.x509.ExtendedKeyUsage(
org.bouncycastle.asn1.x509.KeyPurposeId.id_kp_serverAuth
)
);
// Sign
org.bouncycastle.operator.ContentSigner signer =
new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder(
"SHA256WithRSA")
.build(keyPair.getPrivate());
org.bouncycastle.cert.X509CertificateHolder certHolder =
certBuilder.build(signer);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (java.security.cert.X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(certHolder.getEncoded()));
}
// Example: Generate and use self-signed certificate
public static void demonstrateSelfSignedGeneration() throws Exception {
// Generate key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
// Generate certificate
java.security.cert.X509Certificate cert = generateSelfSignedCertificate(
keyPair,
"CN=localhost, O=MyCompany, C=US",
365 // valid for 1 year
);
System.out.println("Generated self-signed certificate:");
System.out.println(" Subject: " + cert.getSubjectDN());
System.out.println(" Valid until: " + cert.getNotAfter());
// Store in KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
Certificate[] chain = new Certificate[] { cert };
keyStore.setKeyEntry("selfsigned", keyPair.getPrivate(),
"keypass".toCharArray(), chain);
// Save
try (FileOutputStream fos = new FileOutputStream("selfsigned.p12")) {
keyStore.store(fos, "storepass".toCharArray());
}
System.out.println("Saved to selfsigned.p12");
}
}
Certificate Signing Request (CSR) Generation
// Generating Certificate Signing Requests
public class CSRGenerator {
// Generate PKCS#10 CSR
public static byte[] generateCSR(KeyPair keyPair, String subjectDN)
throws Exception {
X500Name subject = new X500Name(subjectDN);
// Create CSR builder (using BouncyCastle)
org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder csrBuilder =
new org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder(
subject,
keyPair.getPublic()
);
// Sign CSR with private key
org.bouncycastle.operator.ContentSigner signer =
new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder(
"SHA256withRSA")
.build(keyPair.getPrivate());
org.bouncycastle.pkcs.PKCS10CertificationRequest csr =
csrBuilder.build(signer);
return csr.getEncoded();
}
// Generate CSR with Subject Alternative Names
public static byte[] generateCSRWithSAN(KeyPair keyPair, String subjectDN,
List<String> dnsNames) throws Exception {
X500Name subject = new X500Name(subjectDN);
org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder csrBuilder =
new org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder(
subject,
keyPair.getPublic()
);
// Add Subject Alternative Names extension
org.bouncycastle.asn1.x509.GeneralName[] generalNames =
new org.bouncycastle.asn1.x509.GeneralName[dnsNames.size()];
for (int i = 0; i < dnsNames.size(); i++) {
generalNames[i] = new org.bouncycastle.asn1.x509.GeneralName(
org.bouncycastle.asn1.x509.GeneralName.dNSName,
dnsNames.get(i)
);
}
org.bouncycastle.asn1.x509.GeneralNames subjectAltNames =
new org.bouncycastle.asn1.x509.GeneralNames(generalNames);
org.bouncycastle.asn1.pkcs.Attribute attr =
new org.bouncycastle.asn1.pkcs.Attribute(
org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,
new org.bouncycastle.asn1.DERSet(
new org.bouncycastle.asn1.x509.Extensions(
new org.bouncycastle.asn1.x509.Extension(
org.bouncycastle.asn1.x509.Extension.subjectAlternativeName,
false,
new org.bouncycastle.asn1.DEROctetString(
subjectAltNames.getEncoded())
)
)
)
);
csrBuilder.addAttribute(
org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,
new org.bouncycastle.asn1.x509.Extensions(
new org.bouncycastle.asn1.x509.Extension(
org.bouncycastle.asn1.x509.Extension.subjectAlternativeName,
false,
new org.bouncycastle.asn1.DEROctetString(
subjectAltNames.getEncoded())
)
)
);
// Sign CSR
org.bouncycastle.operator.ContentSigner signer =
new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder(
"SHA256withRSA")
.build(keyPair.getPrivate());
org.bouncycastle.pkcs.PKCS10CertificationRequest csr =
csrBuilder.build(signer);
return csr.getEncoded();
}
// Export CSR to PEM format
public static String exportCSRtoPEM(byte[] csrBytes) {
String base64 = java.util.Base64.getEncoder().encodeToString(csrBytes);
StringBuilder pem = new StringBuilder();
pem.append("-----BEGIN CERTIFICATE REQUEST-----\n");
for (int i = 0; i < base64.length(); i += 64) {
int end = Math.min(i + 64, base64.length());
pem.append(base64.substring(i, end)).append("\n");
}
pem.append("-----END CERTIFICATE REQUEST-----\n");
return pem.toString();
}
// Save CSR to file
public static void saveCSR(byte[] csrBytes, String filePath) throws Exception {
String pem = exportCSRtoPEM(csrBytes);
Files.writeString(Paths.get(filePath), pem);
System.out.println("CSR saved to: " + filePath);
}
// Example: Generate CSR workflow
public static void demonstrateCSRGeneration() throws Exception {
// 1. Generate key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
// 2. Generate CSR with SANs
List<String> dnsNames = Arrays.asList(
"example.com",
"www.example.com",
"api.example.com"
);
byte[] csrBytes = generateCSRWithSAN(
keyPair,
"CN=example.com, O=My Company, L=San Francisco, ST=CA, C=US",
dnsNames
);
// 3. Save CSR
saveCSR(csrBytes, "example.csr");
// 4. Save private key (securely)
// In production, encrypt the private key or use HSM
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
Files.write(Paths.get("example.key"), privateKeyBytes);
System.out.println("CSR generation complete:");
System.out.println(" CSR: example.csr");
System.out.println(" Private Key: example.key");
System.out.println("\nSubmit example.csr to your CA for signing");
}
// Verify CSR
public static boolean verifyCSR(byte[] csrBytes) throws Exception {
org.bouncycastle.pkcs.PKCS10CertificationRequest csr =
new org.bouncycastle.pkcs.PKCS10CertificationRequest(csrBytes);
try {
org.bouncycastle.operator.ContentVerifier verifier =
new org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder()
.build(csr.getSubjectPublicKeyInfo())
.get(csr.getSignatureAlgorithm());
boolean valid = csr.isSignatureValid(verifier);
if (valid) {
System.out.println("✓ CSR signature is valid");
} else {
System.out.println("✗ CSR signature is invalid");
}
return valid;
} catch (Exception e) {
System.out.println("✗ CSR verification failed: " + e.getMessage());
return false;
}
}
}
Certificate Signing (CA Operations)
// Signing Certificates (CA Operations)
public class CertificateSigning {
// Sign CSR to create certificate
public static java.security.cert.X509Certificate signCSR(
org.bouncycastle.pkcs.PKCS10CertificationRequest csr,
KeyPair caKeyPair,
java.security.cert.X509Certificate caCert,
int validityDays) throws Exception {
// Extract subject from CSR
X500Name subject = csr.getSubject();
X500Name issuer = new X500Name(caCert.getSubjectDN().getName());
// Generate serial number
BigInteger serialNumber = new BigInteger(Long.toString(
System.currentTimeMillis()));
// Set validity
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() +
(long) validityDays * 24 * 60 * 60 * 1000);
// Build certificate
org.bouncycastle.cert.X509v3CertificateBuilder certBuilder =
new org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder(
issuer,
serialNumber,
notBefore,
notAfter,
subject,
org.bouncycastle.asn1.x509.SubjectPublicKeyInfo.getInstance(
csr.getSubjectPublicKeyInfo().getEncoded())
);
// Add extensions from CSR
org.bouncycastle.asn1.pkcs.Attribute[] attributes =
csr.getAttributes(
org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
.pkcs_9_at_extensionRequest);
if (attributes != null && attributes.length > 0) {
org.bouncycastle.asn1.x509.Extensions extensions =
org.bouncycastle.asn1.x509.Extensions.getInstance(
attributes[0].getAttrValues().getObjectAt(0));
for (org.bouncycastle.asn1.ASN1ObjectIdentifier oid :
extensions.getExtensionOIDs()) {
certBuilder.addExtension(extensions.getExtension(oid));
}
}
// Add standard extensions
org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils extUtils =
new org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils();
// Subject Key Identifier
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.subjectKeyIdentifier,
false,
extUtils.createSubjectKeyIdentifier(
org.bouncycastle.asn1.x509.SubjectPublicKeyInfo.getInstance(
csr.getSubjectPublicKeyInfo().getEncoded()))
);
// Authority Key Identifier
certBuilder.addExtension(
org.bouncycastle.asn1.x509.Extension.authorityKeyIdentifier,
false,
extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey())
);
// Sign with CA private key
org.bouncycastle.operator.ContentSigner signer =
new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder(
"SHA256WithRSA")
.build(caKeyPair.getPrivate());
org.bouncycastle.cert.X509CertificateHolder certHolder =
certBuilder.build(signer);
// Convert to X509Certificate
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (java.security.cert.X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(certHolder.getEncoded()));
}
// Example: Complete CA workflow
public static void demonstrateCAWorkflow() throws Exception {
// 1. Create CA key pair and self-signed certificate
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(4096); // Stronger key for CA
KeyPair caKeyPair = keyGen.generateKeyPair();
java.security.cert.X509Certificate caCert =
SelfSignedCertificateGenerator.generateSelfSignedCertificate(
caKeyPair,
"CN=My CA, O=My Organization, C=US",
3650 // 10 years
);
System.out.println("Created CA certificate:");
System.out.println(" Subject: " + caCert.getSubjectDN());
// 2. Generate server key pair
keyGen.initialize(2048);
KeyPair serverKeyPair = keyGen.generateKeyPair();
// 3. Create CSR
byte[] csrBytes = CSRGenerator.generateCSRWithSAN(
serverKeyPair,
"CN=example.com, O=My Company, C=US",
Arrays.asList("example.com", "www.example.com")
);
// 4. Sign CSR with CA
org.bouncycastle.pkcs.PKCS10CertificationRequest csr =
new org.bouncycastle.pkcs.PKCS10CertificationRequest(csrBytes);
java.security.cert.X509Certificate serverCert =
signCSR(csr, caKeyPair, caCert, 365);
System.out.println("Signed server certificate:");
System.out.println(" Subject: " + serverCert.getSubjectDN());
System.out.println(" Issuer: " + serverCert.getIssuerDN());
// 5. Create certificate chain
Certificate[] chain = new Certificate[] { serverCert, caCert };
// 6. Store in KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
keyStore.setKeyEntry("server", serverKeyPair.getPrivate(),
"keypass".toCharArray(), chain);
try (FileOutputStream fos = new FileOutputStream("server.p12")) {
keyStore.store(fos, "storepass".toCharArray());
}
System.out.println("Server KeyStore saved to server.p12");
}
}
Key Pair Generation
// Key Pair Generation
public class KeyPairGeneration {
// Generate RSA key pair
public static KeyPair generateRSAKeyPair(int keySize) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keySize);
return keyGen.generateKeyPair();
}
// Generate EC key pair
public static KeyPair generateECKeyPair(String curveName) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
// Standard curves: secp256r1, secp384r1, secp521r1
java.security.spec.ECGenParameterSpec ecSpec =
new java.security.spec.ECGenParameterSpec(curveName);
keyGen.initialize(ecSpec);
return keyGen.generateKeyPair();
}
// Generate Ed25519 key pair (Java 15+)
public static KeyPair generateEd25519KeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("Ed25519");
return keyGen.generateKeyPair();
}
// Export private key to PKCS#8 format
public static byte[] exportPrivateKeyPKCS8(PrivateKey privateKey) {
return privateKey.getEncoded(); // PKCS#8 format
}
// Export private key to PEM
public static String exportPrivateKeyPEM(PrivateKey privateKey) {
byte[] encoded = exportPrivateKeyPKCS8(privateKey);
String base64 = java.util.Base64.getEncoder().encodeToString(encoded);
StringBuilder pem = new StringBuilder();
pem.append("-----BEGIN PRIVATE KEY-----\n");
for (int i = 0; i < base64.length(); i += 64) {
int end = Math.min(i + 64, base64.length());
pem.append(base64.substring(i, end)).append("\n");
}
pem.append("-----END PRIVATE KEY-----\n");
return pem.toString();
}
// Export public key to X.509 format
public static byte[] exportPublicKeyX509(PublicKey publicKey) {
return publicKey.getEncoded(); // X.509 SubjectPublicKeyInfo format
}
// Example: Generate different key types
public static void demonstrateKeyGeneration() throws Exception {
System.out.println("=== KEY PAIR GENERATION ===");
// RSA 2048-bit
KeyPair rsa2048 = generateRSAKeyPair(2048);
System.out.println("Generated RSA 2048-bit key pair");
// RSA 4096-bit (for CA)
KeyPair rsa4096 = generateRSAKeyPair(4096);
System.out.println("Generated RSA 4096-bit key pair");
// EC P-256 (secp256r1)
KeyPair ecP256 = generateECKeyPair("secp256r1");
System.out.println("Generated EC P-256 key pair");
// EC P-384
KeyPair ecP384 = generateECKeyPair("secp384r1");
System.out.println("Generated EC P-384 key pair");
// Ed25519 (modern signature algorithm)
KeyPair ed25519 = generateEd25519KeyPair();
System.out.println("Generated Ed25519 key pair");
// Export example
String rsaPEM = exportPrivateKeyPEM(rsa2048.getPrivate());
System.out.println("\nRSA Private Key (PEM):");
System.out.println(rsaPEM);
}
}
Best Practices
- Key size: RSA 2048+ bits, EC P-256+ for security.
- CSR validation: Always verify CSR signature before signing.
- CA security: Store CA private keys in HSM or offline.
- Certificate validity: Server certs 1 year, CA certs 10+ years.
- Subject Alternative Names: Include all hostnames.
- Key usage extensions: Set appropriate key usage flags.
- Serial numbers: Use cryptographically random serial numbers.
- Signature algorithms: Use SHA-256 or stronger.
- Private key protection: Encrypt or use secure storage.
- Audit trail: Log all certificate signing operations.