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 74e4f3bda6
commit db0fdfe706
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.SecureRandom;
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>.
@ -41,13 +42,14 @@ final class BouncyCastleSelfSignedCertGenerator {
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();
// Prepare the information required for generating an X.509 certificate.
X500Name owner = new X500Name("CN=" + fqdn);
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);
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.X509CertInfo;
import java.util.Date;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
@ -41,7 +42,8 @@ import static io.netty.handler.ssl.util.SelfSignedCertificate.*;
*/
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();
// Prepare the information required for generating an X.509 certificate.
@ -59,7 +61,7 @@ final class OpenJdkSelfSignedCertGenerator {
} catch (CertificateException ignore) {
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.ALGORITHM_ID,
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.handler.codec.base64.Base64;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -59,9 +60,11 @@ public final class SelfSignedCertificate {
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 */
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 */
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 privateKey;
@ -72,6 +75,15 @@ public final class SelfSignedCertificate {
* Creates a new instance.
*/
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");
}
@ -81,9 +93,20 @@ public final class SelfSignedCertificate {
* @param fqdn a fully qualified domain name
*/
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.
// 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
*/
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.
final KeyPair keypair;
try {
@ -108,12 +145,12 @@ public final class SelfSignedCertificate {
String[] paths;
try {
// Try the OpenJDK's proprietary implementation.
paths = OpenJdkSelfSignedCertGenerator.generate(fqdn, keypair, random);
paths = OpenJdkSelfSignedCertGenerator.generate(fqdn, keypair, random, notBefore, notAfter);
} 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);
paths = BouncyCastleSelfSignedCertGenerator.generate(fqdn, keypair, random, notBefore, notAfter);
} catch (Throwable t2) {
logger.debug("Failed to generate a self-signed X.509 certificate using Bouncy Castle:", t2);
throw new CertificateException(