Specify algorithm for key pair in self signed certificate to generate EC or RSA based certificate. (#10223)
Motivation: EC is better than RSA because of the small key size, efficient and secure which makes it perfect for testing purposes. Modification: Added support to specify an algorithm (EC or RSA) in constructors for key pair generation. The default key size is 256-bits as promulgated by NSA Suite B. Result: Able to generate a self-signed certificate of EC or RSA.
This commit is contained in:
parent
a62fcd9d50
commit
f22993a530
@ -42,8 +42,8 @@ final class BouncyCastleSelfSignedCertGenerator {
|
|||||||
|
|
||||||
private static final Provider PROVIDER = new BouncyCastleProvider();
|
private static final Provider PROVIDER = new BouncyCastleProvider();
|
||||||
|
|
||||||
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter)
|
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter,
|
||||||
throws Exception {
|
String algorithm) throws Exception {
|
||||||
PrivateKey key = keypair.getPrivate();
|
PrivateKey key = keypair.getPrivate();
|
||||||
|
|
||||||
// Prepare the information required for generating an X.509 certificate.
|
// Prepare the information required for generating an X.509 certificate.
|
||||||
@ -51,7 +51,8 @@ final class BouncyCastleSelfSignedCertGenerator {
|
|||||||
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
|
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
|
||||||
owner, new BigInteger(64, random), notBefore, notAfter, owner, keypair.getPublic());
|
owner, new BigInteger(64, random), notBefore, notAfter, owner, keypair.getPublic());
|
||||||
|
|
||||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(key);
|
ContentSigner signer = new JcaContentSignerBuilder(
|
||||||
|
algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256WithRSAEncryption").build(key);
|
||||||
X509CertificateHolder certHolder = builder.build(signer);
|
X509CertificateHolder certHolder = builder.build(signer);
|
||||||
X509Certificate cert = new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder);
|
X509Certificate cert = new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder);
|
||||||
cert.verify(keypair.getPublic());
|
cert.verify(keypair.getPublic());
|
||||||
|
@ -42,8 +42,8 @@ import static io.netty.handler.ssl.util.SelfSignedCertificate.*;
|
|||||||
*/
|
*/
|
||||||
final class OpenJdkSelfSignedCertGenerator {
|
final class OpenJdkSelfSignedCertGenerator {
|
||||||
|
|
||||||
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter)
|
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter,
|
||||||
throws Exception {
|
String algorithm) throws Exception {
|
||||||
PrivateKey key = keypair.getPrivate();
|
PrivateKey key = keypair.getPrivate();
|
||||||
|
|
||||||
// Prepare the information required for generating an X.509 certificate.
|
// Prepare the information required for generating an X.509 certificate.
|
||||||
@ -68,12 +68,12 @@ final class OpenJdkSelfSignedCertGenerator {
|
|||||||
|
|
||||||
// Sign the cert to identify the algorithm that's used.
|
// Sign the cert to identify the algorithm that's used.
|
||||||
X509CertImpl cert = new X509CertImpl(info);
|
X509CertImpl cert = new X509CertImpl(info);
|
||||||
cert.sign(key, "SHA256withRSA");
|
cert.sign(key, algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
|
||||||
|
|
||||||
// Update the algorithm and sign again.
|
// Update the algorithm and sign again.
|
||||||
info.set(CertificateAlgorithmId.NAME + '.' + CertificateAlgorithmId.ALGORITHM, cert.get(X509CertImpl.SIG_ALG));
|
info.set(CertificateAlgorithmId.NAME + '.' + CertificateAlgorithmId.ALGORITHM, cert.get(X509CertImpl.SIG_ALG));
|
||||||
cert = new X509CertImpl(info);
|
cert = new X509CertImpl(info);
|
||||||
cert.sign(key, "SHA256withRSA");
|
cert.sign(key, algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
|
||||||
cert.verify(keypair.getPublic());
|
cert.verify(keypair.getPublic());
|
||||||
|
|
||||||
return newSelfSignedCertificate(fqdn, key, cert);
|
return newSelfSignedCertificate(fqdn, key, cert);
|
||||||
|
@ -49,7 +49,7 @@ import java.util.Date;
|
|||||||
* It is purely for testing purposes, and thus it is very insecure.
|
* It is purely for testing purposes, and thus it is very insecure.
|
||||||
* It even uses an insecure pseudo-random generator for faster generation internally.
|
* It even uses an insecure pseudo-random generator for faster generation internally.
|
||||||
* </p><p>
|
* </p><p>
|
||||||
* An X.509 certificate file and a RSA private key file are generated in a system's temporary directory using
|
* An X.509 certificate file and a EC/RSA private key file are generated in a system's temporary directory using
|
||||||
* {@link java.io.File#createTempFile(String, String)}, and they are deleted when the JVM exits using
|
* {@link java.io.File#createTempFile(String, String)}, and they are deleted when the JVM exits using
|
||||||
* {@link java.io.File#deleteOnExit()}.
|
* {@link java.io.File#deleteOnExit()}.
|
||||||
* </p><p>
|
* </p><p>
|
||||||
@ -69,7 +69,7 @@ public final class SelfSignedCertificate {
|
|||||||
"io.netty.selfSignedCertificate.defaultNotAfter", 253402300799000L));
|
"io.netty.selfSignedCertificate.defaultNotAfter", 253402300799000L));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIPS 140-2 encryption requires the key length to be 2048 bits or greater.
|
* FIPS 140-2 encryption requires the RSA key length to be 2048 bits or greater.
|
||||||
* Let's use that as a sane default but allow the default to be set dynamically
|
* Let's use that as a sane default but allow the default to be set dynamically
|
||||||
* for those that need more stringent security requirements.
|
* for those that need more stringent security requirements.
|
||||||
*/
|
*/
|
||||||
@ -83,84 +83,166 @@ public final class SelfSignedCertificate {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
|
* <p> Algorithm: RSA </p>
|
||||||
*/
|
*/
|
||||||
public SelfSignedCertificate() throws CertificateException {
|
public SelfSignedCertificate() throws CertificateException {
|
||||||
this(DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
|
this(DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, "RSA", DEFAULT_KEY_LENGTH_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
|
* <p> Algorithm: RSA </p>
|
||||||
|
*
|
||||||
* @param notBefore Certificate is not valid before this time
|
* @param notBefore Certificate is not valid before this time
|
||||||
* @param notAfter Certificate is not valid after this time
|
* @param notAfter Certificate is not valid after this time
|
||||||
*/
|
*/
|
||||||
public SelfSignedCertificate(Date notBefore, Date notAfter) throws CertificateException {
|
public SelfSignedCertificate(Date notBefore, Date notAfter)
|
||||||
this("example.com", notBefore, notAfter);
|
throws CertificateException {
|
||||||
|
this("localhost", notBefore, notAfter, "RSA", DEFAULT_KEY_LENGTH_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*
|
*
|
||||||
|
* @param notBefore Certificate is not valid before this time
|
||||||
|
* @param notAfter Certificate is not valid after this time
|
||||||
|
* @param algorithm Key pair algorithm
|
||||||
|
* @param bits the number of bits of the generated private key
|
||||||
|
*/
|
||||||
|
public SelfSignedCertificate(Date notBefore, Date notAfter, String algorithm, int bits)
|
||||||
|
throws CertificateException {
|
||||||
|
this("localhost", notBefore, notAfter, algorithm, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* <p> Algorithm: RSA </p>
|
||||||
|
*
|
||||||
* @param fqdn a fully qualified domain name
|
* @param fqdn a fully qualified domain name
|
||||||
*/
|
*/
|
||||||
public SelfSignedCertificate(String fqdn) throws CertificateException {
|
public SelfSignedCertificate(String fqdn) throws CertificateException {
|
||||||
this(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
|
this(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, "RSA", DEFAULT_KEY_LENGTH_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*
|
*
|
||||||
* @param fqdn a fully qualified domain name
|
* @param fqdn a fully qualified domain name
|
||||||
|
* @param algorithm Key pair algorithm
|
||||||
|
* @param bits the number of bits of the generated private key
|
||||||
|
*/
|
||||||
|
public SelfSignedCertificate(String fqdn, String algorithm, int bits) throws CertificateException {
|
||||||
|
this(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, algorithm, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* <p> Algorithm: RSA </p>
|
||||||
|
*
|
||||||
|
* @param fqdn a fully qualified domain name
|
||||||
* @param notBefore Certificate is not valid before this time
|
* @param notBefore Certificate is not valid before this time
|
||||||
* @param notAfter Certificate is not valid after this time
|
* @param notAfter Certificate is not valid after this time
|
||||||
*/
|
*/
|
||||||
public SelfSignedCertificate(String fqdn, Date notBefore, Date notAfter) throws CertificateException {
|
public SelfSignedCertificate(String fqdn, Date notBefore, Date notAfter) throws CertificateException {
|
||||||
// Bypass entropy collection by using insecure random generator.
|
// Bypass entropy collection by using insecure random generator.
|
||||||
// We just want to generate it without any delay because it's for testing purposes only.
|
// We just want to generate it without any delay because it's for testing purposes only.
|
||||||
this(fqdn, ThreadLocalInsecureRandom.current(), DEFAULT_KEY_LENGTH_BITS, notBefore, notAfter);
|
this(fqdn, ThreadLocalInsecureRandom.current(), DEFAULT_KEY_LENGTH_BITS, notBefore, notAfter, "RSA");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*
|
*
|
||||||
* @param fqdn a fully qualified domain name
|
* @param fqdn a fully qualified domain name
|
||||||
* @param random the {@link java.security.SecureRandom} to use
|
|
||||||
* @param bits the number of bits of the generated private key
|
|
||||||
*/
|
|
||||||
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits) throws CertificateException {
|
|
||||||
this(fqdn, random, bits, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param fqdn a fully qualified domain name
|
|
||||||
* @param random the {@link java.security.SecureRandom} to use
|
|
||||||
* @param bits the number of bits of the generated private key
|
|
||||||
* @param notBefore Certificate is not valid before this time
|
* @param notBefore Certificate is not valid before this time
|
||||||
* @param notAfter Certificate is not valid after this time
|
* @param notAfter Certificate is not valid after this time
|
||||||
|
* @param algorithm Key pair algorithm
|
||||||
|
* @param bits the number of bits of the generated private key
|
||||||
|
*/
|
||||||
|
public SelfSignedCertificate(String fqdn, Date notBefore, Date notAfter, String algorithm, int bits)
|
||||||
|
throws CertificateException {
|
||||||
|
// Bypass entropy collection by using insecure random generator.
|
||||||
|
// We just want to generate it without any delay because it's for testing purposes only.
|
||||||
|
this(fqdn, ThreadLocalInsecureRandom.current(), bits, notBefore, notAfter, algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* <p> Algorithm: RSA </p>
|
||||||
|
*
|
||||||
|
* @param fqdn a fully qualified domain name
|
||||||
|
* @param random the {@link SecureRandom} to use
|
||||||
|
* @param bits the number of bits of the generated private key
|
||||||
|
*/
|
||||||
|
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits)
|
||||||
|
throws CertificateException {
|
||||||
|
this(fqdn, random, bits, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, "RSA");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param fqdn a fully qualified domain name
|
||||||
|
* @param random the {@link SecureRandom} to use
|
||||||
|
* @param algorithm Key pair algorithm
|
||||||
|
* @param bits the number of bits of the generated private key
|
||||||
|
*/
|
||||||
|
public SelfSignedCertificate(String fqdn, SecureRandom random, String algorithm, int bits)
|
||||||
|
throws CertificateException {
|
||||||
|
this(fqdn, random, bits, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* <p> Algorithm: RSA </p>
|
||||||
|
*
|
||||||
|
* @param fqdn a fully qualified domain name
|
||||||
|
* @param random the {@link SecureRandom} to use
|
||||||
|
* @param bits the number of bits of the generated private key
|
||||||
|
* @param notBefore Certificate is not valid before this time
|
||||||
|
* @param notAfter Certificate is not valid after this time
|
||||||
*/
|
*/
|
||||||
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits, Date notBefore, Date notAfter)
|
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits, Date notBefore, Date notAfter)
|
||||||
throws CertificateException {
|
throws CertificateException {
|
||||||
// Generate an RSA key pair.
|
this(fqdn, random, bits, notBefore, notAfter, "RSA");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param fqdn a fully qualified domain name
|
||||||
|
* @param random the {@link SecureRandom} to use
|
||||||
|
* @param bits the number of bits of the generated private key
|
||||||
|
* @param notBefore Certificate is not valid before this time
|
||||||
|
* @param notAfter Certificate is not valid after this time
|
||||||
|
* @param algorithm Key pair algorithm
|
||||||
|
*/
|
||||||
|
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits, Date notBefore, Date notAfter,
|
||||||
|
String algorithm) throws CertificateException {
|
||||||
|
|
||||||
|
if (!algorithm.equalsIgnoreCase("EC") && !algorithm.equalsIgnoreCase("RSA")) {
|
||||||
|
throw new IllegalArgumentException("Algorithm not valid: " + algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
final KeyPair keypair;
|
final KeyPair keypair;
|
||||||
try {
|
try {
|
||||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm);
|
||||||
keyGen.initialize(bits, random);
|
keyGen.initialize(bits, random);
|
||||||
keypair = keyGen.generateKeyPair();
|
keypair = keyGen.generateKeyPair();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
// Should not reach here because every Java implementation must have RSA key pair generator.
|
// Should not reach here because every Java implementation must have RSA and EC key pair generator.
|
||||||
throw new Error(e);
|
throw new Error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] paths;
|
String[] paths;
|
||||||
try {
|
try {
|
||||||
// Try the OpenJDK's proprietary implementation.
|
// Try the OpenJDK's proprietary implementation.
|
||||||
paths = OpenJdkSelfSignedCertGenerator.generate(fqdn, keypair, random, notBefore, notAfter);
|
paths = OpenJdkSelfSignedCertGenerator.generate(fqdn, keypair, random, notBefore, notAfter, algorithm);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.debug("Failed to generate a self-signed X.509 certificate using sun.security.x509:", t);
|
logger.debug("Failed to generate a self-signed X.509 certificate using sun.security.x509:", t);
|
||||||
try {
|
try {
|
||||||
// Try Bouncy Castle if the current JVM didn't have sun.security.x509.
|
// Try Bouncy Castle if the current JVM didn't have sun.security.x509.
|
||||||
paths = BouncyCastleSelfSignedCertGenerator.generate(fqdn, keypair, random, notBefore, notAfter);
|
paths = BouncyCastleSelfSignedCertGenerator.generate(
|
||||||
|
fqdn, keypair, random, notBefore, notAfter, algorithm);
|
||||||
} catch (Throwable t2) {
|
} catch (Throwable t2) {
|
||||||
logger.debug("Failed to generate a self-signed X.509 certificate using Bouncy Castle:", t2);
|
logger.debug("Failed to generate a self-signed X.509 certificate using Bouncy Castle:", t2);
|
||||||
final CertificateException certificateException = new CertificateException(
|
final CertificateException certificateException = new CertificateException(
|
||||||
@ -239,8 +321,8 @@ public final class SelfSignedCertificate {
|
|||||||
encodedBuf = Base64.encode(wrappedBuf, true);
|
encodedBuf = Base64.encode(wrappedBuf, true);
|
||||||
try {
|
try {
|
||||||
keyText = "-----BEGIN PRIVATE KEY-----\n" +
|
keyText = "-----BEGIN PRIVATE KEY-----\n" +
|
||||||
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
||||||
"\n-----END PRIVATE KEY-----\n";
|
"\n-----END PRIVATE KEY-----\n";
|
||||||
} finally {
|
} finally {
|
||||||
encodedBuf.release();
|
encodedBuf.release();
|
||||||
}
|
}
|
||||||
@ -270,8 +352,8 @@ public final class SelfSignedCertificate {
|
|||||||
try {
|
try {
|
||||||
// Encode the certificate into a CRT file.
|
// Encode the certificate into a CRT file.
|
||||||
certText = "-----BEGIN CERTIFICATE-----\n" +
|
certText = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
||||||
"\n-----END CERTIFICATE-----\n";
|
"\n-----END CERTIFICATE-----\n";
|
||||||
} finally {
|
} finally {
|
||||||
encodedBuf.release();
|
encodedBuf.release();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user