I've been researching all over the internet for 3 weeks now for how to make Netty enable TLS (any version of it) on Android (at first, I took it on as a challenge). But as of now, I couldn't get it to work, not even once. I have read a LOT of articles, gists and docs about how TLS works, and certificates (inc. self-signed certificates and two-way authentification), key stores, the handshake itself, and even opportunistic TLS, but this is all confusing to me I don't know where else to ask. I need guidance to piece things together.
I just can't put all the pieces together. Here's what I have:
A Twisted server (Python), which doesn't belong to me, will present his certificates to the client. The certificates are issued by Let's Encrypt's global CAs (If you don't know Let's Encrypt, that's fine, all you have to know is that they're globally trusted and also compatible with Android although I don't know what they meant exactly with that). The server does not support self-signed certificates
And a Netty client that establishes bare unencrypted connection with the server (Which works just fine because the server will accept the bare connection as is if opportunistic TLS isn't triggered. The client code (More precisely, the bootstrapping initialization part, which matters the most) is here:
viewModelScope.launch(Dispatchers.IO) {
val group: EventLoopGroup = NioEventLoopGroup()
val b = Bootstrap()
b.group(group) /* Assigning the event loop group to the bootstrap */
.channel(NioSocketChannel::class.java) /* We want a NIO Socket Channel */
.handler(object : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
val p: ChannelPipeline = ch.pipeline()
/** Adding TLS stuff ? There's literally no good doc on this */
val engine: SSLEngine? = null
engine?.useClientMode = true
p.addLast("ssl", SslHandler(engine))
p.addLast(
"framer",
DelimiterBasedFrameDecoder(8192, *Delimiters.lineDelimiter())
)
p.addLast(StringDecoder())
p.addLast(StringEncoder())
p.addLast(Reader())
}
})
/** The following line works for non-TLS, but for TLS, nothing happens */
val f: ChannelFuture = b.connect(serverHost, serverPort)
}
How exactly do certificates work on Android? Should I create a key store ? or a certificate ? or both ? I don't really understand how to get this done. I heard that Okhttp has SSL built-in, so how does it actually initialize that SSL connection ?
Related
I'd like to make an HTTPS connection to a server and, if I'm using
non-ephemeral DH key exchange, I'd like to know what the parameters
are for that connection. Actually, I don't really care if it's
ephemeral or not.
What I'm looking for is the ability to make a connection and then warn
if the connection is using "weak" DH parameters. Is that something I
can check at connection-time? Or is the set of DH parameters (or, more
specifically, the length of those parameters, in bits) defined by
the cipher suite itself?
For example, the Qualys community thread has an illustration of the
cipher suites that SSLLabs considers "weak" (well, everyone considers
them weak... they just have a public tool which complains about them):
https://community.qualys.com/thread/14821
They specifically mention e.g. TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
which is cipher suite 0x9f and mention the DH parameters. Are those
parameters' parameters baked-into the cipher suite (meaning they are
always 1024-bit) or is this a configuration of the server that makes
those cipher suites weak due to the specific DH parameter choice?
In either case, I'd like to be able to sniff that information from the
connection if at all possible. Does anyone know if this can be done,
and how?
I've written some code to attempt to get this information about the handshake, but I keep getting null for the object I was hoping would contain this data.
SSLSocketFactory sf = ...;
Socket sock = new Socket();
sock.connect(address, timeout);
SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
socket.startHandshake();
SSLSession sess = socket.getHandshakeSession();
I was hoping that sess at this point would contain some interesting information about the handshake, but it's null. The javadoc for startHandshake indicates that it will notify an event listener when the handshake is completed. So I tried this:
SSLSocketFactory sf = ...;
Socket sock = new Socket();
sock.connect(address, timeout);
SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
socket.startHandshake();
// SSLSession sess = socket.getHandshakeSession();
SSLSession sess = socket.getSession(); // This forces the handshake to complete
sess = socket.getHandshakeSession();
... but sess is still null at this point. The "real" SSLSession does exist and gives me information about the connection, but the "handshake session" seems to always be null.
So I tried writing an HandshakeCompletedListener, and I do in fact get an SSLSession, but it appears to be the same one that I can get from the SSLSocket already, so the "handshake" session seems to be unhelpful.
How can I get those parameters from the SSLSession?
Are those parameters' parameters baked-into the cipher suite (meaning they are always 1024-bit) or is this a configuration of the server that makes those cipher suites weak due to the specific DH parameter choice?
No, this is a configuration parameter for the protocol. There is a default of 1024 bits for Java but that may be changed globally for JSSE (the Java TLS implementation) using a system property: jdk.tls.ephemeralDHKeySize. Best set this during startup with a -D option for the Java VM.
For static DH key pairs (that are used for authentication) you would have to look into the DH certificate. But I don't think you'll find any, everybody uses RSA for authentication.
In either case, I'd like to be able to sniff that information from the connection if at all possible. Does anyone know if this can be done, and how?
Well, for sniffing tools such as WireShark would suffice. Undoubtedly you can parse things like DH parameters from a TLS connection (if they are used in the first place of course).
You can also debug connections using -Djavax.net.debug
For Java applications / libraries you could look up the cipher suite and then, if it contains DHE_ look up the aforementioned system property (keeping in mind its default values).
The Java JSSE API was not written with deep packet inspection in mind. It's (literally) a service oriented implementation for servers and client applications. Although you could of course use the OpenJDK code itself (it's GPL'ed, right?) you are better off using a separate implementation, possibly with an even more permissive license.
For a sniffer however I would rather use C/C++ (or at least a C/C++ frontend) than Java.
For most cipher algorithms, the length is determined by the name cypher name, as also mentioned here How to get the actual block cipher key size for Java SSL connection _in code_? .
Instead of trying to warn people when they are using unsecure cyphers, I'd recommend to disable those ciphers by selecting only the cyphers you want to support. You can do this on a jvm level or on the SSLSocket, e.g.
String pickedCipher[] ={"TLS_RSA_WITH_AES_128_CBC_SHA"};
socket.setEnabledCipherSuites(pickedCipher);
You can also set the desired key size, see here
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#customizing_dh_keys
You can see defaults and classes used in Java security here https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
If you are curious and want to investigate this in more detail, I'd recommend to turn on ssl logging, as described here.
I need to check whether a server supports SSL and the ciphers from the web server.
I looked into SSLSocket in Java but I do not get it to work properly.
I used the method getSupportedProtocols() it always gives out the same protocols for each url. Moreover I do not get the ciphers that are given from the server. I guessed getEnabledCipherSuites() would be the correct method
try {
SSLContext ctx = SSLContext.getDefault();
ctx.getClientSessionContext().setSessionTimeout(5); // in seconds
SSLSocket socket = (SSLSocket) ctx.getSocketFactory().createSocket("host.com", 443);
socket.setSoTimeout(5000); // in millis
String[] result = socket.getEnabledCipherSuites();
Arrays.sort(result);
for (int i = 0; i < result.length; i++) {
System.out.println(result[i]);
}
} catch (IOException ex) {
System.out.println("Error!");
}
How can I check that the server uses SSL? What ciphers were returned from the server?
getSupportedProtocols()/CipherSuites() returns the lists of protocols and ciphers (respectively) that your side can support. getEnabledProtocols()/CipherSuites() returns the subset of these lists that are enabled on your side.
That won't tell you much about about what the server supports.
According to the TLS specification, the client sends the highest protocol version can use and the list of cipher suites it wants to use. The server then uses the highest protocol it supports within that list (if any) and selects the cipher suite it wants amongst that list (if any).
There's some flexibility regarding how the cipher suite selection is done (see SSLHonorCipherOrder directive in Apache Httpd, for example).
Essentially, if you want to see which cipher suites are actually supported by your server, you'll have to iterate through each cipher suite you support and enable them one by one to try a handshake.
used the method getSupportedProtocols() it always gives out the same protocols for each url.
It always give you the protocols supported by your end. That doesn't change per URL.
Moreover I do not get the ciphers that are given from the server. I guessed getEnabledCipherSuites() would be the correct method.
You guessed wrong. It gives you the ciphers supported by your end.
How can I check that the server uses SSL?
Call startHandshake() and catch the exception.
What ciphers were returned from the server?
You can't, but you can tell what cipher suite was negotiated, by looking in the SSLSession.
I have created a ssl socket connection with my server. The server sends me the RC4 key and i use the key to create a cipher and bind it to the input and output streams of the existing socket. I am getting the following error on trying to read from the input stream:
javax.net.ssl.SSLProtocolException: Read error: ssl=0x32fbf8: Failure in SSL library, usually a protocol error
Is it possible that the decryption is not working properly or the RC4 key cipher is not correct. what are the reasons for such an error. I am doing it in an app on android 2.3.3.
One more query, does android 2.3.3 support sslv23(openssl) . If so how can instantiate the same ?(In windows client, i set the session context with the rc4 key and it works perfectly fine)
I am new to java and android and come from VC++ background.
Experts and programmers please enlighten me on my queries. My code is as follows:
sslContext = SSLContext.getInstance("TLS");
/* some code to initialize ssl context */
SSLSocketFactory sslSocketfactory = sslContext.getSocketFactory();
sock = (SSLSocket) sslSocketfactory.createSocket(host,port1);
sock.setSoTimeout(12000);
is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
16384));
os = sock.getOutputStream();
/* some more code using above mentioned is and os to recieve rc4 key and
write it into a byte array */
SecretKey key2 = new SecretKeySpec(reverserRc4InBytes, 0, reverserRc4InBytes.length, "RC4");
Cipher cipherOS = Cipher.getInstance("RC4");
cipherOS.init(Cipher.ENCRYPT_MODE, key2);
Cipher cipherIS = Cipher.getInstance("RC4");
cipherIS.init(Cipher.DECRYPT_MODE, key2);
cos = new CipherOutputStream(os,cipherOS);
cis = new CipherInputStream(is,cipherIS);
From what I understand, you're first making an SSL/TLS connection to exchange the RC4 key somehow, and then you're using it to encipher and decipher the result, instead of letting the SSL/TLS stack do it all for you. (It's clearly unnecessarily convoluted, and it's not clear how secure this is, since SSL/TLS provides you with more than encryption, in particular with integrity.)
The server is implemented in C and using an existing SSL stack ..
sslv23 to be specific.
SSLv23 isn't really a "stack" (by "stack" I mean an implementation: JSSE, OpenSSL, .Net's Security API, ...). SSLv23 usually refers to SSLv3 where the initial Client Hello message is wrapped in SSLv2 format. This wrapping happens on the client side, for clients that supports both SSLv3 and SSLv3/TLSv1.x. Which versions the server support should be fixed, in particular, you shouldn't need to use that trick if your server supports SSLv3 and above. (Note that the JSSE supports the v2 wrapped Client Hello format, but doesn't actually support SSLv2. I guess this is also the case for Android.)
javax.net.ssl.SSLProtocolException: Read error: ssl=0x32fbf8: Failure
in SSL library, usually a protocol error
This indicates that something incompatible is happening between your server and the client. There can be a few reasons for this:
Your server only supports SSLv2 (not 3). Let's assume your server supports at least SSLv3 (you can check whether the handshake completes using Wireshark, for example.)
There's a more general problem with the SSL/TLS implementation on the server. (You could attempt to connect to it using other tools, such as openssl s_client, at least to see if the connection can be established.)
Your key exchange protocol expects the SSL/TLS connection to end there, while leaving your manual handling of the connection on a plain TCP socket afterwards. You may have to use your Socket as an SSLSocket only during the section where SSL/TLS is used and revert back to the plain Socket afterwards. ()
You can try to establish a plain socket to your server, upgrade it to an SSLSocket using createSocket (Socket s, String host, int port, boolean autoClose) with autoClose=false and get the I/O from the plain socket to do your manual encryption.
I would assume that this should have caused other problems on the SSLSocket side, especially when the server closes its side of the SSL/TLS connection, though. This is just a guess, this approach might work.
Anyway, I don't think the problems you're seeing have anything to do with your using Cipher manually from your SSLSocket's I/O streams, since the exception happens at the underlying layer, which should be hidden as far as the data you read/write there is concerned.
I don't think you have understood your assignment properly. Either you should be using SSL or you need to implement this home-grown cipher.
Or else you should look for a saner job ;-)
I'm trying to connect an Android app to a SSL-enabled server, which uses a self-signed certificate. I've already read through dozens of tutorials and the app is now accepting the certificate & connecting to the server, but I never get any data back.
The original code i used to initialize the socket is this:
//passphrase for keystore
char[] keystorePass="password".toCharArray();
//load own keystore (MyApp just holds reference to application context)
KeyStore keyStore=KeyStore.getInstance("BKS");
keyStore.load(MyApp.getStaticApplicationContext().getResources().openRawResource(R.raw.keystore),keystorePass);
//create a factory
TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//get context
SSLContext sslContext=SSLContext.getInstance("TLS");
//init context
sslContext.init(
null,
trustManagerFactory.getTrustManagers(),
new SecureRandom()
);
//create the socket
Socket socket=sslContext.getSocketFactory().createSocket("hostname",443);
socket.setKeepAlive(true);
Afterwards, the run loop of the receiver thread uses socket.getInputStream() to access the input stream.
As long as I use an unencrypted connection, this works without a problem. But the secure connection does not retrieve any data from the socket. I've verified this by adding log messages to the receive loop and even used OpenSSL's s_server to check. I retrieved data from the client, but the client never received anything I sent to it.
As a last test, I tried to open a connection to www.google.com:443 like this:
javax.net.SocketFactory fact=SSLSocketFactory.getDefault();
Socket socket=fact.createSocket(_config.getUri().getHost(), _config.getUri().getPort());
Still the same result, connection works but using the InputStream I receive nothing from the server.
Anybody got any ideas?
EDIT:
I'm currently not allowed to answer my own question, but here's the answer:
Well, turns out the problem WAS the receive loop. I relied on InputStream.available() to get the number of bytes to read, but didn't realize it was rather unreliable (always returns 0 for SSL socket). So I switched the receive loop to use the blocking read() instead.
As mentioned above: Turns out the problem WAS the receive loop. I relied on InputStream.available() to get the number of bytes to read, but didn't realize it was rather unreliable (always returns 0 for SSL socket). So I switched the receive loop to use the blocking read() instead.
My server uses data from an internal web service to construct its response, on a per request basis. I'm using Apache HttpClient 4.1 to make the requests. Each initial request will result in about 30 requests to the web service. Of these, 4 - 8 will end up with sockets stuck in CLOSE_WAIT, which never get released. Eventually these stuck sockets exceed my ulimit and my process runs out of file descriptors.
I don't want to just raise my ulimit (1024), because that will just mask the problem.
The reason I've moved to HttpClient is that java.net.HttpUrlConnection was behaving the same way.
I have tried moving to a SingleClientConnManager per request, and calling client.getConnectionManager().shutdown() on it, but sockets still end up stuck.
Should I be trying to solve this so that I end up with 0 open sockets while there are no running requests, or should I be concentrating on request persistence and pooling?
For clarity I'm including some details which may be relevant:
OS: Ubuntu 10.10
JRE: 1.6.0_22
Language: Scala 2.8
Sample code:
val cleaner = Executors.newScheduledThreadPool(1)
private val client = {
val ssl_ctx = SSLContext.getInstance("TLS")
val managers = Array[TrustManager](TrustingTrustManager)
ssl_ctx.init(null, managers, new java.security.SecureRandom())
val sslSf = new org.apache.http.conn.ssl.SSLSocketFactory(ssl_ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
val schemeRegistry = new SchemeRegistry()
schemeRegistry.register(new Scheme("https", 443, sslSf))
val connection = new ThreadSafeClientConnManager(schemeRegistry)
object clean extends Runnable{
override def run = {
connection.closeExpiredConnections
connection.closeIdleConnections(30, SECONDS)
}
}
cleaner.scheduleAtFixedRate(clean,10,10,SECONDS)
val httpClient = new DefaultHttpClient(connection)
httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), new UsernamePasswordCredentials(username,password))
httpClient
}
val get = new HttpGet(uri)
val entity = client.execute(get).getEntity
val stream = entity.getContent
val justForTheExample = IOUtils.toString(stream)
stream.close()
Test: netstat -a | grep {myInternalWebServiceName} | grep CLOSE_WAIT
(Lists sockets for my process that are in CLOSE_WAIT state)
Post comment discussion:
This code now demonstrates correct usage.
One needs to pro-actively evict expired / idle connections from the connection pool, as in the blocking I/O model connections cannot react to I/O events unless they are being read from / written to. For details see
http://hc.apache.org/httpcomponents-client-dev/tutorial/html/connmgmt.html#d4e631
I've marked oleg's answer as correct, as it highlights an important usage point about HttpClient's connection pooling.
To answer my specific original question, though, which was "Should I be trying to solve for 0 unused sockets or trying to maximize pooling?"
Now that the pooling solution is in place and working correctly the application throughput has increased by about 150%. I attribute this to not having to renegotiate SSL and multiple handshakes, instead reusing persistent connections in accordance with HTTP 1.1.
It is definitely worth working to utilize pooling as intended, rather than trying to hack around with calling ThreadSafeClientConnManager.shutdown() after each request etcetera. If, on the other hand, you were calling arbitrary hosts and not reusing routes the way I am you might easily find that it becomes necessary to do that sort of hackery, as the JVM might surprise you with the long life of CLOSE_WAIT designated sockets if you're not garbage collecting very often.
I had the same issue and solved it using the suggesting found here: here. The author touches on some TCP basics:
When a TCP connection is about to close, its finalization is negotiated by both parties. Think of it as breaking a contract in a civilized manner. Both parties sign the paper and it’s all good. In geek talk, this is done via the FIN/ACK messages. Party A sends a FIN message to indicate it wants to close the socket. Party B sends an ACK saying it received the message and is considering the demand. Party B then cleans up and sends a FIN to Party A. Party A responds with the ACK and everyone walks away.
The problem comes in
when B doesn’t send its FIN. A is kinda stuck waiting for it. It has
initiated its finalization sequence and is waiting for the other party
to do the same.
He then mentions RFC 2616, 14.10 to suggest setting up an http header to solve this issue:
postMethod.addHeader("Connection", "close");
Honestly, I don't really know the implications of setting this header. But it did stop CLOSE_WAIT from happening on my unit tests.