Java equivalent to OpenSSL s_client command

前端 未结 3 968
我寻月下人不归
我寻月下人不归 2021-01-17 14:06

I have a requirement to convert certain bash scripts to java and one such script connects to a server using openssl with a vanit

3条回答
  •  无人及你
    2021-01-17 14:29

    Without really knowing what SNI was I tried to get some insight with the test-program shown below.

    I don't know the output from the openssl s_client command, but the test-program might prove to be a starting point. When the javax.net.debug output is turned on a lot of output is dumped of which only a few lines are relevant (see also the comments). That is a bit annoying and I do not have an easy solution for that. The TrustAllServers class can be reworked to inspect the certificates you expect to receive from the server (a.ka. host) for a particular domain. There might be other options (e.g. the socket's handshake methods) but this is as far as I got.

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    import java.nio.charset.StandardCharsets;
    import java.security.KeyStore;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.util.Arrays;
    
    import javax.net.ssl.SNIHostName;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLEngine;
    import javax.net.ssl.SSLParameters;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.TrustManagerFactory;
    import javax.net.ssl.X509ExtendedTrustManager;
    
    // https://stackoverflow.com/questions/56005883/java-equivalent-to-openssl-s-client-command
    // Please use latest Java 8 version, bugs are around in earlier versions.
    public class ServerNameTest {
    
        public static void main(String[] args) {
    
            // SSL debug options, see https://stackoverflow.com/q/23659564/3080094 and https://access.redhat.com/solutions/973783
            // System.setProperty("javax.net.debug", "all");
            // System.setProperty("javax.net.debug", "ssl:handshake");
            // System.setProperty("jsse.enableSNIExtension", "true"); // "true" is the default
            try {
                ServerNameTest sn = new ServerNameTest();
                // This will show 2 different server certificate chains.
                // Note this is a random server - please pick your own one.
                sn.test("major.io", "rackerhacker.com");
                sn.test("major.io", "major.io");
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("Done");
        }
    
        /*
         * With javax.net.debug output you should see something like:
         * 
         * *** ClientHello
         * ...
         * Extension server_name, server_name: [type=host_name (0), value=DOMAIN;]
         * ...
         * *** ServerHello
         * ...
         * Extension server_name, server_name: 
         * ...
         * 
    * Note that if the server does not provide a value for server_name, * it does not actually mean the server does not support SNI/server_name (see https://serverfault.com/a/506303) */ void test(String host, String domain) throws Exception { SSLParameters sslParams = new SSLParameters(); if (domain != null && !domain.isEmpty()) { sslParams.setServerNames(Arrays.asList(new SNIHostName(domain))); } // Only for webservers: set endpoint algorithm to HTTPS sslParams.setEndpointIdentificationAlgorithm("HTTPS"); SSLSocketFactory sslsf = serverTrustingSSLFactory(); try (SSLSocket socket = (SSLSocket) sslsf.createSocket()) { socket.setSSLParameters(sslParams); socket.setSoTimeout(3_000); System.out.println("Connecting to " + host + " for domain " + domain); socket.connect(new InetSocketAddress(host, 443), 3_000); // Trigger actual connection by getting the session. socket.getSession(); System.out.println("Connected to remote " + socket.getRemoteSocketAddress()); try (BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) { try (OutputStream out = socket.getOutputStream()) { System.out.println(">> OPTIONS"); out.write("OPTIONS * HTTP/1.1\r\n\r\n".getBytes(StandardCharsets.UTF_8)); System.out.println("<< " + input.readLine()); } } catch (Exception e) { System.err.println("No line read: " + e); } } } SSLSocketFactory serverTrustingSSLFactory() throws Exception { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, trustManager(), null); return ctx.getSocketFactory(); } TrustManager[] trustManager() throws Exception { TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init( (KeyStore) null); // Must use "extended" type versus the default javax.net.ssl.X509TrustManager, // otherwise the error "No subject alternative DNS name matching" keeps showing up. X509ExtendedTrustManager defaultManager = null; for (TrustManager trustManager : tmf.getTrustManagers()) { if (trustManager instanceof X509ExtendedTrustManager) { defaultManager = (X509ExtendedTrustManager) trustManager; break; } } if (defaultManager == null) { throw new RuntimeException("Cannot find default X509ExtendedTrustManager"); } return new TrustManager[] { new TrustAllServers(defaultManager) }; } static void printChain(X509Certificate[] chain) { try { for (int i = 0; i < chain.length; i++) { X509Certificate cert = chain[i]; System.out.println("Cert[" + i + "] " + cert.getSubjectX500Principal() + " :alt: " + cert.getSubjectAlternativeNames()); } } catch (Exception e) { e.printStackTrace(); } } static class TrustAllServers extends X509ExtendedTrustManager { final X509ExtendedTrustManager defaultManager; public TrustAllServers(X509ExtendedTrustManager defaultManager) { this.defaultManager = defaultManager; } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { defaultManager.checkServerTrusted(chain, authType); } catch (Exception e) { System.err.println("Untrusted server: " + e); } printChain(chain); } public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { try { defaultManager.checkServerTrusted(chain, authType, socket); } catch (Exception e) { System.err.println("Untrusted server for socket: " + e); } printChain(chain); } public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { try { defaultManager.checkServerTrusted(chain, authType, engine); } catch (Exception e) { System.err.println("Untrusted server for engine: " + e); } printChain(chain); } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { defaultManager.checkClientTrusted(chain, authType); } public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { defaultManager.checkClientTrusted(chain, authType, socket); } public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { defaultManager.checkClientTrusted(chain, authType, engine); } public X509Certificate[] getAcceptedIssuers() { return defaultManager.getAcceptedIssuers(); } } }

提交回复
热议问题