You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Apache Wiki <wi...@apache.org> on 2015/10/13 23:18:13 UTC

[Tomcat Wiki] Update of "tools/SSLTest.java" by ChristopherSchultz

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tomcat Wiki" for change notification.

The "tools/SSLTest.java" page has been changed by ChristopherSchultz:
https://wiki.apache.org/tomcat/tools/SSLTest.java

New page:
{{{
/*
 * SSLTest.java
 *
 * Tests servers for SSL/TLS protocol and cipher support.
 *
 * Copyright (c) 2015 Christopher Schultz
 *
 * Christopher Schultz 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.
 */
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

/**
 * A driver class to test a server's SSL/TLS support.
 * 
 * Usage: java SSLTest [opts] host[:port]
 * 
 * Try "java SSLTest -h" for help.
 * 
 * This tester will attempts to handshake with the target host with all
 * available protocols and ciphers and report which ones were accepted and
 * which were rejected. An HTTP connection is never fully made, so these
 * connections should not flood the host's access log with entries.
 * 
 * @author Christopher Schultz
 */
public class SSLTest
{
    public static void usage()
    {
        System.out.println("Usage: java " + SSLTest.class + " [opts] host[:port]");
        System.out.println();
        System.out.println("-sslprotocol                 Sets the SSL/TLS protocol to be used (e.g. SSL, TLS, SSLv3, TLSv1.2, etc.)");
        System.out.println("-enabledprotocols protocols  Sets individual SSL/TLS ptotocols that should be enabled");
        System.out.println("-ciphers cipherspec          A comma-separated list of SSL/TLS ciphers");

        System.out.println("-truststore                  Sets the trust store for connections");
        System.out.println("-truststoretype type         Sets the type for the trust store");
        System.out.println("-truststorepassword pass     Sets the password for the trust store");
        System.out.println("-truststorealgorithm alg     Sets the algorithm for the trust store");
        System.out.println("-truststoreprovider provider Sets the crypto provider for the trust store");

        System.out.println("-no-check-certificate        Ignores certificate errors");
        System.out.println("-no-verify-hostname          Ignores hostname mismatches");

        System.out.println("-h -help --help     Shows this help message");
    }

    public static void main(String[] args)
        throws Exception
    {
        int connectTimeout = 0; // default = infinite
        int readTimeout = 1000;

        boolean disableHostnameVerification = true;
        boolean disableCertificateChecking = true;

        String trustStoreFilename = System.getProperty("javax.net.ssl.trustStore");
        String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
        String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType");
        String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider");
        String trustStoreAlgorithm = null;
        String sslProtocol = "TLS";
        String[] sslEnabledProtocols = new String[] { "SSLv2", "SSLv2hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" };
        String[] sslCipherSuites = null; // Default = default for protocol
        String crlFilename = null;
        boolean showCerts = false;

        if(args.length < 1)
        {
            usage();
            System.exit(0);
        }

        int argIndex;
        for(argIndex = 0; argIndex < args.length; ++argIndex)
        {
            String arg = args[argIndex];

            if(!arg.startsWith("-"))
                break;
            else if("--".equals(arg))
                break;
            else if("-no-check-certificate".equals(arg))
                disableCertificateChecking = true;
            else if("-no-verify-hostname".equals(arg))
                disableHostnameVerification = true;
            else if("-sslprotocol".equals(arg))
                sslProtocol = args[++argIndex];
            else if("-enabledprotocols".equals(arg))
                sslEnabledProtocols = args[++argIndex].split("\\s*,\\s*");
            else if("-ciphers".equals(arg))
                sslCipherSuites = args[++argIndex].split("\\s*,\\s*");
            else if("-connecttimeout".equals(arg))
                connectTimeout = Integer.parseInt(args[++argIndex]);
            else if("-readtimeout".equals(arg))
                readTimeout = Integer.parseInt(args[++argIndex]);
            else if("-truststore".equals(arg))
                trustStoreFilename = args[++argIndex];
            else if("-truststoretype".equals(arg))
                trustStoreType = args[++argIndex];
            else if("-truststorepassword".equals(arg))
                trustStorePassword = args[++argIndex];
            else if("-truststoreprovider".equals(arg))
                trustStoreProvider = args[++argIndex];
            else if("-truststorealgorithm".equals(arg))
                trustStoreAlgorithm = args[++argIndex];
            else if("-showcerts".equals(arg))
                showCerts = true;
            else if("--help".equals(arg)
                    || "-h".equals(arg)
                    || "-help".equals(arg))
            {
                usage();
                System.exit(0);
            }
            else
            {
                System.err.println("Unrecognized option: " + arg);
                System.exit(1);
            }
        }
        
        if(argIndex >= args.length)
        {
            System.err.println("Unexpected additional arguments: "
                               + java.util.Arrays.asList(args).subList(argIndex, args.length));

            usage();
            System.exit(1);
        }

        if(disableHostnameVerification)
            SSLUtils.disableSSLHostnameVerification();

        TrustManager[] trustManagers;
        if(disableCertificateChecking
           || "true".equalsIgnoreCase(System.getProperty("disable.ssl.cert.checks")))
        {
            trustManagers = SSLUtils.getTrustAllCertsTrustManagers();
        }
        else if(null != trustStoreFilename)
        {
            if(null == trustStoreType)
                trustStoreType = "JKS";

            trustManagers = SSLUtils.getTrustManagers(trustStoreFilename, trustStorePassword, trustStoreType, trustStoreProvider, trustStoreAlgorithm, null, crlFilename);            
        }
        else
            trustManagers = null;

        int port = 443;
        String host = args[argIndex];

        int pos = host.indexOf(':');
        if(pos > 0)
        {
            port = Integer.parseInt(host.substring(pos + 1));
            host = host.substring(0, pos);
        }
        
        System.out.println("Testing server " + host + ":" + port);

        SecureRandom rand = new SecureRandom();

        String reportFormat = "%9s %8s %s\n";
        System.out.print(String.format(reportFormat, "Supported", "Protocol", "Cipher"));

        InetSocketAddress address = new InetSocketAddress(host, port);

        ArrayList<String> supportedProtocols = new ArrayList<String>(Arrays.asList(sslEnabledProtocols));

        for(String protocol : sslEnabledProtocols)
        {
            SSLContext sc;
            try
            {
                sc = SSLContext.getInstance(protocol);
            }
            catch (NoSuchAlgorithmException nsae)
            {
                System.out.print(String.format(reportFormat, "-----", protocol, " Not supported by client"));
                supportedProtocols.remove(protocol);
                continue;
            }
            catch (Exception e)
            {
                e.printStackTrace();
                continue; // Skip this protocol
            }

            sc.init(null, null, rand);

            // Restrict cipher suites to those specified by sslCipherSuites
            HashSet<String> cipherSuites = new HashSet<String>();
            cipherSuites.addAll(Arrays.asList(sc.getSocketFactory().getSupportedCipherSuites()));
            if(null != sslCipherSuites)
                cipherSuites.retainAll(Arrays.asList(sslCipherSuites));

            if(cipherSuites.isEmpty())
            {
                System.err.println("No overlapping cipher suites found for protocol " + protocol);
                supportedProtocols.remove(protocol);
                continue; // Go to the next protocol
            }

            for(String cipherSuite : cipherSuites)
            {
                String status;

                SSLSocketFactory sf = SSLUtils.getSSLSocketFactory(protocol,
                                                                   new String[] { protocol },
                                                                   new String[] { cipherSuite },
                                                                   rand,
                                                                   trustManagers);

                Socket sock = null;

                try
                {
                    //
                    // Note: SSLSocketFactory has several create() methods.
                    // Those that take arguments all connect immediately
                    // and have no options for specifying a connection timeout.
                    //
                    // So, we have to create a socket and connect it (with a
                    // connection timeout), then have the SSLSocketFactory wrap
                    // the already-connected socket.
                    //
                    sock = new Socket();
                    sock.setSoTimeout(readTimeout);
                    sock.connect(address, connectTimeout);

                    // Wrap plain socket in an SSL socket
                    SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
                    socket.startHandshake();

                    assert protocol.equals(socket.getSession().getProtocol());
                    assert cipherSuite.equals(socket.getSession().getCipherSuite());

                    status = "Accepted";
                }
                catch (SocketTimeoutException ste)
                {
                    status = "Failed";
                }
                catch (IOException ioe)
                {
                    // System.out.println(ioe);
                    status = "Rejected";
                }
                catch (Exception e)
                {
                    System.out.print(e.getMessage());
                    status = "Rejected";
                }
                finally
                {
                    if(null != sock) try { sock.close(); }
                    catch (IOException ioe) { ioe.printStackTrace(); }
                }
                System.out.print(String.format(reportFormat,
                                               status,
                                               protocol,
                                               cipherSuite));

            }
        }

        if(supportedProtocols.isEmpty())
        {
            System.err.println("No protocols ");
        }
        // Now get generic and allow the server to decide on the protocol and cipher suite
        String[] protocolsToTry = supportedProtocols.toArray(new String[supportedProtocols.size()]);

        SSLSocketFactory sf = SSLUtils.getSSLSocketFactory(sslProtocol,
                                                           protocolsToTry,
                                                           sslCipherSuites,
                                                           rand,
                                                           trustManagers);

        Socket sock = null;

        try
        {
            //
            // Note: SSLSocketFactory has several create() methods.
            // Those that take arguments all connect immediately
            // and have no options for specifying a connection timeout.
            //
            // So, we have to create a socket and connect it (with a
            // connection timeout), then have the SSLSocketFactory wrap
            // the already-connected socket.
            //
            sock = new Socket();
            sock.connect(address, connectTimeout);
            sock.setSoTimeout(readTimeout);

            // Wrap plain socket in an SSL socket
            SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
            socket.startHandshake();

            System.out.print("Given this client's capabilities ("
                             + supportedProtocols
                             + "), the server prefers protocol=");
            System.out.print(socket.getSession().getProtocol());
            System.out.print(", cipher=");
            System.out.println(socket.getSession().getCipherSuite());
            
            if(showCerts)
            {
                for(Certificate cert : socket.getSession().getPeerCertificates())
                {
                    System.out.println("Certificate: " + cert.getType());
                    if("X.509".equals(cert.getType()))
                    {
                        X509Certificate x509 = (X509Certificate)cert;
                        System.out.println("Subject: " + x509.getSubjectDN());
                        System.out.println("Issuer: " + x509.getIssuerDN());
                        System.out.println("Serial: " + x509.getSerialNumber());
//                        System.out.println("Signature: " + toHexString(x509.getSignature()));
//                        System.out.println("cert bytes: " + toHexString(cert.getEncoded()));
//                        System.out.println("cert bytes: " + cert.getPublicKey());
                    }
                    else
                    {
                        System.out.println("Unknown certificate type (" + cert.getType() + "): " + cert);
                    }
                }
            }
        }
        finally
        {
            if (null != sock) try { sock.close(); }
            catch (IOException ioe) { ioe.printStackTrace(); }
        }
    }
    static final char[] hexChars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f' };
    static String toHexString(byte[] bytes)
    {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        
        for(byte b : bytes)
            sb.append(hexChars[(b >> 4) & 0x0f])
              .append(hexChars[b & 0x0f]);
        
        return sb.toString();
    }
}
}}}

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org