|
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); |
|
} |