diff --git a/handler/src/main/java/io/netty/handler/ssl/util/BouncyCastleSelfSignedCertGenerator.java b/handler/src/main/java/io/netty/handler/ssl/util/BouncyCastleSelfSignedCertGenerator.java index eacffec7bc..704d9da71d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/BouncyCastleSelfSignedCertGenerator.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/BouncyCastleSelfSignedCertGenerator.java @@ -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()); diff --git a/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java b/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java index 30d74e2705..3efb565f13 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java @@ -42,8 +42,8 @@ import static io.netty.handler.ssl.util.SelfSignedCertificate.*; */ final class OpenJdkSelfSignedCertGenerator { - 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. @@ -68,12 +68,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); diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java index c0b8467bd7..eb23ce3d68 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java @@ -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. *

- * 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()}. *

@@ -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. + *

Algorithm: RSA

*/ 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. + *

Algorithm: RSA

+ * * @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. + *

Algorithm: RSA

+ * * @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. + *

Algorithm: RSA

+ * + * @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. + *

Algorithm: RSA

+ * + * @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. + *

Algorithm: RSA

+ * + * @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(); }