blob: 3e466bcdc73b3bfdb0bb109f2a01d33c1cb513bb [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* 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 libcore.javax.net.ssl;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.security.Principal;
import java.security.SecureRandom;
import libcore.java.security.StandardNames;
import libcore.java.security.TestKeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
import junit.framework.Assert;
/**
* TestSSLContext is a convenience class for other tests that
* want a canned SSLContext and related state for testing so they
* don't have to duplicate the logic.
*/
public final class TestSSLContext extends Assert {
/*
* The RI and Android have very different default SSLSession cache behaviors.
* The RI keeps an unlimited number of SSLSesions around for 1 day.
* Android keeps 10 SSLSessions forever.
*/
private static final boolean IS_RI = StandardNames.IS_RI;
public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10;
public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100;
public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT = (IS_RI) ? 86400 : 0;
/**
* The Android SSLSocket and SSLServerSocket implementations are
* based on a version of OpenSSL which includes support for RFC
* 4507 session tickets. When using session tickets, the server
* does not need to keep a cache mapping session IDs to SSL
* sessions for reuse. Instead, the client presents the server
* with a session ticket it received from the server earlier,
* which is an SSL session encrypted by the server's secret
* key. Since in this case the server does not need to keep a
* cache, some tests may find different results depending on
* whether or not the session tickets are in use. These tests can
* use this function to determine if loopback SSL connections are
* expected to use session tickets and conditionalize their
* results appropriately.
*/
public static boolean sslServerSocketSupportsSessionTickets () {
// Disabled session tickets for better compatability b/2682876
// return !IS_RI;
return false;
}
public final KeyStore clientKeyStore;
public final char[] clientStorePassword;
public final KeyStore serverKeyStore;
public final char[] serverStorePassword;
public final X509ExtendedKeyManager clientKeyManager;
public final X509ExtendedKeyManager serverKeyManager;
public final X509TrustManager clientTrustManager;
public final X509TrustManager serverTrustManager;
public final SSLContext clientContext;
public final SSLContext serverContext;
public final SSLServerSocket serverSocket;
public final InetAddress host;
public final int port;
private TestSSLContext(KeyStore clientKeyStore,
char[] clientStorePassword,
KeyStore serverKeyStore,
char[] serverStorePassword,
X509ExtendedKeyManager clientKeyManager,
X509ExtendedKeyManager serverKeyManager,
X509TrustManager clientTrustManager,
X509TrustManager serverTrustManager,
SSLContext clientContext,
SSLContext serverContext,
SSLServerSocket serverSocket,
InetAddress host,
int port) {
this.clientKeyStore = clientKeyStore;
this.clientStorePassword = clientStorePassword;
this.serverKeyStore = serverKeyStore;
this.serverStorePassword = serverStorePassword;
this.clientKeyManager = clientKeyManager;
this.serverKeyManager = serverKeyManager;
this.clientTrustManager = clientTrustManager;
this.serverTrustManager = serverTrustManager;
this.clientContext = clientContext;
this.serverContext = serverContext;
this.serverSocket = serverSocket;
this.host = host;
this.port = port;
}
/**
* Usual TestSSLContext creation method, creates underlying
* SSLContext with certificate and key as well as SSLServerSocket
* listening provided host and port.
*/
public static TestSSLContext create() {
return create(TestKeyStore.getClient(),
TestKeyStore.getServer());
}
/**
* TestSSLContext creation method that allows separate creation of server key store
*/
public static TestSSLContext create(TestKeyStore client, TestKeyStore server) {
String provider = StandardNames.JSSE_PROVIDER_NAME;
return create(client, server, provider, provider);
}
public static TestSSLContext create(TestKeyStore client, TestKeyStore server,
String clientProvider, String serverProvider) {
String protocol = "TLS";
SSLContext clientContext = createSSLContext(protocol, clientProvider,
client.keyManagers, client.trustManagers);
SSLContext serverContext = createSSLContext(protocol, serverProvider,
server.keyManagers, server.trustManagers);
return create(client.keyStore, client.storePassword,
server.keyStore, server.storePassword,
client.keyManagers[0],
server.keyManagers[0],
client.trustManagers[0],
server.trustManagers[0],
clientContext,
serverContext);
}
/**
* TestSSLContext creation method that allows separate creation of client and server key store
*/
public static TestSSLContext create(KeyStore clientKeyStore, char[] clientStorePassword,
KeyStore serverKeyStore, char[] serverStorePassword,
KeyManager clientKeyManagers,
KeyManager serverKeyManagers,
TrustManager clientTrustManagers,
TrustManager serverTrustManagers,
SSLContext clientContext,
SSLContext serverContext) {
try {
SSLServerSocket serverSocket = (SSLServerSocket)
serverContext.getServerSocketFactory().createServerSocket(0);
InetSocketAddress sa = (InetSocketAddress) serverSocket.getLocalSocketAddress();
InetAddress host = sa.getAddress();
int port = sa.getPort();
return new TestSSLContext(clientKeyStore, clientStorePassword,
serverKeyStore, serverStorePassword,
(X509ExtendedKeyManager) clientKeyManagers,
(X509ExtendedKeyManager) serverKeyManagers,
(X509TrustManager) clientTrustManagers,
(X509TrustManager) serverTrustManagers,
clientContext, serverContext,
serverSocket, host, port);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Create a SSLContext with a KeyManager using the private key and
* certificate chain from the given KeyStore and a TrustManager
* using the certificates authorities from the same KeyStore.
*/
public static final SSLContext createSSLContext(final String protocol,
final String provider,
final KeyManager[] keyManagers,
final TrustManager[] trustManagers)
{
try {
SSLContext context = SSLContext.getInstance(protocol, provider);
context.init(keyManagers, trustManagers, new SecureRandom());
return context;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void assertCertificateInKeyStore(Principal principal,
KeyStore keyStore) throws Exception {
String subjectName = principal.getName();
boolean found = false;
for (String alias: Collections.list(keyStore.aliases())) {
if (!keyStore.isCertificateEntry(alias)) {
continue;
}
X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias);
if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) {
found = true;
break;
}
}
assertTrue(found);
}
public static void assertCertificateInKeyStore(Certificate certificate,
KeyStore keyStore) throws Exception {
boolean found = false;
for (String alias: Collections.list(keyStore.aliases())) {
if (!keyStore.isCertificateEntry(alias)) {
continue;
}
Certificate keyStoreCertificate = keyStore.getCertificate(alias);
if (certificate.equals(keyStoreCertificate)) {
found = true;
break;
}
}
assertTrue(found);
}
public static void assertServerCertificateChain(X509TrustManager trustManager,
Certificate[] serverChain)
throws CertificateException {
X509Certificate[] chain = (X509Certificate[]) serverChain;
trustManager.checkServerTrusted(chain, chain[0].getPublicKey().getAlgorithm());
}
public static void assertClientCertificateChain(X509TrustManager trustManager,
Certificate[] clientChain)
throws CertificateException {
X509Certificate[] chain = (X509Certificate[]) clientChain;
trustManager.checkClientTrusted(chain, chain[0].getPublicKey().getAlgorithm());
}
}