Java equivalent to OpenSSL s_client command

前端 未结 3 938
我寻月下人不归
我寻月下人不归 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:21

    call isAliasExists with your values ,

    isAliasExists("api.sys.found1.cf.company.com","www.app.company.com");

    Returns true if your alias (servername) is part of the cert,

    private static boolean isAliasExists(String hostName, String alias) throws Exception  {
            String host;
            int port;
            String[] parts = hostName.split(":");
            host = parts[0];
            port = (parts.length == 1) ? 443 : Integer.parseInt(parts[1]);
            // key store password
            char[] passphrase = "changeit".toCharArray();
            File file = new File("jssecacerts");
            if (file.isFile() == false) {
                char SEP = File.separatorChar;
                File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security");
                file = new File(dir, "jssecacerts");
                if (file.isFile() == false) {
                    file = new File(dir, "cacerts");
                }
            }
            InputStream in = new FileInputStream(file);
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(in, passphrase);
            in.close();
            SSLContext context = SSLContext.getInstance("TLS");
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(ks);
            X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
            SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
            context.init(null, new TrustManager[] { tm }, null);
            SSLSocketFactory factory = context.getSocketFactory();
            SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
            socket.setSoTimeout(10000);
            try {
                System.out.println("Starting SSL handshake...");
                socket.startHandshake();
                socket.close();
                System.out.println("Certificate is already trusted");
            } catch (SSLException e) {
                e.printStackTrace();
            }
    
            X509Certificate[] chain = tm.chain;
    
            List<String> altNames=new ArrayList<String>();
    
            for (X509Certificate cert: chain)
            {
                altNames.addAll(getSubjectAltNames(cert));
            }
    
            for(String altName: altNames) {
                if(altName.trim().contains(alias))
                   return true;
            }
    
            if (chain == null) {
                System.out.println("Could not obtain server certificate chain");
                return false;
            }
    
    
    
            return false;
        }
    

    Returns list of alternative names from cert,

    private static List<String> getSubjectAltNames(X509Certificate certificate) throws CertificateParsingException {
             List<String> result = new ArrayList<>();
             try {
              Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
              if (subjectAltNames == null) {
               return Collections.emptyList();
              }
              for (Object subjectAltName : subjectAltNames) {
               List<?> entry = (List<?>) subjectAltName;
               if (entry == null || entry.size() < 2) {
                continue;
               }
               Integer altNameType = (Integer) entry.get(0);
               if (altNameType == null) {
                continue;
               }
                String altName = (String) entry.get(1);
                if (altName != null) {
                 result.add(altName);
                }
              }
              return result;
             } catch (CertificateParsingException e) {
              return Collections.emptyList();
             }
            }
    

    custom trust manager,

    private static class SavingTrustManager implements X509TrustManager {
    
            private final X509TrustManager tm;
            private X509Certificate[] chain;
    
            SavingTrustManager(X509TrustManager tm) {
                this.tm = tm;
            }
    
            public X509Certificate[] getAcceptedIssuers() {
    
                return new X509Certificate[0];
                // throw new UnsupportedOperationException();
            }
    
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                throw new UnsupportedOperationException();
            }
    
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                this.chain = chain;
                tm.checkServerTrusted(chain, authType);
            }
        }
    
    0 讨论(0)
  • 2021-01-17 14:28

    I was able to achieve this by referring the document over here

    Basically, a SSLEngine needs to be created and make a successful handshake along with SNI

     private SocketChannel createSocketChannel() throws IOException {
            InetSocketAddress socketAddress = new InetSocketAddress(PROXY_ADDRESS, PROXY_PORT);
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.connect(socketAddress);
            socketChannel.configureBlocking(false);
            return socketChannel;
    
        }
    
    private SSLContext createSSLContext() throws KeyManagementException, NoSuchAlgorithmException {
            SSLContext sslContext = SSLContext.getInstance(TLS_VERSION);
            sslContext.init(null,null,null);
            return sslContext;
        }
    
    
    
    
    private SSLEngine createSSLEngine() throws KeyManagementException, NoSuchAlgorithmException {
            SSLContext sslContext = createSSLContext();
            SSLEngine sslEngine = sslContext.createSSLEngine(PROXY_ADDRESS, PROXY_PORT);
            sslEngine.setUseClientMode(true);
    
            List<SNIServerName> serverNameList = new ArrayList<>();
            serverNameList.add(new SNIHostName(SNI_HOST_NAME));
            SSLParameters sslParameters = sslEngine.getSSLParameters();
            sslParameters.setServerNames(serverNameList);
    
            sslEngine.setSSLParameters(sslParameters);
    
            return sslEngine;
        }
    

    After creating SSLEngine, the handShake has to begin

    SocketChannel channel = createSocketChannel();
    SSLEngine sslEngine = createSSLEngine();
    doHandShake(sslEngine,channel);
    
    
     private void doHandShake(SSLEngine sslEngine, SocketChannel socketChannel) throws Exception {
            System.out.println("Going to do Handshake");
    
            SSLSession session = sslEngine.getSession();
    
            ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
            ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
    
            ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
            ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
    
            sslEngine.beginHandshake();
            SSLEngineResult result;
    
            handshakeStatus = sslEngine.getHandshakeStatus();
    
            while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED &&
                    handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
    
    
                switch (handshakeStatus) {
                    case NEED_UNWRAP:
                        if (! (socketChannel.read(peerNetData) <0)) {
                            peerNetData.flip();
                            result = sslEngine.unwrap(peerNetData,peerAppData);
                            peerNetData.compact();
    
                            handshakeStatus = result.getHandshakeStatus();
    
                            switch (result.getStatus()) {
                                case OK:
                                    break;
                            }
    
                        }
    
                        break;
                    case NEED_WRAP:
                        myNetData.clear() ;// Empty the local network packet buffer
                        result = sslEngine.wrap(myAppData,myNetData);
                        handshakeStatus = result.getHandshakeStatus();
                        switch (result.getStatus()) {
                            case OK:
                                myNetData.flip();
                                while (myNetData.hasRemaining()) {
                                    socketChannel.write(myNetData);
                                }
                        }
                        break;
    
                    case NEED_TASK:
                        Runnable task  = sslEngine.getDelegatedTask();
                        if (null!=task) {
                            task.run();
                        }
                        handshakeStatus = sslEngine.getHandshakeStatus();
                        break;
                }
    
    
            }
    

    Once the handShake is done. you can get the Principal object

    Principal principal = sslEngine.getSession().getPeerPrincipal();
    
                if (principal.getName().contains(SNI_HOST_NAME)) {
                    System.out.println("available ... ");
                }else {
                    System.out.println("Not available");
                }
    
    0 讨论(0)
  • 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:
         * <pre>
         * *** ClientHello
         * ...
         * Extension server_name, server_name: [type=host_name (0), value=DOMAIN;]
         * ...
         * *** ServerHello
         * ...
         * Extension server_name, server_name: 
         * ...
         * </pre>
         * 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();
            }
    
        }
    
    }
    
    
    0 讨论(0)
提交回复
热议问题