Search code examples
javahttpsslhttpshttpclient

Java SSL Exception, protocol_version when trying to use HttpClient to log into a website


I have a Java program that I am trying to get to talk to a website that uses an HTTPS connection with TLSv1. (Checked with FireFox.)

The problem is that I still get a protocol_version exception. Here is the stack trace:

    javax.net.ssl.SSLException: Received fatal alert: protocol_version
2015-07-28 14:48:38 [INFO ] 
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2023)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
    at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
    at org.openhab.binding.honeywellwifithermostat.handler.honeywellWifiThermostatHandler.getHoneywellWebsiteAuthCookie(honeywellWifiThermostatHandler.java:91)
    at org.openhab.binding.honeywellwifithermostat.handler.honeywellWifiThermostatHandler.initialize(honeywellWifiThermostatHandler.java:73)
    at org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory.registerHandler(BaseThingHandlerFactory.java:116)
    at org.eclipse.smarthome.core.thing.internal.ThingManager$6.call(ThingManager.java:480)
    at org.eclipse.smarthome.core.thing.internal.ThingManager$6.call(ThingManager.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Here is the relevant Java source:

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.SSLContext;

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link honeywellWifiThermostatHandler} is responsible for handling commands, which are
 * sent to one of the channels.
 *
 * @author  - Initial contribution
 */
public class honeywellWifiThermostatHandler extends BaseThingHandler {

    private Logger logger = LoggerFactory.getLogger(honeywellWifiThermostatHandler.class);

    private String userName = null;
    private String passWord = null;

    public honeywellWifiThermostatHandler(Thing thing) {
        super(thing);
    }

    @Override
    public void handleCommand(ChannelUID channelUID, Command command) {
        if (channelUID.getId().equals(CHANNEL_1)) {
            // TODO: handle command
        }
    }

    @Override
    public void initialize() {
        // TODO Auto-generated method stub
        Configuration conf = this.getConfig();

        if (conf.get("userName") != null) {
            userName = String.valueOf(conf.get("userName"));
        }
        if (conf.get("passWord") != null) {
            passWord = String.valueOf(conf.get("passWord"));
        }

        logger.debug("Attempting to get auth cookie for Honeywell site.");
        getHoneywellWebsiteAuthCookie(userName, passWord);

        super.initialize();
    }

