Skip to content
Permalink
Browse files

Use CA cert in tests (#475)

* Use CA cert in tests

Our tests were using a self-signed cert created by a netty utility.
That cert is not CA, and cannot be used with newer JDK versions
(starting from u242 with Ubuntu openjdk).

Now we generate a unsafe cert for tests. The new cert,
SelfSignedCaCertificate, needs to be made test-only.
To do so we need to refactor both prober and proxy.
  • Loading branch information
weiminyu committed Feb 7, 2020
1 parent d6f49f5 commit 736f788eea015ecc6b11e08efc8aeb69756b9d97
@@ -41,12 +41,6 @@ dependencies {
testAnnotationProcessor deps['com.google.dagger:dagger-compiler']
}

test {
// Temporarily allow non-CA cert as trust anchor (legacy behavior) in tests.
// TODO(weiminyu): generate test cert as a CA cert.
systemProperty 'jdk.security.allowNonCaAnchor', 'true'
}

// Make testing artifacts available to be depended up on by other projects.
task testJar(type: Jar) {
classifier = 'test'
@@ -25,7 +25,7 @@
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import google.registry.networking.util.SelfSignedCaCertificate;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -163,9 +163,9 @@ private CertificateSupplierModule() {}

@Singleton
@Provides
static SelfSignedCertificate provideSelfSignedCertificate() {
static SelfSignedCaCertificate provideSelfSignedCertificate() {
try {
return new SelfSignedCertificate();
return SelfSignedCaCertificate.create();
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -174,15 +174,15 @@ static SelfSignedCertificate provideSelfSignedCertificate() {
@Singleton
@Provides
@SelfSigned
static Supplier<PrivateKey> provideSelfSignedPrivateKeySupplier(SelfSignedCertificate ssc) {
static Supplier<PrivateKey> provideSelfSignedPrivateKeySupplier(SelfSignedCaCertificate ssc) {
return Suppliers.ofInstance(ssc.key());
}

@Singleton
@Provides
@SelfSigned
static Supplier<ImmutableList<X509Certificate>> provideSelfSignedCertificatesSupplier(
SelfSignedCertificate ssc) {
SelfSignedCaCertificate ssc) {
return Suppliers.ofInstance(ImmutableList.of(ssc.cert()));
}

@@ -0,0 +1,112 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package google.registry.networking.util;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Random;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

/** A self-signed certificate authority (CA) cert for use in tests. */
// TODO(weiminyu): make this class test-only. Requires refactor in proxy and prober.
public class SelfSignedCaCertificate {

private static final String DEFAULT_ISSUER_FQDN = "registry-test";
private static final Date DEFAULT_NOT_BEFORE =
Date.from(Instant.now().minus(Duration.ofHours(1)));
private static final Date DEFAULT_NOT_AFTER = Date.from(Instant.now().plus(Duration.ofDays(1)));

private static final Random RANDOM = new Random();
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
private static final KeyPairGenerator keyGen = createKeyPairGenerator();

private final PrivateKey privateKey;
private final X509Certificate cert;

public SelfSignedCaCertificate(PrivateKey privateKey, X509Certificate cert) {
this.privateKey = privateKey;
this.cert = cert;
}

public PrivateKey key() {
return privateKey;
}

public X509Certificate cert() {
return cert;
}

public static SelfSignedCaCertificate create() throws Exception {
return create(
keyGen.generateKeyPair(), DEFAULT_ISSUER_FQDN, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
}

public static SelfSignedCaCertificate create(String fqdn) throws Exception {
return create(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
}

public static SelfSignedCaCertificate create(String fqdn, Date from, Date to) throws Exception {
return create(keyGen.generateKeyPair(), fqdn, from, to);
}

public static SelfSignedCaCertificate create(KeyPair keyPair, String fqdn, Date from, Date to)
throws Exception {
return new SelfSignedCaCertificate(keyPair.getPrivate(), createCaCert(keyPair, fqdn, from, to));
}

static KeyPairGenerator createKeyPairGenerator() {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", PROVIDER);
keyGen.initialize(2048, new SecureRandom());
return keyGen;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/** Returns a self-signed Certificate Authority (CA) certificate. */
static X509Certificate createCaCert(KeyPair keyPair, String fqdn, Date from, Date to)
throws Exception {
X500Name owner = new X500Name("CN=" + fqdn);
ContentSigner signer =
new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate());
X509v3CertificateBuilder builder =
new JcaX509v3CertificateBuilder(
owner, new BigInteger(64, RANDOM), from, to, owner, keyPair.getPublic());

// Mark cert as CA by adding basicConstraint with cA=true to the builder
BasicConstraints basicConstraints = new BasicConstraints(true);
builder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints);

X509CertificateHolder certHolder = builder.build(signer);
return new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder);
}
}
@@ -21,6 +21,7 @@
import static google.registry.networking.handler.SslInitializerTestUtils.verifySslExcpetion;

import com.google.common.collect.ImmutableList;
import google.registry.networking.util.SelfSignedCaCertificate;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
@@ -35,7 +36,6 @@
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.CertPathBuilderException;
@@ -153,7 +153,7 @@ public void testSuccess_nullPort() {

@Test
public void testFailure_defaultTrustManager_rejectSelfSignedCert() throws Exception {
SelfSignedCertificate ssc = new SelfSignedCertificate(SSL_HOST);
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST);
LocalAddress localAddress =
new LocalAddress("DEFAULT_TRUST_MANAGER_REJECT_SELF_SIGNED_CERT_" + sslProvider);
nettyRule.setUpServer(localAddress, getServerHandler(false, ssc.key(), ssc.cert()));
@@ -177,7 +177,7 @@ public void testSuccess_customTrustManager_acceptCertSignedByTrustedCa() throws
KeyPair keyPair = getKeyPair();

// Generate a self signed certificate, and use it to sign the key pair.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
X509Certificate cert = signKeyPair(ssc, keyPair, SSL_HOST);

// Set up the server to use the signed cert and private key to perform handshake;
@@ -206,7 +206,7 @@ public void testFailure_customTrustManager_serverCertExpired() throws Exception
KeyPair keyPair = getKeyPair();

// Generate a self signed certificate, and use it to sign the key pair.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
X509Certificate cert =
signKeyPair(
ssc,
@@ -240,7 +240,7 @@ public void testFailure_customTrustManager_serverCertNotYetValid() throws Except
KeyPair keyPair = getKeyPair();

// Generate a self signed certificate, and use it to sign the key pair.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
X509Certificate cert =
signKeyPair(
ssc,
@@ -272,8 +272,8 @@ public void testSuccess_customTrustManager_acceptSelfSignedCert_clientCertRequir
new LocalAddress(
"CUSTOM_TRUST_MANAGER_ACCEPT_SELF_SIGNED_CERT_CLIENT_CERT_REQUIRED_" + sslProvider);

SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
SelfSignedCertificate clientSsc = new SelfSignedCertificate();
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();

// Set up the server to require client certificate.
nettyRule.setUpServer(localAddress, getServerHandler(true, serverSsc.key(), serverSsc.cert()));
@@ -311,7 +311,7 @@ public void testFailure_customTrustManager_wrongHostnameInCertificate() throws E
KeyPair keyPair = getKeyPair();

// Generate a self signed certificate, and use it to sign the key pair.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
X509Certificate cert = signKeyPair(ssc, keyPair, "wrong.com");

// Set up the server to use the signed cert and private key to perform handshake;
@@ -18,50 +18,52 @@
import static org.junit.Assert.assertThrows;

import com.google.common.base.Throwables;
import google.registry.networking.util.SelfSignedCaCertificate;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import javax.net.ssl.SSLSession;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

/**
* Utility class that provides methods used by {@link SslClientInitializerTest} and {@link
* SslServerInitializerTest}.
*/
public final class SslInitializerTestUtils {

static {
Security.addProvider(new BouncyCastleProvider());
}
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
private static final KeyPairGenerator KEY_PAIR_GENERATOR = getKeyPairGenerator();

private SslInitializerTestUtils() {}

private static KeyPairGenerator getKeyPairGenerator() {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", PROVIDER);
keyPairGenerator.initialize(2048, new SecureRandom());
return keyPairGenerator;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public static KeyPair getKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(2048, new SecureRandom());
return keyPairGenerator.generateKeyPair();
return KEY_PAIR_GENERATOR.generateKeyPair();
}

/**
@@ -71,26 +73,20 @@ public static KeyPair getKeyPair() throws Exception {
* @return signed public key (of the key pair) certificate
*/
public static X509Certificate signKeyPair(
SelfSignedCertificate ssc, KeyPair keyPair, String hostname, Date from, Date to)
SelfSignedCaCertificate ssc, KeyPair keyPair, String hostname, Date from, Date to)
throws Exception {
X500Name subjectDnName = new X500Name("CN=" + hostname);
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
X500Name issuerDnName = new X500Name(ssc.cert().getIssuerDN().getName());
SubjectPublicKeyInfo subPubKeyInfo =
SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
AlgorithmIdentifier sigAlgId =
new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSAEncryption");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);

ContentSigner sigGen =
new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
.build(PrivateKeyFactory.createKey(ssc.key().getEncoded()));
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(ssc.key());
X509v3CertificateBuilder v3CertGen =
new X509v3CertificateBuilder(
issuerDnName, serialNumber, from, to, subjectDnName, subPubKeyInfo);
new JcaX509v3CertificateBuilder(
issuerDnName, serialNumber, from, to, subjectDnName, keyPair.getPublic());

X509CertificateHolder certificateHolder = v3CertGen.build(sigGen);
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder);
return new JcaX509CertificateConverter()
.setProvider(PROVIDER)
.getCertificate(certificateHolder);
}

/**
@@ -100,7 +96,7 @@ public static X509Certificate signKeyPair(
* @return signed public key (of the key pair) certificate
*/
public static X509Certificate signKeyPair(
SelfSignedCertificate ssc, KeyPair keyPair, String hostname) throws Exception {
SelfSignedCaCertificate ssc, KeyPair keyPair, String hostname) throws Exception {
return signKeyPair(
ssc,
keyPair,

0 comments on commit 736f788

Please sign in to comment.
You can’t perform that action at this time.