SelfSignedCertificate configurable valid dates

Motivation:
Users may want to control the valid dates for SelfSignedCertificate.

Modifications:
- Allow NOT_BEFORE and NOT_AFTER to be controlled via java system properties.

Result:
Fixes https://github.com/netty/netty/issues/3978
This commit is contained in:
Scott Mitchell 2015-09-22 11:25:48 -07:00
parent ed4928f62a
commit c116c35ed0
3 changed files with 51 additions and 10 deletions

View File

@ -31,8 +31,9 @@ import java.security.PrivateKey;
import java.security.Provider; import java.security.Provider;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Date;
import static io.netty.handler.ssl.util.SelfSignedCertificate.*; import static io.netty.handler.ssl.util.SelfSignedCertificate.newSelfSignedCertificate;
/** /**
* Generates a self-signed certificate using <a href="http://www.bouncycastle.org/">Bouncy Castle</a>. * Generates a self-signed certificate using <a href="http://www.bouncycastle.org/">Bouncy Castle</a>.
@ -41,13 +42,14 @@ 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) throws Exception { static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter)
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.
X500Name owner = new X500Name("CN=" + fqdn); X500Name owner = new X500Name("CN=" + fqdn);
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder( X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
owner, new BigInteger(64, random), NOT_BEFORE, NOT_AFTER, owner, keypair.getPublic()); owner, new BigInteger(64, random), notBefore, notAfter, owner, keypair.getPublic());
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(key); ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(key);
X509CertificateHolder certHolder = builder.build(signer); X509CertificateHolder certHolder = builder.build(signer);

View File

@ -28,6 +28,7 @@ import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl; import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo; import sun.security.x509.X509CertInfo;
import java.util.Date;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.PrivateKey; import java.security.PrivateKey;
@ -41,7 +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) throws Exception { static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter)
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.
@ -59,7 +61,7 @@ final class OpenJdkSelfSignedCertGenerator {
} catch (CertificateException ignore) { } catch (CertificateException ignore) {
info.set(X509CertInfo.ISSUER, owner); info.set(X509CertInfo.ISSUER, owner);
} }
info.set(X509CertInfo.VALIDITY, new CertificateValidity(NOT_BEFORE, NOT_AFTER)); info.set(X509CertInfo.VALIDITY, new CertificateValidity(notBefore, notAfter));
info.set(X509CertInfo.KEY, new CertificateX509Key(keypair.getPublic())); info.set(X509CertInfo.KEY, new CertificateX509Key(keypair.getPublic()));
info.set(X509CertInfo.ALGORITHM_ID, info.set(X509CertInfo.ALGORITHM_ID,
new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid))); new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid)));

View File

@ -19,6 +19,7 @@ package io.netty.handler.ssl.util;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.base64.Base64;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -59,9 +60,11 @@ public final class SelfSignedCertificate {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SelfSignedCertificate.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(SelfSignedCertificate.class);
/** Current time minus 1 year, just in case software clock goes back due to time synchronization */ /** Current time minus 1 year, just in case software clock goes back due to time synchronization */
static final Date NOT_BEFORE = new Date(System.currentTimeMillis() - 86400000L * 365); private static final Date DEFAULT_NOT_BEFORE = new Date(SystemPropertyUtil.getLong(
"io.netty.selfSignedCertificate.defaultNotBefore", System.currentTimeMillis() - 86400000L * 365));
/** The maximum possible value in X.509 specification: 9999-12-31 23:59:59 */ /** The maximum possible value in X.509 specification: 9999-12-31 23:59:59 */
static final Date NOT_AFTER = new Date(253402300799000L); private static final Date DEFAULT_NOT_AFTER = new Date(SystemPropertyUtil.getLong(
"io.netty.selfSignedCertificate.defaultNotAfter", 253402300799000L));
private final File certificate; private final File certificate;
private final File privateKey; private final File privateKey;
@ -72,6 +75,15 @@ public final class SelfSignedCertificate {
* Creates a new instance. * Creates a new instance.
*/ */
public SelfSignedCertificate() throws CertificateException { public SelfSignedCertificate() throws CertificateException {
this(DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
}
/**
* Creates a new instance.
* @param notBefore Certificate is not valid before this time
* @param notAfter Certificate is not valid after this time
*/
public SelfSignedCertificate(Date notBefore, Date notAfter) throws CertificateException {
this("example.com"); this("example.com");
} }
@ -81,9 +93,20 @@ public final class SelfSignedCertificate {
* @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);
}
/**
* Creates a new instance.
*
* @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
*/
public SelfSignedCertificate(String fqdn, Date notBefore, Date notAfter) throws CertificateException {
// Bypass entrophy collection by using insecure random generator. // Bypass entrophy 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(), 1024); this(fqdn, ThreadLocalInsecureRandom.current(), 1024, notBefore, notAfter);
} }
/** /**
@ -94,6 +117,20 @@ public final class SelfSignedCertificate {
* @param bits the number of bits of the generated private key * @param bits the number of bits of the generated private key
*/ */
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits) throws CertificateException { 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 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. // Generate an RSA key pair.
final KeyPair keypair; final KeyPair keypair;
try { try {
@ -108,12 +145,12 @@ public final class SelfSignedCertificate {
String[] paths; String[] paths;
try { try {
// Try the OpenJDK's proprietary implementation. // Try the OpenJDK's proprietary implementation.
paths = OpenJdkSelfSignedCertGenerator.generate(fqdn, keypair, random); paths = OpenJdkSelfSignedCertGenerator.generate(fqdn, keypair, random, notBefore, notAfter);
} 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); paths = BouncyCastleSelfSignedCertGenerator.generate(fqdn, keypair, random, notBefore, notAfter);
} 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);
throw new CertificateException( throw new CertificateException(