Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apache HttpClient - Log version of TLS that's negotiated in a request?

I've got lots of code that uses Apache's HttpClient, and I'd like to log the version of TLS that's being negotiated when a request is made.

Is this possible?

I'd prefer an approach that doesn't requiring changing how the request is being built, if possible - something that inspects a response or views a log or something like that?

From my review, it appears this is only possible if we debug SSL at the VM level, or perhaps if a custom SSLContext is used?

like image 757
Brad Parks Avatar asked Jan 03 '23 12:01

Brad Parks


2 Answers

If you turn on debug level logging for org.apache.http.conn.ssl category HttpClient will log quite a bit of details about the SSL session used including TLS/SSL protocol version.

[DEBUG] DefaultHttpClientConnectionOperator - Connecting to httpbin.org/54.225.150.40:443
[DEBUG] SSLConnectionSocketFactory - Connecting socket to httpbin.org/54.225.150.40:443 with timeout 0
[DEBUG] SSLConnectionSocketFactory - Enabled protocols: [TLSv1, TLSv1.1, TLSv1.2]
[DEBUG] SSLConnectionSocketFactory - Enabled cipher suites:[TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
[DEBUG] SSLConnectionSocketFactory - Starting handshake
[DEBUG] SSLConnectionSocketFactory - Secure session established
[DEBUG] SSLConnectionSocketFactory -  negotiated protocol: TLSv1.2
[DEBUG] SSLConnectionSocketFactory -  negotiated cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
[DEBUG] SSLConnectionSocketFactory -  peer principal: CN=httpbin.org
[DEBUG] SSLConnectionSocketFactory -  peer alternative names: [httpbin.org, www.httpbin.org]
[DEBUG] SSLConnectionSocketFactory -  issuer principal: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
[DEBUG] DefaultHttpClientConnectionOperator - Connection established 192.168.43.64:57534<->54.225.150.40:443
like image 70
ok2c Avatar answered Jan 05 '23 00:01

ok2c


Also, another possibility is to create a custom SSLContext and get information from the opened sessions:

    private static String URL = "https://www.google.com";
    private static String TRUST_STORE_FILE = "/Users/xpto/trust.p12";
    private static String TRUST_STORE_PASS = "truststore";
    private static String TRUST_STORE_TYPE = "PKCS12";
    private static String TLS_VERSION = "TLSv1.2";

    public static void main(String[] args) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(TRUST_STORE_TYPE);
        keyStore.load(new FileInputStream(TRUST_STORE_FILE), TRUST_STORE_PASS.toCharArray());

        SSLContext sslContext = SSLContexts
                .custom()
                .loadTrustMaterial(keyStore)
                .useProtocol(TLS_VERSION)
                .build();

        HttpComponentsClientHttpRequestFactory clientFactory = new HttpComponentsClientHttpRequestFactory(HttpClients
                .custom()
                .setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext))
                .build());

        RestTemplate restTemplate = new RestTemplate(clientFactory);

        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        requestHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));

        HttpEntity<String> requestEntity = new HttpEntity<String>(requestHeaders);
        print("Requesting: " + URL);
        ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.GET, requestEntity, String.class);
        print("Response: " + response.getBody());

        printSSLContextInfo(sslContext);
    }

    private static void printSSLContextInfo(SSLContext sslContext) throws Exception {
        print("-------------\nPrinting TLS Client Information");
        SSLSessionContext sslSessionContext = sslContext.getClientSessionContext();
        Enumeration<byte[]> sessionIds = sslSessionContext.getIds();
        while (sessionIds.hasMoreElements()) {
            SSLSession sslSession = sslSessionContext.getSession(sessionIds.nextElement());
            print("Client: " + sslSession.getPeerHost() + ":" + sslSession.getPeerPort());
            print("\tProtocol: " + sslSession.getProtocol());
            print("\tSessionID: " + byteArrayToHex(sslSession.getId()));
            print("\tCipherSuite: " + sslSession.getCipherSuite());
            for (X509Certificate certificate : sslSession.getPeerCertificateChain()) {
                print("\tX509 Certificate: " + certificate.getSubjectDN());
                print("\t\tIssuer: " + certificate.getIssuerDN().getName());
                print("\t\tAlgorithm: " + certificate.getSigAlgName());
                print("\t\tValidity: " + certificate.getNotAfter());
            }
        }
    }

    public static String byteArrayToHex(byte[] a) {
        StringBuilder sb = new StringBuilder(a.length * 2);
        for (byte b : a)
            sb.append(String.format("%02x", b));
        return sb.toString();
    }

    public static void print(Object msg) {
        System.out.println(msg);
    }

I'm just sharing this because I face similar issues, for further reference see: How to obtain all TLS sessions from SSLContext using IBMJSSE2 default provider?

like image 39
João Pedro Schmitt Avatar answered Jan 05 '23 00:01

João Pedro Schmitt