Using distinct aliases when building the trust manager factory, and renamed trustCertChain into trustCertCollection.

Motivation:

SSLContext.buildTrustManagerFactory(...) builds a KeyStore to
initialize the TrustManagerFactory from an array of X509Certificates,
assuming that array is a chain and that each certificate will have a
unique Subject Distinguised Name.
However, the collection of certificates used as trust anchors is generally
not a chain (it is an unordered collection), and it is legitimate for it
to contain multiple certificates with the same Subject DN.
The existing code uses the Subject DN as the alias name when filling in
the `KeyStore`, thereby overwriting other certificates with the same
Subject DN in this collection, so some certificates may be discarded.
In addition, the code related to building trust managers can take an array of
X509Certificate instances to use as trust anchors. The variable name is
usually trustCertChain, and the documentation refers to them as a "chain".
However, while it makes sense to talk about a "chain" from a keymanager
point of view, these certificates are just an unordered collection in a
trust manager. (There is no chaining requirement, having the Subject DN
matching its predecessor's Issuer DN.)
This can create confusion to for users not used with PKI concepts.

Modifications:

SSLContext.buildTrustManagerFactory(...) now uses a distinct alias for each
array (simply using a counter, since this name is never used for reference
later). This patch also includes a unit test with CA certificates using the
same Subject DN.
Also renamed trustCertChain into trustCertCollection, and changed the
references to "chain" in the Javadoc.

Result:

Each loaded certificate now has a unique identifier when loaded, so it is
now possible to use multiple certificates with the same Subject DN as
trust anchors.
Hopefully, renaming the parameter should also reduce confusion around PKI
concepts.
This commit is contained in:
Bruno Harbulot 2016-03-13 16:34:25 +00:00 committed by Norman Maurer
parent 881ff3cd98
commit 9ebb4b7164
13 changed files with 359 additions and 85 deletions

View File

@ -171,11 +171,12 @@ public final class JdkSslClientContext extends JdkSslContext {
/**
* Creates a new instance.
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}
* @param keyCertChainFile an X.509 certificate chain file in PEM format.
* This provides the public key for mutual authentication.
* {@code null} to use the system default
@ -200,21 +201,22 @@ public final class JdkSslClientContext extends JdkSslContext {
* @deprecated use {@link SslContextBuilder}
*/
@Deprecated
public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
public JdkSslClientContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
this(trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
this(trustCertCollectionFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
ciphers, cipherFilter, toNegotiator(apn, false), sessionCacheSize, sessionTimeout);
}
/**
* Creates a new instance.
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}
* @param keyCertChainFile an X.509 certificate chain file in PEM format.
* This provides the public key for mutual authentication.
* {@code null} to use the system default
@ -239,13 +241,13 @@ public final class JdkSslClientContext extends JdkSslContext {
* @deprecated use {@link SslContextBuilder}
*/
@Deprecated
public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
public JdkSslClientContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn, ClientAuth.NONE);
try {
ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory,
ctx = newSSLContext(toX509Certificates(trustCertCollectionFile), trustManagerFactory,
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout);
} catch (Exception e) {
@ -256,22 +258,22 @@ public final class JdkSslClientContext extends JdkSslContext {
}
}
JdkSslClientContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
JdkSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, toNegotiator(apn, false), ClientAuth.NONE);
ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
ctx = newSSLContext(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, sessionCacheSize, sessionTimeout);
}
private static SSLContext newSSLContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, long sessionCacheSize,
long sessionTimeout) throws SSLException {
private static SSLContext newSSLContext(X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
long sessionCacheSize, long sessionTimeout) throws SSLException {
try {
if (trustCertChain != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory);
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
}
if (keyCertChain != null) {
keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory);

View File

@ -142,12 +142,13 @@ public final class JdkSslServerContext extends JdkSslContext {
/**
* Creates a new instance.
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* This provides the certificate chains used for mutual authentication.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* This provides the certificate collection used for mutual authentication.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from clients.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}.
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}.
* @param keyCertChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
@ -168,22 +169,23 @@ public final class JdkSslServerContext extends JdkSslContext {
* @deprecated use {@link SslContextBuilder}
*/
@Deprecated
public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
public JdkSslServerContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
this(trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
this(trustCertCollectionFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
ciphers, cipherFilter, toNegotiator(apn, true), sessionCacheSize, sessionTimeout);
}
/**
* Creates a new instance.
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* This provides the certificate chains used for mutual authentication.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* This provides the certificate collection used for mutual authentication.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from clients.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}
* @param keyCertChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
@ -204,13 +206,13 @@ public final class JdkSslServerContext extends JdkSslContext {
* @deprecated use {@link SslContextBuilder}
*/
@Deprecated
public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
public JdkSslServerContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn, ClientAuth.NONE);
try {
ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory,
ctx = newSSLContext(toX509Certificates(trustCertCollectionFile), trustManagerFactory,
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout);
} catch (Exception e) {
@ -221,27 +223,28 @@ public final class JdkSslServerContext extends JdkSslContext {
}
}
JdkSslServerContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
JdkSslServerContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout,
ClientAuth clientAuth) throws SSLException {
super(ciphers, cipherFilter, toNegotiator(apn, true), clientAuth);
ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key,
ctx = newSSLContext(trustCertCollection, trustManagerFactory, keyCertChain, key,
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout);
}
private static SSLContext newSSLContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, long sessionCacheSize, long sessionTimeout)
private static SSLContext newSSLContext(X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
long sessionCacheSize, long sessionTimeout)
throws SSLException {
if (key == null && keyManagerFactory == null) {
throw new NullPointerException("key, keyManagerFactory");
}
try {
if (trustCertChain != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory);
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
}
if (key != null) {
keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory);

View File

@ -136,11 +136,12 @@ public final class OpenSslClientContext extends OpenSslContext {
/**
* Creates a new instance.
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}
* @param keyCertChainFile an X.509 certificate chain file in PEM format.
* This provides the public key for mutual authentication.
* {@code null} to use the system default
@ -165,19 +166,19 @@ public final class OpenSslClientContext extends OpenSslContext {
* @deprecated use {@link SslContextBuilder}
*/
@Deprecated
public OpenSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
public OpenSslClientContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout)
throws SSLException {
this(toX509CertificatesInternal(trustCertChainFile), trustManagerFactory,
this(toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory,
toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
}
@SuppressWarnings("deprecation")
OpenSslClientContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
OpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
@ -237,8 +238,8 @@ public final class OpenSslClientContext extends OpenSslContext {
SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH);
try {
if (trustCertChain != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory);
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
} else if (trustManagerFactory == null) {
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());

View File

@ -195,12 +195,13 @@ public final class OpenSslServerContext extends OpenSslContext {
/**
* Creates a new instance.
*
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* This provides the certificate chains used for mutual authentication.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* This provides the certificate collection used for mutual authentication.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from clients.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}.
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}.
* @param keyCertChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
@ -222,11 +223,11 @@ public final class OpenSslServerContext extends OpenSslContext {
*/
@Deprecated
public OpenSslServerContext(
File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config,
long sessionCacheSize, long sessionTimeout) throws SSLException {
this(trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
this(trustCertCollectionFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
ciphers, cipherFilter, toNegotiator(config), sessionCacheSize, sessionTimeout);
}
@ -286,12 +287,13 @@ public final class OpenSslServerContext extends OpenSslContext {
* Creates a new instance.
*
*
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* This provides the certificate chains used for mutual authentication.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* This provides the certificate collection used for mutual authentication.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from clients.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}.
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}.
* @param keyCertChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
@ -313,28 +315,28 @@ public final class OpenSslServerContext extends OpenSslContext {
*/
@Deprecated
public OpenSslServerContext(
File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
this(toX509CertificatesInternal(trustCertChainFile), trustManagerFactory,
this(toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory,
toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter,
apn, sessionCacheSize, sessionTimeout, ClientAuth.NONE);
}
OpenSslServerContext(
X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth) throws SSLException {
this(trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers,
this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers,
cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth);
}
@SuppressWarnings("deprecation")
private OpenSslServerContext(
X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth) throws SSLException {
@ -402,8 +404,8 @@ public final class OpenSslServerContext extends OpenSslContext {
}
}
try {
if (trustCertChain != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory);
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
} else if (trustManagerFactory == null) {
// Mimic the way SSLContext.getInstance(KeyManager[], null, null) works
trustManagerFactory = TrustManagerFactory.getInstance(

View File

@ -39,7 +39,7 @@ import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -338,12 +338,13 @@ public abstract class SslContext {
* Creates a new server-side {@link SslContext}.
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* This provides the certificate chains used for mutual authentication.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* This provides the certificate collection used for mutual authentication.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from clients.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}.
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}.
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
* @param keyCertChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
@ -369,12 +370,12 @@ public abstract class SslContext {
@Deprecated
public static SslContext newServerContext(
SslProvider provider,
File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
try {
return newServerContextInternal(provider, toX509Certificates(trustCertChainFile), trustManagerFactory,
return newServerContextInternal(provider, toX509Certificates(trustCertCollectionFile), trustManagerFactory,
toX509Certificates(keyCertChainFile),
toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter, apn,
@ -389,7 +390,7 @@ public abstract class SslContext {
static SslContext newServerContextInternal(
SslProvider provider,
X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout,
@ -402,12 +403,12 @@ public abstract class SslContext {
switch (provider) {
case JDK:
return new JdkSslServerContext(
trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth);
case OPENSSL:
return new OpenSslServerContext(
trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth);
default:
@ -673,11 +674,12 @@ public abstract class SslContext {
* Creates a new client-side {@link SslContext}.
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default or the results of parsing {@code trustCertChainFile}.
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}.
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
* @param keyCertChainFile an X.509 certificate chain file in PEM format.
* This provides the public key for mutual authentication.
@ -708,12 +710,12 @@ public abstract class SslContext {
@Deprecated
public static SslContext newClientContext(
SslProvider provider,
File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
try {
return newClientContextInternal(provider, toX509Certificates(trustCertChainFile), trustManagerFactory,
return newClientContextInternal(provider, toX509Certificates(trustCertCollectionFile), trustManagerFactory,
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter,
apn,
@ -996,14 +998,16 @@ public abstract class SslContext {
}
static TrustManagerFactory buildTrustManagerFactory(
X509Certificate[] certChain, TrustManagerFactory trustManagerFactory)
X509Certificate[] certCollection, TrustManagerFactory trustManagerFactory)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
for (X509Certificate cert: certChain) {
X500Principal principal = cert.getSubjectX500Principal();
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
int i = 1;
for (X509Certificate cert: certCollection) {
String alias = Integer.toString(i);
ks.setCertificateEntry(alias, cert);
i++;
}
// Set up trust manager factory to use our key store.

View File

@ -125,7 +125,7 @@ public final class SslContextBuilder {
private final boolean forServer;
private SslProvider provider;
private X509Certificate[] trustCertChain;
private X509Certificate[] trustCertCollection;
private TrustManagerFactory trustManagerFactory;
private X509Certificate[] keyCertChain;
private PrivateKey key;
@ -152,23 +152,24 @@ public final class SslContextBuilder {
/**
* Trusted certificates for verifying the remote endpoint's certificate. The file should
* contain an X.509 certificate chain in PEM format. {@code null} uses the system default.
* contain an X.509 certificate collection in PEM format. {@code null} uses the system default.
*/
public SslContextBuilder trustManager(File trustCertChainFile) {
public SslContextBuilder trustManager(File trustCertCollectionFile) {
try {
return trustManager(SslContext.toX509Certificates(trustCertChainFile));
return trustManager(SslContext.toX509Certificates(trustCertCollectionFile));
} catch (Exception e) {
throw new IllegalArgumentException("File does not contain valid certificates: " + trustCertChainFile, e);
throw new IllegalArgumentException("File does not contain valid certificates: "
+ trustCertCollectionFile, e);
}
}
/**
* Trusted certificates for verifying the remote endpoint's certificate. The input stream should
* contain an X.509 certificate chain in PEM format. {@code null} uses the system default.
* contain an X.509 certificate collection in PEM format. {@code null} uses the system default.
*/
public SslContextBuilder trustManager(InputStream trustCertChainInputStream) {
public SslContextBuilder trustManager(InputStream trustCertCollectionInputStream) {
try {
return trustManager(SslContext.toX509Certificates(trustCertChainInputStream));
return trustManager(SslContext.toX509Certificates(trustCertCollectionInputStream));
} catch (Exception e) {
throw new IllegalArgumentException("Input stream does not contain valid certificates.", e);
}
@ -177,8 +178,8 @@ public final class SslContextBuilder {
/**
* Trusted certificates for verifying the remote endpoint's certificate, {@code null} uses the system default.
*/
public SslContextBuilder trustManager(X509Certificate... trustCertChain) {
this.trustCertChain = trustCertChain != null ? trustCertChain.clone() : null;
public SslContextBuilder trustManager(X509Certificate... trustCertCollection) {
this.trustCertCollection = trustCertCollection != null ? trustCertCollection.clone() : null;
trustManagerFactory = null;
return this;
}
@ -189,7 +190,7 @@ public final class SslContextBuilder {
* you must use {@link #trustManager(File)}. {@code null} uses the system default.
*/
public SslContextBuilder trustManager(TrustManagerFactory trustManagerFactory) {
trustCertChain = null;
trustCertCollection = null;
this.trustManagerFactory = trustManagerFactory;
return this;
}
@ -387,11 +388,11 @@ public final class SslContextBuilder {
*/
public SslContext build() throws SSLException {
if (forServer) {
return SslContext.newServerContextInternal(provider, trustCertChain,
return SslContext.newServerContextInternal(provider, trustCertCollection,
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth);
} else {
return SslContext.newClientContextInternal(provider, trustCertChain,
return SslContext.newClientContextInternal(provider, trustCertCollection,
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
}

View File

@ -0,0 +1,147 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you 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.netty.handler.ssl;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertNotNull;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.junit.Test;
public class SslContextTrustManagerTest {
@Test
public void testUsingAllCAs() throws Exception {
runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_1b.pem",
"tm_test_ca_2.pem" }, new String[] { "tm_test_eec_1.pem",
"tm_test_eec_2.pem", "tm_test_eec_3.pem" }, new boolean[] {
true, true, true });
}
@Test
public void testUsingAllCAsWithDuplicates() throws Exception {
runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_1b.pem",
"tm_test_ca_2.pem", "tm_test_ca_2.pem" },
new String[] { "tm_test_eec_1.pem", "tm_test_eec_2.pem",
"tm_test_eec_3.pem" },
new boolean[] { true, true, true });
}
@Test
public void testUsingCAsOneAandB() throws Exception {
runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_1b.pem", },
new String[] { "tm_test_eec_1.pem", "tm_test_eec_2.pem",
"tm_test_eec_3.pem" }, new boolean[] { true, true,
false });
}
@Test
public void testUsingCAsOneAandTwo() throws Exception {
runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_2.pem" },
new String[] { "tm_test_eec_1.pem", "tm_test_eec_2.pem",
"tm_test_eec_3.pem" }, new boolean[] { true, false,
true });
}
/**
*
* @param caResources
* an array of paths to CA Certificates in PEM format to load
* from the classpath (relative to this class).
* @param eecResources
* an array of paths to Server Certificates in PEM format in to
* load from the classpath (relative to this class).
* @param expectations
* an array of expecting results for each EEC Server Certificate
* (the array is expected to have the same length the previous
* argument, and be arrange in matching order: true means
* expected to be valid, false otherwise.
*/
private static void runTests(String[] caResources, String[] eecResources,
boolean[] expectations) throws Exception {
X509TrustManager tm = getTrustManager(caResources);
X509Certificate[] eecCerts = loadCertCollection(eecResources);
for (int i = 0; i < eecResources.length; i++) {
X509Certificate eecCert = eecCerts[i];
assertNotNull("Cannot use cert " + eecResources[i], eecCert);
try {
tm.checkServerTrusted(new X509Certificate[] { eecCert }, "RSA");
if (!expectations[i]) {
fail(String.format(
"Certificate %s was expected not to be valid when using CAs %s, but its "
+ "verification passed.", eecResources[i],
Arrays.asList(caResources)));
}
} catch (CertificateException e) {
if (expectations[i]) {
fail(String.format(
"Certificate %s was expected to be valid when using CAs %s, but its "
+ "verification failed.", eecResources[i],
Arrays.asList(caResources)));
}
}
}
}
private static X509TrustManager getTrustManager(String[] resourceNames)
throws Exception {
X509Certificate[] certCollection = loadCertCollection(resourceNames);
TrustManagerFactory tmf = SslContext.buildTrustManagerFactory(
certCollection, null);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
throw new Exception(
"Unable to find any X509TrustManager from this factory.");
}
private static X509Certificate[] loadCertCollection(String[] resourceNames)
throws Exception {
CertificateFactory certFactory = CertificateFactory
.getInstance("X.509");
X509Certificate[] certCollection = new X509Certificate[resourceNames.length];
for (int i = 0; i < resourceNames.length; i++) {
String resourceName = resourceNames[i];
InputStream is = null;
try {
is = SslContextTest.class.getResourceAsStream(resourceName);
assertNotNull("Cannot find " + resourceName, is);
certCollection[i] = (X509Certificate) certFactory
.generateCertificate(is);
} finally {
if (is != null) {
is.close();
}
}
}
return certCollection;
}
}

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/TCCAeWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDEwxuZXR0
eS50ZXN0LjEwIBcNMTYwMzIyMTIwMDAwWhgPMjExNjAzMjIxMjAwMDBaMBcxFTAT
BgNVBAMTDG5ldHR5LnRlc3QuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBALWMMCP4QWYWJNt+fNqpwLNM9/LkJlS3NtzJl1chvnyHpxt8OFSD8/cYSl6z
MrbgRYyGNuaL3lsKIL5p2ZnUYzcR61niAhjuMXQgM6ZkptlIsgK6426OTALOSN6l
HukItWDDL/om0Mnc8zMuLL/kIpfnzYOKMseUf/1R1MftzlNSSAMPQ7Rn8So/3nUG
j42NywEInoONv89UZ4L+xPpyJwrp0k/u19ckwhFWdudw7l2lVo6s5aBJW9CK8v/f
uUxC75eUYiQ57suKhXCy1Vf8T4vVDiEjKxa3whD1QxlxRxZNYdHJA6tEQIhiWjCC
RiDZZcaAcCCD0evE/0l5V9nnRc8CAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUvdqINGhE1D1xZi9Q8NyR+G+5bLwwCwYDVR0PBAQDAgEGMBEGCWCG
SAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQsFAAOCAQEAR8gHn7MJp6cNwMR6qF3e
jU3tAzCshZVM03NyoHvMpcHsILlR0g/q2KTjcgHzpMMo5PrUGf3oR6ad4JFr5els
kstgbCe4Vv/XzEC6faTEuhLolHGMyzr3Pd6k/wJSsMktF7Ob+YjsyZbgQbyhXqJV
UDQDDncIwxl5rdsRwfiltLUOle4702b4hSCb/1NsDsvsuZQVfeAHHzT1aS8XDSwK
bHOgrDgQGhVR6rBTH9WhcRgFY9rKQ4vVjhoNbwWweQvHmQSO8xYNUhtQnxVOeB7B
NzBM+kx5nw7oIqPCYT0hBINNqeoac9Bidfl4UoTB5YjsQFse4BNuBDPFowAXq5ZB
fg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/TCCAeWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDEwxuZXR0
eS50ZXN0LjEwIBcNMTYwMzIyMTMwMDAwWhgPMjExNjAzMjIxMzAwMDBaMBcxFTAT
BgNVBAMTDG5ldHR5LnRlc3QuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBANEUVJUwvWr+qyS28W2EMiDq5frQwforMED7Q8wMiMod+LFxy7y04p12zYWW
35iqC3RaQUQ31DOknAxc7H8vfr0vdl87BIsxc27Ud9h+Do0ggktCaz9Te8/q2Yxo
4TQ8QEFJ8x37zPB05LVqF4djim4GE/yaj0WFMuaRaZLUFvGbHTL7ilC2l6p9SuYx
y40cCucP5nNAXGNhnVYsJCPa/LkyIDLGbkvMMARorkbr7zfaYI2D1YfedwmCOEo1
CkfBm2qL9/+ig/8VFrTRPlYzUWHsyPvCzfL9F/69NxRCdVk7XCyMEgmf/ztyy/7k
3iZeBhQ0z+RXiNLqBkK3RbaMD7ECAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQU2vr0yImPHyJv84PXeoSJYbI12uEwCwYDVR0PBAQDAgEGMBEGCWCG
SAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQsFAAOCAQEAT8mmZi/4dzozqa76tgxM
8ZQgw2C+WgetC81CKqIN3F3tFtu2KEsaAsXEpOVvDR278bLk+r3H3d47Or0xn053
grk6kdI4C9IPHP7IDaNmAskZ5u9Hrl25P1fxMKG6hXwrk2Je7gD8aNP5IkOSKulo
e9b3XSW53WdtHZ+b98LKVMO0lRLQsiG1EmNrL0kJwMXuPxq5s0Ljqz/L19iWGupk
kybRWPcmjHnWIOnnYTwFswI/h79/afvwW5xUP4HgcU/nKrNDWveE7lSYq66zcvpt
rBCESrr3gvETNTJKCPN4u41EOJKGGgoN4U9fBopU4DfzIrcwZ5a4eFLsAFEqAB23
3A==
-----END CERTIFICATE-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/TCCAeWgAwIBAgIBATANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0
eS50ZXN0LjIwIBcNMTYwMzIyMTIwMDAwWhgPMjExNjAzMjIxMjAwMDBaMBcxFTAT
BgNVBAMTDG5ldHR5LnRlc3QuMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMFTCqEUaMvZPwV8PFHgjpcqlrVfIadY+B2DAsYBQp6HirRv2STYdBue9bHS
Ya8n6J99Qcp0jct692MeOh9BhWoX7wOvi8Tckiu+LAMo7vBHAyUSamJ5qKyNvZNW
3Uwrng1LFwOo6uhY6N6vqyv5CIoDGv+afOlnZOS7484ZYvmYEPejbIPLZpj7IP8Z
c2xmi7TOj781uO2rwUzZgqGSEKsYZNMhVp7yZrsJ3el9T2+2Dma1aYt2w/grOW1t
pzxWvXqjkrjbNjJamAxyiy3qyQ2iDhpFYz8ONqSxqN/QRE72q2N8e+QtajceCFz7
lpMWRqr7Z6Fdh5zUS7yJrCksJh8CAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUnk0/s/aeR4sjFcubiyB/QMWryj8wCwYDVR0PBAQDAgEGMBEGCWCG
SAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEAWu+RyHkL3lNl8dLlVuDa
A/Vakxf/xbd8+qFEIox2nLvSYZ3OkLzE+vUip/KP0JyQmmmzaz3sx5eZXx33gw9Z
rRYW3I0/c2QPjT5xNYnITUoX5z17FKd71lMr/bz8uhaF9Do+ZV84HgORwtmOCwNg
bOIIVtHO6Ht3V2RmLcQgUV4dK3neNJHa75/Wi3OkJNEZqbzcJX2r69BqupoLte8j
FxqkLBmwUruuCVl5gUFoXsxT3+qgWMxNweLSxEmbqkQ54g8W+06PTHMM/BpbsApv
Ce5mKeC8lHvbV3CxaOYp8w5xJPJbEt/vK6w8jrN47Tz6LaQcimDMdJVVM2H5zubG
RA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDADCCAeigAwIBAgIBAjANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0
eS50ZXN0LjEwIBcNMTYwMzIyMTQwMDAwWhgPMjExNjAzMjIxMjAwMDBaMB0xGzAZ
BgNVBAMTEm5ldHR5LnRlc3Quc2VydmVyMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBALEytHncQ4iJ0d3nbLcRhe3wWmHzeqr9JKvAMSnIGw2Oc+nUK3FP
xMjdYnY3CSw6BYjK5zJsSF2UWU/jycqc5caQTEXVUQJDjQENScHHsWB+jEevd1i7
HDmd4Ykw/bqRRRP/I6npukpwJkPuqJ7LAsDfA7FuHJY15ZlhDod5+1zleWv2tdDK
fRCvODi8ehujyNqBqNeRL2YF4wt4yqviFbDNy5+JreyfzPvv1iujSprN5JJCRJ1C
7AABrHYYMQXuEYb10t9dS908Zg2B3sDUa969F1RnU86bCppwK1otQr/RO2hUqqdq
IItb9FHRkeko81OvUiM6nvLzzJLBOInyAzMCAwEAAaNPME0wDAYDVR0TAQH/BAIw
ADAdBgNVHQ4EFgQUt5uJ55JYS1Qw2YE5OWOhgv6RxUAwCwYDVR0PBAQDAgXgMBEG
CWCGSAGG+EIBAQQEAwIGQDANBgkqhkiG9w0BAQUFAAOCAQEAUS9HzI9VXyZiaGM6
RwpUEDgUhbAeI7i7xsdJgqlbvKrTQQy+MKIbxDgsyoz2buqwdX7ekvykTmo0pltS
ASr36gTTW4dwRtiecn/HutrnyuJIckbvMZzld5xIdNERqLHnoiRAopVhe1Fc5UFd
YGEOd+685X2fuc9PMy3G8JjQAOftYOx21JaaNumyVVLcyvciGK0Ptwh/q+6hf4+h
XUHHtIzjnPAM9vkcCmHttVbl3uvare7TfeAoU82NODz0sUaOrIwG8dQbmEdrafHa
JHXti1wv+9ZEEiYKcecvnB3q4e0MT3atf3qedw4B9ZkzoniHEOhFpZgQg6UVA7/f
ga0mCg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDADCCAeigAwIBAgIBAjANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0
eS50ZXN0LjEwIBcNMTYwMzIyMTQwMDAwWhgPMjExNjAzMjIxMzAwMDBaMB0xGzAZ
BgNVBAMTEm5ldHR5LnRlc3Quc2VydmVyMjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMFf61lsJ5Tpcg6ux5lbZhPEzvLi117aZ42figORTeZg0fMX8a3W
XfRUHldTYD6CugTIpgheZDzsYfwrSLyK5jMyQl332rCoozj2dXdi+HY/JQ5gw8DY
odeQCjCWS3V6HpHTu8tqgNquqjygSLQHSsh+oCVUa/5IWA1N+zZY5/XARnyajwS0
8Nrgok9kd//jR7hIwHm40YThrTawRetooDK/MuFxJb6FX6UPwZ5/9g2UZMUaKzIa
hrGfjAQmRxzkRyACbVqYv+wjBMSCq2SqYZ2Fq3nKeW+dvpPeFfHGOyF/F8kIqtpa
BRawXPHKaUoQYn0PDU+RRfZjfkWTQuz/OMcCAwEAAaNPME0wDAYDVR0TAQH/BAIw
ADAdBgNVHQ4EFgQUIUVnoWei3itUqTyFxPczCYsvW4EwCwYDVR0PBAQDAgXgMBEG
CWCGSAGG+EIBAQQEAwIGQDANBgkqhkiG9w0BAQUFAAOCAQEAfK+YlGqBVExATkGF
1ZIcJZtvaiX8rGH8mwqj1wPvKjPRCHvNpPTDLNGhHrFu/0sJlZQDz6hDn0NpJpD8
TffF+jqmBfvGQW1MEd+jyfp5IXHwR0ZejJepQIeGYuMwyrlZXUKnXvQR2QDkLyx+
rxmO58XWLNoFUkM4guts3Jb7oAgfCbzYnmBELMVhI8v+SQhuZamvL6S5Wdb18O9i
/N/zH/KDwJmIVtWo7D8UOAMeq69s9zYZLKkqwt8o+DSXth0YPZcNcU8IouDzEJ14
C35My7Ll7vFehgetXq9D7cMYltx2VPKKYOeT5ZzI580ZvtryT8yCTBj4GoSvAzb6
RHwFRw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDADCCAeigAwIBAgIBAjANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0
eS50ZXN0LjIwIBcNMTYwMzIyMTUwMDAwWhgPMjExNjAzMjIxMjAwMDBaMB0xGzAZ
BgNVBAMTEm5ldHR5LnRlc3Quc2VydmVyMzCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBALGdif7vCqwoWg001wmw2X3HnF/9cjZGTY6BWiex3cIarO+aby6L
QObv7lXzzh3r7bJc1JquaawKUek6CIt2mNM+KLAIscScNUOCYg4T1XOD0rh3Qrin
V8Q+hY8InSrUqN4cuX95YOdJoOddw3mDXs376fcNBU10raP4L7k7EsyvnQqIKyNP
ysU9PpDoDLPCMBEGB8cDASv1bkopvvH9u5sic1OHtTLVllsbGNnG6Y9B+1ysG1UO
iJFAX+teigPXKVokJ1+a8dt1CkzDd+iTK1j6PrY+TXc4XOhP/cSnbxwq6JDzkkSb
3pMTJK8ypi7DQNkDbPx73A8qbjRT32gJz4ECAwEAAaNPME0wDAYDVR0TAQH/BAIw
ADAdBgNVHQ4EFgQUCIQ1MChHCo6/1mI6B7S6QPPvEZcwCwYDVR0PBAQDAgXgMBEG
CWCGSAGG+EIBAQQEAwIGQDANBgkqhkiG9w0BAQUFAAOCAQEAZas05SOIsNFmKUYY
kyR1ctlgaA7OZwSzeRPh6vZJ4YaT2lVhNPUeO84tf3LqKE8B827FzWH9mcO/2zeJ
6PTR+QYls/wg8VR881V0Xb5KVNGfwTYpmfhH9+JSzKvKiEtlOoHyvYBMdUon7LJL
ojvlragwXm4QA246345+md5C8PEyQYQf/AoZVZWeLL/BRXZ2ZjsuIT+LzpMIXuTW
AKoH7IlFbKQ5tccQDGCzZb6V1txRDFlKZ/5bvFQZqo12n0MeJy2WPjrmeRm2NC+9
imP9oR9GIGNyGKTT1h1qjnaZZwK24cx/82eb63qQKUx80pD4DYW9EDU6/tULz5gs
Kw0iig==
-----END CERTIFICATE-----