    public void getHoneywellWebsiteAuthCookie(String userName, String passWord) {

        CloseableHttpClient httpclient;
        try {
            SSLContext ctx = SSLContexts.custom().useProtocol("TLSv1").build();
            httpclient = HttpClientBuilder.create().setSslcontext(ctx).build();

            HttpPost httpPost = new HttpPost("https://www.mytotalconnectcomfort.com/portal/");
            List<NameValuePair> fields = new ArrayList<NameValuePair>();
            fields.add(new BasicNameValuePair("UserName", userName));
            fields.add(new BasicNameValuePair("Password", passWord));

            httpPost.setEntity(new UrlEncodedFormEntity(fields));
            CloseableHttpResponse resp = httpclient.execute(httpPost);

            logger.debug("Status line: {}", resp.getStatusLine());
        } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

Any ideas out there? Even an idea of how I could trace this down?

UPDATE

I got the debug information from the Java VM. It gives a bunch of errors about unsupported cipher suites, then maybe tries to fallback to SSLv3? I'm not too strong with crypto stuff. Again, any help is much appreciated.

-------------------------  NET DEBUG CRAP ---------------------------
trustStore is: /usr/lib/jvm/java-8-openjdk/jre/lib/security/cacerts
trustStore type is : jks
trustStore provider is : 
init truststore

== REMOVED ADDING TRUSTED ROOT CERT MESSAGES ==

trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1421269547 bytes = { 179, 68, 198, 137, 7, 197, 13, 106, 3, 187, 99, 160, 117, 164, 48, 226, 113, 136, 166, 199, 101, 82, 195, 192, 46, 52, 140, 181 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 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_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]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
Extension server_name, server_name: [type=host_name (0), value=www.mytotalconnectcomfort.com]
***
[write] MD5 and SHA1 hashes:  len = 219
0000: 01 00 00 D7 03 03 55 B7   DA 2B B3 44 C6 89 07 C5  ......U..+.D....
0010: 0D 6A 03 BB 63 A0 75 A4   30 E2 71 88 A6 C7 65 52  .j..c.u.0.q...eR
0020: C3 C0 2E 34 8C B5 00 00   2C C0 0A C0 14 00 35 C0  ...4....,.....5.
0030: 05 C0 0F 00 39 00 38 C0   09 C0 13 00 2F C0 04 C0  ....9.8...../...
0040: 0E 00 33 00 32 C0 08 C0   12 00 0A C0 03 C0 0D 00  ..3.2...........
0050: 16 00 13 00 FF 01 00 00   82 00 0A 00 34 00 32 00  ............4.2.
0060: 17 00 01 00 03 00 13 00   15 00 06 00 07 00 09 00  ................
0070: 0A 00 18 00 0B 00 0C 00   19 00 0D 00 0E 00 0F 00  ................
0080: 10 00 11 00 02 00 12 00   04 00 05 00 14 00 08 00  ................
0090: 16 00 0B 00 02 01 00 00   0D 00 1A 00 18 06 03 06  ................
00A0: 01 05 03 05 01 04 03 04   01 03 03 03 01 02 03 02  ................
00B0: 01 02 02 01 01 00 00 00   22 00 20 00 00 1D 77 77  ........". ...ww
00C0: 77 2E 6D 79 74 6F 74 61   6C 63 6F 6E 6E 65 63 74  w.mytotalconnect
00D0: 63 6F 6D 66 6F 72 74 2E   63 6F 6D                 comfort.com
ESH-safeCall-2, WRITE: TLSv1.2 Handshake, length = 219
[Raw write]: length = 224
0000: 16 03 03 00 DB 01 00 00   D7 03 03 55 B7 DA 2B B3  ...........U..+.
0010: 44 C6 89 07 C5 0D 6A 03   BB 63 A0 75 A4 30 E2 71  D.....j..c.u.0.q
0020: 88 A6 C7 65 52 C3 C0 2E   34 8C B5 00 00 2C C0 0A  ...eR...4....,..
0030: C0 14 00 35 C0 05 C0 0F   00 39 00 38 C0 09 C0 13  ...5.....9.8....
0040: 00 2F C0 04 C0 0E 00 33   00 32 C0 08 C0 12 00 0A  ./.....3.2......
0050: C0 03 C0 0D 00 16 00 13   00 FF 01 00 00 82 00 0A  ................
0060: 00 34 00 32 00 17 00 01   00 03 00 13 00 15 00 06  .4.2............
0070: 00 07 00 09 00 0A 00 18   00 0B 00 0C 00 19 00 0D  ................
0080: 00 0E 00 0F 00 10 00 11   00 02 00 12 00 04 00 05  ................
0090: 00 14 00 08 00 16 00 0B   00 02 01 00 00 0D 00 1A  ................
00A0: 00 18 06 03 06 01 05 03   05 01 04 03 04 01 03 03  ................
00B0: 03 01 02 03 02 01 02 02   01 01 00 00 00 22 00 20  .............". 
00C0: 00 00 1D 77 77 77 2E 6D   79 74 6F 74 61 6C 63 6F  ...www.mytotalco
00D0: 6E 6E 65 63 74 63 6F 6D   66 6F 72 74 2E 63 6F 6D  nnectcomfort.com
[Raw read]: length = 5
0000: 15 03 00 00 02                                     .....
[Raw read]: length = 2
0000: 02 46                                              .F
ESH-safeCall-2, READ: SSLv3 Alert, length = 2
ESH-safeCall-2, RECV TLSv1.2 ALERT:  fatal, protocol_version
ESH-safeCall-2, called closeSocket()
ESH-safeCall-2, handling exception: javax.net.ssl.SSLException: Received fatal alert: protocol_version

Solution

  • *** ClientHello, TLSv1.2

    You are using TLS 1.2. But unfortunately the server does not understand TLS 1.2 at all and will cause a handshake failure. Tests with my analyze-ssl.pl show:

     * supported SSL versions with handshake used and preferred cipher(s):
      * handshake protocols ciphers
      * SSLv23    TLSv1     RC4-MD5
      * TLSv1_2   FAILED: SSL connect attempt failed error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
      * TLSv1_1   FAILED: SSL connect attempt failed error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
      * TLSv1     TLSv1     RC4-MD5
      * SSLv3     FAILED: SSL connect attempt failed because of handshake problems error:1409442E:SSL routines:SSL3_READ_BYTES:tlsv1 alert protocol version
    

    Thus the server simply fails on TLSv1_2 handshakes.

    You are trying to set TLS 1.0 inside your code:

            SSLContext ctx = SSLContexts.custom().useProtocol("TLSv1").build();
    

    Unfortunately the documentation states that 'TLSv1' is not only TLS 1.0 but

    TLSv1   Supports RFC 2246: TLS version 1.0 ; may support other versions
    

    Thus it tries with the best version it can (TLS1.2) and fails because the server is broken. BTW, Chrome and Firefox only succeed because they retry with a lower protocol version on such errors. I don't do Java myself but based on the various documentation I've found on the net you probably need to do something like:

       sslSocket.setEnabledProtocols(new String[] {"SSLv2Hello", "TLSv1" })
    

    which should remove the support for TLS1.1 and TLS1.2 .