Two-way SSL authentication on Android

I am trying to get two-way SSL authentication working between a Python server and an Android client application. I have access to the server and client, and I would like to implement client authentication using my own certificate. So far, I have been able to verify the server certificate and connect without client authentication.

What certificate does the client need and how to get it so that it automatically sends it to the server during the handshake process? Here is the client and server code that I still have. Is my approach wrong?

Server code

while True: # Keep listening for clients c, fromaddr = sock.accept() ssl_sock = ssl.wrap_socket(c, keyfile = "serverPrivateKey.pem", certfile = "servercert.pem", server_side = True, # Require the client to provide a certificate cert_reqs = ssl.CERT_REQUIRED, ssl_version = ssl.PROTOCOL_TLSv1, ca_certs = "clientcert.pem", #TODO must point to a file of CA certificates?? do_handshake_on_connect = True, ciphers="!NULL:!EXPORT:AES256-SHA") print ssl_sock.cipher() thrd = sock_thread(ssl_sock) thrd.daemon = True thrd.start() 

I suspect that I can use the wrong file for ca_certs ...?

Client code

  private boolean connect() { try { KeyStore keystore = KeyStore.getInstance("BKS"); // Stores the client certificate, to be sent to server KeyStore truststore = KeyStore.getInstance("BKS"); // Stores the server certificate we want to trust // TODO: change hard coded password... THIS IS REAL BAD MKAY truststore.load(mSocketService.getResources().openRawResource(R.raw.truststore), "test".toCharArray()); keystore.load(mSocketService.getResources().openRawResource(R.raw.keystore), "test".toCharArray()); // Use the key manager for client authentication. Keys in the key manager will be sent to the host KeyManagerFactory keyFManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyFManager.init(keystore, "test".toCharArray()); // Use the trust manager to determine if the host I am connecting to is a trusted host TrustManagerFactory trustMFactory = TrustManagerFactory.getInstance(TrustManagerFactory .getDefaultAlgorithm()); trustMFactory.init(truststore); // Create the socket factory and add both the trust manager and key manager SSLCertificateSocketFactory socketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory .getDefault(5000, new SSLSessionCache(mSocketService)); socketFactory.setTrustManagers(trustMFactory.getTrustManagers()); socketFactory.setKeyManagers(keyFManager.getKeyManagers()); // Open SSL socket directly to host, host name verification is NOT performed here due to // SSLCertificateFactory implementation mSSLSocket = (SSLSocket) socketFactory.createSocket(mHostname, mPort); mSSLSocket.setSoTimeout(TIMEOUT); // Most SSLSocketFactory implementations do not verify the server identity, allowing man-in-the-middle // attacks. This implementation (SSLCertificateSocketFactory) does check the server certificate hostname, // but only for createSocket variants that specify a hostname. When using methods that use InetAddress or // which return an unconnected socket, you MUST verify the server identity yourself to ensure a secure // connection. verifyHostname(); // Safe to proceed with socket now ... 

I created a client private key, client certificate, server private key and server certificate using openssl. Then I added the client certificate to keystore.bks (which I stored in /res/raw/keystore.bks ). Then I added the server certificate to truststore.bks

So, now that the client is trying to connect, I get this side of the error server:

 ssl.SSLError: [Errno 1] _ssl.c:504: error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate 

And when I try to do this in android client

 SSLSession s = mSSLSocket.getSession(); s.getPeerCertificates(); 

I get this error:

 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 

Thus, it is obvious that the keystore that I use does not seem to have the correct peer certificate in it, and therefore does not send it to the server.

What should be stored in the keystore to prevent this exception?

Also, is this two-way SSL authentication method safe and effective?

+4
source share
2 answers

The server needs to trust the client certificate. The usual way to do this is to create a CA and then sign the server certificate and client certificate. Each of them must have a CA certificate in their respective trust stores. Then you need to initialize the SSLContext as follows:

 KeyStore trustStore = loadTrustStore(); KeyStore keyStore = loadKeyStore(); TrustManagerFactory tmf = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); KeyManagerFactory kmf = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, KEYSTORE_PASSWORD.toCharArray()); SSLContext sslCtx = SSLContext.getInstance("TLS"); sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

Then you can use SSLContext to create socket factories. They will be initialized with the appropriate keys and certificates.

+1
source

Update

I am having trouble getting a peer-to-peer network certificate added to the keystore and having this certificate sent to the server.

Do some research that might help your trip. mvsjes2 reported that the wrong ports could throw an SSLPeerUnverifiedException. Make sure you use port 443 as I do not see in your code where you install the port.

Also check out the emmby answer, which I found also insightful.

Original post

Thus, it is obvious that the keystore that I use does not seem to have the correct certificate equal to it and, therefore, does not send it to the server.

Try this article on using connect to Unknown Certificates permissions, which will allow you to use a certificate that is not standard for Android. You may need to include a public / intermediate certificate in the application to make sure that existing / older devices can connect to your server. Follow the article and see if your problem has resolved.

Also, is this two-way SSL authentication method safe and effective?

Only if you can start working! While your code can verify the validity of the SSL certificate, it should be quite efficient. Security measures ... And for so long the ssl certificate itself has been created with a strong RSA signature / key, which will make it more secure than just plain http. Let me know if you have any problems.

0
source

Source: https://habr.com/ru/post/1488540/


All Articles