Skip to content

Instantly share code, notes, and snippets.

@boseabhishek
Last active April 13, 2023 15:46
Show Gist options
  • Save boseabhishek/3864c6c6fe3e5959990c52dbd06624e9 to your computer and use it in GitHub Desktop.
Save boseabhishek/3864c6c6fe3e5959990c52dbd06624e9 to your computer and use it in GitHub Desktop.
X.509 Certificate/Keys | Java | BouncyCastle | DER | Base64 | Human

Notes

PEM vs Base64? Why both?

PEM - Format type Base64 - content of the file encoding type

PEM (originally “Privacy Enhanced Mail”) is the most common format for X.509 certificates, CSRs, and cryptographic keys(public/private keys).

A PEM file is a text file containing one or more items in Base64 ASCII encoding, each with plain-text headers and footers (e.g. -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----).

A single PEM file could contain an end-entity certificate, a private key, or multiple certificates forming a complete chain of trust.

Most certificate files downloaded from SSL.com will be in PEM format.

https://www.ssl.com/guide/pem-der-crt-and-cer-x-509-encodings-and-conversions/

package something;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import javax.security.auth.x500.X500Principal;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Date;
record TLSData(String cert, String key) {
}
/**
* Generates X.509 TLS Certificate and private key for the DNS name provided.
* This outputs the certs as files (side-effect) in diff formats, as well as return them as string.
*
* @param domainName - CA/Server DNS for which the certificate is needed.
* @param outputHumanReadableCertFilePath - path with a file name (e.g. tls.crt) where you want to output the human-readable cert file.
* @param outputDerEncodedCertFilePath - path with a file name (e.g. tls-der.crt) where you want to output the DER encoded cert file.
* @param outputBase64CertFilePath - path with a file name (e.g. tls-base64.crt) where you want to output the BASE64 encoded cert file.
*
* @return TLSDate containing cert and private key in string format.
*/
private static TLSData generateX509CertKeyPair(String domainName,
String outputHumanReadableCertFilePath,
String outputDerEncodedCertFilePath,
String outputBase64CertFilePath) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
X500Principal subjectDN = new X500Principal("CN=" + domainName);
X500Principal issuerDN = subjectDN;
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
Date notBefore = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000L); // Yesterday
Date notAfter = new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000L); // One year from now
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
issuerDN,
serialNumber,
notBefore,
notAfter,
subjectDN,
publicKey
);
// Add the subject alternative name extension
GeneralNames subjectAltNames = new GeneralNames(new GeneralName(GeneralName.dNSName, "*." + domainName));
certBuilder.addExtension(X509Extension.subjectAlternativeName, false, subjectAltNames);
// Sign the certificate
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate());
X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner));
// Create a file tls.crt in human-readable format
PrintWriter pw = new PrintWriter(new FileWriter(outputHumanReadableCertFilePath));
pw.print(certificate);
pw.close();
// Create a file tls-der.crt in DER encoded format
FileOutputStream fosDer = new FileOutputStream(outputDerEncodedCertFilePath);
fosDer.write(certificate.getEncoded()); // write takes []byte
fosDer.close();
// Create a file tls-base64.crt in BASE64 encoded format
FileOutputStream fosB64 = new FileOutputStream(outputBase64CertFilePath);
fosB64.write(Base64.getEncoder().encode(certificate.getEncoded())); // write takes []byte
fosB64.close();
//
}
// This generates the cert and key base64 encoded and in PEM format
// PEM (originally “Privacy Enhanced Mail”) is the most common format for X.509 certificates, CSRs, and cryptographic keys
private static TLSData toPemTlsData(X509Certificate cert, PrivateKey key) throws Exception {
StringWriter sw = new StringWriter();
PEMWriter pmw = new PEMWriter(sw);
pmw.writeObject(key);
pmw.flush();
String privateKeyPEM = sw.toString();
sw = new StringWriter();
pmw = new PEMWriter(sw);
pmw.writeObject(cert);
pmw.flush();
String certPEM = sw.toString();
// Output the private key and certificate in PEM format
System.out.println("Private key:\n" + privateKeyPEM);
System.out.println("Certificate:\n" + certPEM);
String pemCertWithBase64encoding = Base64.getEncoder().encodeToString(certPEM.getBytes());
String pemKeyWithBase64encoding = Base64.getEncoder().encodeToString(privateKeyPEM.getBytes());
return new TLSData(pemCertWithBase64encoding, pemKeyWithBase64encoding);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment