3
votes

I'm working on an application that processes food orders and we send the requests via HttpsURLConnection to a php function that has been ssl certified. The problem I'm having is that it rejects the handshake sometimes and not others. I was wondering if anyone could explain to me why it would reject it one time and not another.

javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x56cbe008: Failure in SSL library, usually a protocol error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x52eb6d74:0x00000000) javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x56cbe008: Failure in SSL library, usually a protocol error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x52eb6d74:0x00000000)

url = new URL(request.endpointUri);

Log.d(LOG_TAG, "Opening connection to " + request.endpointUri);
conn = (HttpsURLConnection)url.openConnection();

//setup the connection
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("charset", "UTF-8");
conn.setDoInput(true);
conn.setDoOutput(true);

//setup the parameters
Uri.Builder params = new Uri.Builder();
String paramString;

params.appendQueryParameter("cctoken", request.token.getId());
params.appendQueryParameter("amt", Integer.toString(request.order.amount));
params.appendQueryParameter("email", request.order.customerEmail);
params.appendQueryParameter("order", request.order.details);

paramString = params.build().getEncodedQuery();
conn.setFixedLengthStreamingMode(paramString.getBytes("UTF-8").length);

Log.d(LOG_TAG, "Compiled query into: " + paramString);

//write the POST request params
OutputStream os = conn.getOutputStream();
BufferedWriter streamWriter = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
streamWriter.write(paramString);
streamWriter.flush();
streamWriter.close();
os.close();

//read the response
int responseCode = conn.getResponseCode();
InputStream is;

The line given for failure is when it attempts to collect the output.

OutputStream os = conn.getOutputStream();
3

3 Answers

4
votes

SSL handshake errors which only happen sometimes are often related to server side problems, so your code is not of much help here. Possible causes at the server side are multiple servers with different configuration (some work, some don't), timeouts which might be caused by too much load, server side crashes. There might also be some erratic middleware involved (firewalls) or if the connection is unreliable from start it will also affect the SSL handshake.

Thus don't look too much at your code but look at the server and the network. If in doubt try another client and if this one shows a more stable behavior look into the differences in the connection and SSL handshake (i.e. packet captures).

1
votes

This problem could be due to self signed certificate of server to which you are sending request. You have to do something like this: `// Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...)

CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt

InputStream caInput = new BufferedInputStream(new FileInputStream("load-   der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}

// Create a KeyStore containing our trusted CAs

String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// Create a TrustManager that trusts the CAs in our KeyStore

String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

// Create an SSLContext that uses our TrustManager

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

//Tell the URLConnection to use a SocketFactory from our SSLContext

URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);`

Found it here

0
votes

Related Issue: Client and server don't support a common encryption protocol

If the error message says something like:

error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version (external/openssl/ssl/s23_clnt.c:741 0x8d92d990:0x00000000)

(Note that is says 'tlsv1' after 'GET_SERVER_HELLO:' rather than 'sslv3') This is a faint clue that the problem could be with the version of encryption needed. If the client is old, it may only support sslv2 or sslv3. An up to date server may support TLS1.2 and does not support the older (perhaps depracated ssl versions. The opposite may be true as well, with the server supporting only the old and the client only the new.

I ran into this problem with an old Android Jelly Bean client that doesn't support TLS1.2, by default, for HttpsUrlConnection. By creating a TLSSocketFactory and X509TrustManager, and calling setSSLSocketFactory() I could get the old version of Android to use TLS1.2, which made the server happy. Navneet Krishna wrote a good description of how to do this in: https://medium.com/@krisnavneet/how-to-solve-sslhandshakeexception-in-android-ssl23-get-server-hello-tlsv1-alert-protocol-13b457c724ef