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
8f7ca2b4ef
commit
9427255ffb
@ -42,8 +42,8 @@ final class BouncyCastleSelfSignedCertGenerator {
|
||||
|
||||
private static final Provider PROVIDER = new BouncyCastleProvider();
|
||||
|
||||
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter)
|
||||
throws Exception {
|
||||
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter,
|
||||
String algorithm) throws Exception {
|
||||
PrivateKey key = keypair.getPrivate();
|
||||
|
||||
// Prepare the information required for generating an X.509 certificate.
|
||||
@ -51,7 +51,8 @@ final class BouncyCastleSelfSignedCertGenerator {
|
||||
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
|
||||
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);
|
||||
X509Certificate cert = new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder);
|
||||
cert.verify(keypair.getPublic());
|
||||
|
@ -44,8 +44,8 @@ import static io.netty.handler.ssl.util.SelfSignedCertificate.*;
|
||||
final class OpenJdkSelfSignedCertGenerator {
|
||||
|
||||
@SuppressJava6Requirement(reason = "Usage guarded by dependency check")
|
||||
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter)
|
||||
throws Exception {
|
||||
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter,
|
||||
String algorithm) throws Exception {
|
||||
PrivateKey key = keypair.getPrivate();
|
||||
|
||||
// Prepare the information required for generating an X.509 certificate.
|
||||
@ -70,12 +70,12 @@ final class OpenJdkSelfSignedCertGenerator {
|
||||
|
||||
// Sign the cert to identify the algorithm that's used.
|
||||
X509CertImpl cert = new X509CertImpl(info);
|
||||
cert.sign(key, "SHA256withRSA");
|
||||
cert.sign(key, algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
|
||||
|
||||
// Update the algorithm and sign again.
|
||||
info.set(CertificateAlgorithmId.NAME + '.' + CertificateAlgorithmId.ALGORITHM, cert.get(X509CertImpl.SIG_ALG));
|
||||
cert = new X509CertImpl(info);
|
||||
cert.sign(key, "SHA256withRSA");
|
||||
cert.sign(key, algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256withRSA");
|
||||
cert.verify(keypair.getPublic());
|
||||
|
||||
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 even uses an insecure pseudo-random generator for faster generation internally.
|
||||
* </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#deleteOnExit()}.
|
||||
* </p><p>
|
||||
@ -69,7 +69,7 @@ public final class SelfSignedCertificate {
|
||||
"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
|
||||
* for those that need more stringent security requirements.
|
||||
*/
|
||||
@ -83,84 +83,166 @@ public final class SelfSignedCertificate {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* <p> Algorithm: RSA </p>
|
||||
*/
|
||||
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.
|
||||
* <p> Algorithm: RSA </p>
|
||||
*
|
||||
* @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 {
|
||||
this("example.com", notBefore, notAfter);
|
||||
public SelfSignedCertificate(Date notBefore, Date notAfter)
|
||||
throws CertificateException {
|
||||
this("localhost", notBefore, notAfter, "RSA", DEFAULT_KEY_LENGTH_BITS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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 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 {
|
||||
// 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(), DEFAULT_KEY_LENGTH_BITS, notBefore, notAfter);
|
||||
this(fqdn, ThreadLocalInsecureRandom.current(), DEFAULT_KEY_LENGTH_BITS, notBefore, notAfter, "RSA");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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 fqdn a fully qualified domain name
|
||||
* @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)
|
||||
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;
|
||||
try {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm);
|
||||
keyGen.initialize(bits, random);
|
||||
keypair = keyGen.generateKeyPair();
|
||||
} 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);
|
||||
}
|
||||
|
||||
String[] paths;
|
||||
try {
|
||||
// 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) {
|
||||
logger.debug("Failed to generate a self-signed X.509 certificate using sun.security.x509:", t);
|
||||
try {
|
||||
// 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) {
|
||||
logger.debug("Failed to generate a self-signed X.509 certificate using Bouncy Castle:", t2);
|
||||
final CertificateException certificateException = new CertificateException(
|
||||
@ -239,8 +321,8 @@ public final class SelfSignedCertificate {
|
||||
encodedBuf = Base64.encode(wrappedBuf, true);
|
||||
try {
|
||||
keyText = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
||||
"\n-----END PRIVATE KEY-----\n";
|
||||
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
||||
"\n-----END PRIVATE KEY-----\n";
|
||||
} finally {
|
||||
encodedBuf.release();
|
||||
}
|
||||
@ -270,8 +352,8 @@ public final class SelfSignedCertificate {
|
||||
try {
|
||||
// Encode the certificate into a CRT file.
|
||||
certText = "-----BEGIN CERTIFICATE-----\n" +
|
||||
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
||||
"\n-----END CERTIFICATE-----\n";
|
||||
encodedBuf.toString(CharsetUtil.US_ASCII) +
|
||||
"\n-----END CERTIFICATE-----\n";
|
||||
} finally {
|
||||
encodedBuf.release();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user