Path: blob/master/test/jdk/javax/net/ssl/sanity/interop/CipherTest.java
41154 views
/*1* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.*;24import java.net.*;25import java.util.*;26import java.util.concurrent.*;2728import java.security.*;29import java.security.cert.*;3031import javax.net.ssl.*;3233/**34* Test that all ciphersuites work in all versions and all client35* authentication types. The way this is setup the server is stateless and36* all checking is done on the client side.37*38* The test is multithreaded to speed it up, especially on multiprocessor39* machines. To simplify debugging, run with -DnumThreads=1.40*41* @author Andreas Sterbenz42*/43public class CipherTest {4445// use any available port for the server socket46static int serverPort = 0;4748final int THREADS;4950// assume that if we do not read anything for 20 seconds, something51// has gone wrong52final static int TIMEOUT = 20 * 1000;5354static KeyStore trustStore, keyStore;55static X509ExtendedKeyManager keyManager;56static X509TrustManager trustManager;57static SecureRandom secureRandom;5859private static PeerFactory peerFactory;6061static abstract class Server implements Runnable {6263final CipherTest cipherTest;6465Server(CipherTest cipherTest) throws Exception {66this.cipherTest = cipherTest;67}6869public abstract void run();7071void handleRequest(InputStream in, OutputStream out) throws IOException {72boolean newline = false;73StringBuilder sb = new StringBuilder();74while (true) {75int ch = in.read();76if (ch < 0) {77throw new EOFException();78}79sb.append((char)ch);80if (ch == '\r') {81// empty82} else if (ch == '\n') {83if (newline) {84// 2nd newline in a row, end of request85break;86}87newline = true;88} else {89newline = false;90}91}92String request = sb.toString();93if (request.startsWith("GET / HTTP/1.") == false) {94throw new IOException("Invalid request: " + request);95}96out.write("HTTP/1.0 200 OK\r\n\r\n".getBytes());97}9899}100101public static class TestParameters {102103CipherSuite cipherSuite;104Protocol protocol;105String clientAuth;106107TestParameters(CipherSuite cipherSuite, Protocol protocol,108String clientAuth) {109this.cipherSuite = cipherSuite;110this.protocol = protocol;111this.clientAuth = clientAuth;112}113114boolean isEnabled() {115return cipherSuite.supportedByProtocol(protocol);116}117118public String toString() {119String s = cipherSuite + " in " + protocol + " mode";120if (clientAuth != null) {121s += " with " + clientAuth + " client authentication";122}123return s;124}125}126127private List<TestParameters> tests;128private Iterator<TestParameters> testIterator;129private SSLSocketFactory factory;130private boolean failed;131132private CipherTest(PeerFactory peerFactory) throws IOException {133THREADS = Integer.parseInt(System.getProperty("numThreads", "4"));134factory = (SSLSocketFactory)SSLSocketFactory.getDefault();135SSLSocket socket = (SSLSocket)factory.createSocket();136String[] cipherSuites = socket.getSupportedCipherSuites();137String[] protocols = socket.getSupportedProtocols();138String[] clientAuths = {null, "RSA", "DSA"};139tests = new ArrayList<TestParameters>(140cipherSuites.length * protocols.length * clientAuths.length);141for (int j = 0; j < protocols.length; j++) {142String protocol = protocols[j];143if (protocol.equals(Protocol.SSLV2HELLO.name)) {144System.out.println("Skipping SSLv2Hello protocol");145continue;146}147148for (int i = 0; i < cipherSuites.length; i++) {149String cipherSuite = cipherSuites[i];150151// skip kerberos cipher suites and TLS_EMPTY_RENEGOTIATION_INFO_SCSV152if (cipherSuite.startsWith("TLS_KRB5") || cipherSuite.equals(153CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.name())) {154System.out.println("Skipping unsupported test for " +155cipherSuite + " of " + protocol);156continue;157}158159if (!peerFactory.isSupported(cipherSuite, protocol)) {160continue;161}162163for (int k = 0; k < clientAuths.length; k++) {164String clientAuth = clientAuths[k];165// no client with anonymous cipher suites;166// TLS 1.3 doesn't support DSA167if ((clientAuth != null && cipherSuite.contains("DH_anon"))168|| ("DSA".equals(clientAuth) && "TLSv1.3".equals(protocol))) {169continue;170}171tests.add(new TestParameters(172CipherSuite.cipherSuite(cipherSuite),173Protocol.protocol(protocol),174clientAuth));175}176}177}178testIterator = tests.iterator();179}180181synchronized void setFailed() {182failed = true;183}184185public void run() throws Exception {186Thread[] threads = new Thread[THREADS];187for (int i = 0; i < THREADS; i++) {188try {189threads[i] = new Thread(peerFactory.newClient(this),190"Client " + i);191} catch (Exception e) {192e.printStackTrace();193return;194}195threads[i].start();196}197try {198for (int i = 0; i < THREADS; i++) {199threads[i].join();200}201} catch (InterruptedException e) {202setFailed();203e.printStackTrace();204}205if (failed) {206throw new Exception("*** Test '" + peerFactory.getName() +207"' failed ***");208} else {209System.out.println("Test '" + peerFactory.getName() +210"' completed successfully");211}212}213214synchronized TestParameters getTest() {215if (failed) {216return null;217}218if (testIterator.hasNext()) {219return (TestParameters)testIterator.next();220}221return null;222}223224SSLSocketFactory getFactory() {225return factory;226}227228static abstract class Client implements Runnable {229230final CipherTest cipherTest;231232Client(CipherTest cipherTest) throws Exception {233this.cipherTest = cipherTest;234}235236public final void run() {237while (true) {238TestParameters params = cipherTest.getTest();239if (params == null) {240// no more tests241break;242}243if (!params.isEnabled()) {244System.out.println("Skipping disabled test " + params);245continue;246}247try {248runTest(params);249System.out.println("Passed " + params);250} catch (Exception e) {251cipherTest.setFailed();252System.out.println("** Failed " + params + "**");253e.printStackTrace();254}255}256}257258abstract void runTest(TestParameters params) throws Exception;259260void sendRequest(InputStream in, OutputStream out) throws IOException {261out.write("GET / HTTP/1.0\r\n\r\n".getBytes());262out.flush();263StringBuilder sb = new StringBuilder();264while (true) {265int ch = in.read();266if (ch < 0) {267break;268}269sb.append((char)ch);270}271String response = sb.toString();272if (response.startsWith("HTTP/1.0 200 ") == false) {273throw new IOException("Invalid response: " + response);274}275}276277}278279// for some reason, ${test.src} has a different value when the280// test is called from the script and when it is called directly...281static String pathToStores = "../../etc";282static String pathToStoresSH = ".";283static String keyStoreFile = "keystore";284static String trustStoreFile = "truststore";285static char[] passwd = "passphrase".toCharArray();286287static File PATH;288289private static KeyStore readKeyStore(String name) throws Exception {290File file = new File(PATH, name);291InputStream in = new FileInputStream(file);292KeyStore ks = KeyStore.getInstance("JKS");293ks.load(in, passwd);294in.close();295return ks;296}297298public static void main(PeerFactory peerFactory, String[] args)299throws Exception {300long time = System.currentTimeMillis();301String relPath;302if ((args != null) && (args.length > 0) && args[0].equals("sh")) {303relPath = pathToStoresSH;304} else {305relPath = pathToStores;306}307PATH = new File(System.getProperty("test.src", "."), relPath);308CipherTest.peerFactory = peerFactory;309System.out.println(310"Initializing test '" + peerFactory.getName() + "'...");311secureRandom = new SecureRandom();312secureRandom.nextInt();313trustStore = readKeyStore(trustStoreFile);314keyStore = readKeyStore(keyStoreFile);315KeyManagerFactory keyFactory =316KeyManagerFactory.getInstance(317KeyManagerFactory.getDefaultAlgorithm());318keyFactory.init(keyStore, passwd);319keyManager = (X509ExtendedKeyManager)keyFactory.getKeyManagers()[0];320trustManager = new AlwaysTrustManager();321322CipherTest cipherTest = new CipherTest(peerFactory);323Thread serverThread = new Thread(peerFactory.newServer(cipherTest),324"Server");325serverThread.setDaemon(true);326serverThread.start();327System.out.println("Done");328cipherTest.run();329time = System.currentTimeMillis() - time;330System.out.println("Done. (" + time + " ms)");331}332333static abstract class PeerFactory {334335abstract String getName();336337abstract Client newClient(CipherTest cipherTest) throws Exception;338339abstract Server newServer(CipherTest cipherTest) throws Exception;340341boolean isSupported(String cipherSuite, String protocol) {342// ignore exportable cipher suite for TLSv1.1343if (protocol.equals("TLSv1.1")344&& (cipherSuite.indexOf("_EXPORT_WITH") != -1)) {345System.out.println("Skipping obsoleted test for " +346cipherSuite + " of " + protocol);347return false;348}349350// ignore obsoleted cipher suite for the specified protocol351// TODO352353// ignore unsupported cipher suite for the specified protocol354// TODO355356return true;357}358}359360}361362// we currently don't do any chain verification. we assume that works ok363// and we can speed up the test. we could also just add a plain certificate364// chain comparision with our trusted certificates.365class AlwaysTrustManager implements X509TrustManager {366367public AlwaysTrustManager() {368369}370371public void checkClientTrusted(X509Certificate[] chain, String authType)372throws CertificateException {373// empty374}375376public void checkServerTrusted(X509Certificate[] chain, String authType)377throws CertificateException {378// empty379}380381public X509Certificate[] getAcceptedIssuers() {382return new X509Certificate[0];383}384}385386class MyX509KeyManager extends X509ExtendedKeyManager {387388private final X509ExtendedKeyManager keyManager;389private String authType;390391MyX509KeyManager(X509ExtendedKeyManager keyManager) {392this.keyManager = keyManager;393}394395void setAuthType(String authType) {396this.authType = authType;397}398399public String[] getClientAliases(String keyType, Principal[] issuers) {400if (authType == null) {401return null;402}403return keyManager.getClientAliases(authType, issuers);404}405406public String chooseClientAlias(String[] keyType, Principal[] issuers,407Socket socket) {408if (authType == null) {409return null;410}411return keyManager.chooseClientAlias(new String[] {authType},412issuers, socket);413}414415public String chooseEngineClientAlias(String[] keyType,416Principal[] issuers, SSLEngine engine) {417if (authType == null) {418return null;419}420return keyManager.chooseEngineClientAlias(new String[] {authType},421issuers, engine);422}423424public String[] getServerAliases(String keyType, Principal[] issuers) {425throw new UnsupportedOperationException("Servers not supported");426}427428public String chooseServerAlias(String keyType, Principal[] issuers,429Socket socket) {430throw new UnsupportedOperationException("Servers not supported");431}432433public String chooseEngineServerAlias(String keyType, Principal[] issuers,434SSLEngine engine) {435throw new UnsupportedOperationException("Servers not supported");436}437438public X509Certificate[] getCertificateChain(String alias) {439return keyManager.getCertificateChain(alias);440}441442public PrivateKey getPrivateKey(String alias) {443return keyManager.getPrivateKey(alias);444}445446}447448class DaemonThreadFactory implements ThreadFactory {449450final static ThreadFactory INSTANCE = new DaemonThreadFactory();451452private final static ThreadFactory DEFAULT = Executors.defaultThreadFactory();453454public Thread newThread(Runnable r) {455Thread t = DEFAULT.newThread(r);456t.setDaemon(true);457return t;458}459460}461462463