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?
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
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With