xds: SdsTrustManagerFactory implementation - uses SdsX509TrustManager (#6267)

This commit is contained in:
sanjaypujare 2019-10-16 14:35:49 -07:00 committed by GitHub
parent 9eca2dcc4f
commit 4fc41bd0e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 247 additions and 0 deletions

View File

@ -0,0 +1,115 @@
/*
* Copyright 2019 The gRPC Authors
*
* 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 io.grpc.xds.sds.trust;
import static com.google.common.base.Preconditions.checkNotNull;
import io.envoyproxy.envoy.api.v2.auth.CertificateValidationContext;
import io.grpc.Internal;
import io.netty.handler.ssl.util.SimpleTrustManagerFactory;
import java.io.File;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
/**
* Factory class used by providers of {@link io.grpc.xds.sds.TlsContextManager} to provide a {@link
* SdsX509TrustManager} for trust and SAN checks.
*/
@Internal
public final class SdsTrustManagerFactory extends SimpleTrustManagerFactory {
private static final Logger logger = Logger.getLogger(SdsTrustManagerFactory.class.getName());
private SdsX509TrustManager sdsX509TrustManager;
/** Constructor that loads certs from a file. */
public SdsTrustManagerFactory(
String certsFile, @Nullable CertificateValidationContext certContext)
throws CertificateException, IOException, CertStoreException {
this(CertificateUtils.toX509Certificates(new File(certsFile)), certContext);
}
/** Constructor that takes array of certs. */
public SdsTrustManagerFactory(
X509Certificate[] certs, @Nullable CertificateValidationContext certContext)
throws CertStoreException {
checkNotNull(certs, "certs");
createSdsX509TrustManager(certs, certContext);
}
private void createSdsX509TrustManager(
X509Certificate[] certs, CertificateValidationContext certContext) throws CertStoreException {
TrustManagerFactory tmf = null;
try {
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("PKCS12");
// perform a load to initialize KeyStore
ks.load(/* stream= */ null, /* password= */ null);
int i = 1;
for (X509Certificate cert : certs) {
// note: alias lookup uses toLowerCase(Locale.ENGLISH)
// so our alias needs to be all lower-case and unique
ks.setCertificateEntry("alias" + Integer.toString(i), cert);
i++;
}
tmf.init(ks);
} catch (NoSuchAlgorithmException | KeyStoreException | IOException | CertificateException e) {
logger.log(Level.SEVERE, "createSdsX509TrustManager", e);
throw new CertStoreException(e);
}
TrustManager[] tms = tmf.getTrustManagers();
X509ExtendedTrustManager myDelegate = null;
if (tms != null) {
for (TrustManager tm : tms) {
if (tm instanceof X509ExtendedTrustManager) {
myDelegate = (X509ExtendedTrustManager) tm;
break;
}
}
}
if (myDelegate == null) {
throw new CertStoreException("Native X509 TrustManager not found.");
}
sdsX509TrustManager = new SdsX509TrustManager(certContext, myDelegate);
}
@Override
protected void engineInit(KeyStore keyStore) throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected TrustManager[] engineGetTrustManagers() {
return new TrustManager[] {sdsX509TrustManager};
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2019 The gRPC Authors
*
* 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 io.grpc.xds.sds.trust;
import static com.google.common.truth.Truth.assertThat;
import io.grpc.internal.testing.TestUtils;
import java.io.IOException;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.TrustManager;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link SdsTrustManagerFactory}. */
@RunWith(JUnit4.class)
public class SdsTrustManagerFactoryTest {
/** Trust store cert. */
private static final String CA_PEM_FILE = "ca.pem";
/** server cert. */
private static final String SERVER_1_PEM_FILE = "server1.pem";
/** client cert. */
private static final String CLIENT_PEM_FILE = "client.pem";
/** bad server cert. */
private static final String BAD_SERVER_PEM_FILE = "badserver.pem";
/** bad client cert. */
private static final String BAD_CLIENT_PEM_FILE = "badclient.pem";
@Test
public void constructor_fromFile() throws CertificateException, IOException, CertStoreException {
SdsTrustManagerFactory factory =
new SdsTrustManagerFactory(
TestUtils.loadCert(CA_PEM_FILE).getAbsolutePath(), /* certContext= */ null);
assertThat(factory).isNotNull();
TrustManager[] tms = factory.getTrustManagers();
assertThat(tms).isNotNull();
assertThat(tms).hasLength(1);
TrustManager myTm = tms[0];
assertThat(myTm).isInstanceOf(SdsX509TrustManager.class);
SdsX509TrustManager sdsX509TrustManager = (SdsX509TrustManager) myTm;
X509Certificate[] acceptedIssuers = sdsX509TrustManager.getAcceptedIssuers();
assertThat(acceptedIssuers).isNotNull();
assertThat(acceptedIssuers).hasLength(1);
X509Certificate caCert = acceptedIssuers[0];
assertThat(caCert)
.isEqualTo(CertificateUtils.toX509Certificates(TestUtils.loadCert(CA_PEM_FILE))[0]);
}
@Test
public void checkServerTrusted_goodCert()
throws CertificateException, IOException, CertStoreException {
SdsTrustManagerFactory factory =
new SdsTrustManagerFactory(
TestUtils.loadCert(CA_PEM_FILE).getAbsolutePath(), /* certContext= */ null);
SdsX509TrustManager sdsX509TrustManager = (SdsX509TrustManager) factory.getTrustManagers()[0];
X509Certificate[] serverChain =
CertificateUtils.toX509Certificates(TestUtils.loadCert(SERVER_1_PEM_FILE));
sdsX509TrustManager.checkServerTrusted(serverChain, "RSA");
}
@Test
public void checkClientTrusted_goodCert()
throws CertificateException, IOException, CertStoreException {
SdsTrustManagerFactory factory =
new SdsTrustManagerFactory(
TestUtils.loadCert(CA_PEM_FILE).getAbsolutePath(), /* certContext= */ null);
SdsX509TrustManager sdsX509TrustManager = (SdsX509TrustManager) factory.getTrustManagers()[0];
X509Certificate[] clientChain =
CertificateUtils.toX509Certificates(TestUtils.loadCert(CLIENT_PEM_FILE));
sdsX509TrustManager.checkClientTrusted(clientChain, "RSA");
}
@Test
public void checkServerTrusted_badCert_throwsException()
throws CertificateException, IOException, CertStoreException {
SdsTrustManagerFactory factory =
new SdsTrustManagerFactory(
TestUtils.loadCert(CA_PEM_FILE).getAbsolutePath(), /* certContext= */ null);
SdsX509TrustManager sdsX509TrustManager = (SdsX509TrustManager) factory.getTrustManagers()[0];
X509Certificate[] serverChain =
CertificateUtils.toX509Certificates(TestUtils.loadCert(BAD_SERVER_PEM_FILE));
try {
sdsX509TrustManager.checkServerTrusted(serverChain, "RSA");
Assert.fail("no exception thrown");
} catch (CertificateException expected) {
assertThat(expected)
.hasMessageThat()
.contains("unable to find valid certification path to requested target");
}
}
@Test
public void checkClientTrusted_badCert_throwsException()
throws CertificateException, IOException, CertStoreException {
SdsTrustManagerFactory factory =
new SdsTrustManagerFactory(
TestUtils.loadCert(CA_PEM_FILE).getAbsolutePath(), /* certContext= */ null);
SdsX509TrustManager sdsX509TrustManager = (SdsX509TrustManager) factory.getTrustManagers()[0];
X509Certificate[] clientChain =
CertificateUtils.toX509Certificates(TestUtils.loadCert(BAD_CLIENT_PEM_FILE));
try {
sdsX509TrustManager.checkClientTrusted(clientChain, "RSA");
Assert.fail("no exception thrown");
} catch (CertificateException expected) {
assertThat(expected)
.hasMessageThat()
.contains("unable to find valid certification path to requested target");
}
}
}