Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SSL Client authentication in Android 4.x

I would like to create an App which connects to a server. This server uses SSL Client Authentication. The User of the App should be able to choose the certificate and allow the use of it like it is implemented in the browser app.

In the browser app the authentication is working as expected, so the certificate I use is valid.

When I try to connect in my App I get the following Error:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException:
SSL handshake terminated: ssl=0x2a2d3b38:
Failure in SSL library, usually a protocol error
error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
(external/openssl/ssl/s3_pkt.c:1290 0x2a2df880:0x00000003)

I tried to follow the android documentation for my implementation.

  • Unifying Key Store Access in ICS
  • HttpsURLConnection

Here is the code of my sample Activity:

public class ClientCertificateActivity extends Activity implements
    KeyChainAliasCallback {

protected static final String TAG = "CERT_TEST";
private String alias;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    choseCertificate();
    LinearLayout layout = new LinearLayout(this);
    Button connectToServer = new Button(this);
    connectToServer.setText("Try to connect to Server");
    connectToServer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    connectToServer.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            connectToServer();
        }
    });
    layout.addView(connectToServer);
    addContentView(layout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
}

protected void connectToServer() {
    final Context ctx = this;
    new AsyncTask<Void, Void, Boolean>() {

        private Exception error;

        @Override
        protected Boolean doInBackground(Void... arg) {
            try {
                PrivateKey pk = KeyChain.getPrivateKey(ctx, alias);
                X509Certificate[] chain = KeyChain.getCertificateChain(ctx,
                        alias);

                KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
                TrustManagerFactory tmf = TrustManagerFactory
                        .getInstance(TrustManagerFactory
                                .getDefaultAlgorithm());
                tmf.init(keyStore);

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

                URL url = new URL("https://usecert.example.com/");
                HttpsURLConnection urlConnection = (HttpsURLConnection) url
                        .openConnection();
                urlConnection.setSSLSocketFactory(context
                        .getSocketFactory());
                InputStream in = urlConnection.getInputStream();

                return true;
            } catch (Exception e) {
                e.printStackTrace();
                error = e;
                return false;
            }
        }

        @Override
        protected void onPostExecute(Boolean valid) {
            if (error != null) {
                Toast.makeText(ctx, "Error: " + error.getMessage(),
                        Toast.LENGTH_LONG).show();
                return;
            }
            Toast.makeText(ctx, "Success: ", Toast.LENGTH_SHORT).show();
        }
    }.execute();

}

protected void choseCertificate() {
    KeyChain.choosePrivateKeyAlias(this, this,
            new String[] { "RSA", "DSA" }, null, "m.ergon.ch", 443, null);
}

@Override
public void alias(String alias) {
    this.alias = alias;
}
}

The Exception is thrown at urlConnection.getInputStream();

Here is the capture of the handshake between the server and the client. Network capture of the SSL Handshake

Thanks for any suggestions and tipps.

like image 718
Michael Klenk Avatar asked Nov 23 '12 08:11

Michael Klenk


People also ask

What is client authentication in SSL?

Client authentication provides additional authentication and access control by checking client certificates at the server. This support prevents a client from obtaining a connection without an installation approved certificate.

How do I get SSL client authentication certificate?

Create a client certificate request. After receiving the certificate, export it to a password-protected PKCS12 file and send the password and the file to the user. Make sure the file is securely sent.

How do I enable SSL client?

Log into the SSL VPN web interface. Go to the Manage System > ACCESS CONTROL > Security Settings page. In the Client Certificates section, configure the client certificates settings. Click Save Changes.


1 Answers

You are never initializing a KeyManager with your private key, so there is no way client authentication can pick it up.

You'd have to implement X509KeyManager to return your PrivateKey and some hard-coded alias. Here's the one from the stock Email application (ICS+) for reference. You may need to modify it somewhat, but it should be easy to follow: basically it just saves the key, alias and certificate chain to fields and returns them via the appropriate methods (StubKeyManager just throws exceptions for the unimplemented and unneeded methods):

public static class KeyChainKeyManager extends StubKeyManager {
    private final String mClientAlias;
    private final X509Certificate[] mCertificateChain;
    private final PrivateKey mPrivateKey;

    public static KeyChainKeyManager fromAlias(Context context, String alias)
            throws CertificateException {
        X509Certificate[] certificateChain;
        try {
            certificateChain = KeyChain.getCertificateChain(context, alias);
        } catch (KeyChainException e) {
            logError(alias, "certificate chain", e);
            throw new CertificateException(e);
        } catch (InterruptedException e) {
            logError(alias, "certificate chain", e);
            throw new CertificateException(e);
        }

        PrivateKey privateKey;
        try {
            privateKey = KeyChain.getPrivateKey(context, alias);
        } catch (KeyChainException e) {
            logError(alias, "private key", e);
            throw new CertificateException(e);
        } catch (InterruptedException e) {
            logError(alias, "private key", e);
            throw new CertificateException(e);
        }

        if (certificateChain == null || privateKey == null) {
            throw new CertificateException("Can't access certificate from keystore");
        }

        return new KeyChainKeyManager(alias, certificateChain, privateKey);
    }

    private KeyChainKeyManager(
            String clientAlias, X509Certificate[] certificateChain, 
            PrivateKey privateKey) {
        mClientAlias = clientAlias;
        mCertificateChain = certificateChain;
        mPrivateKey = privateKey;
    }


    @Override
    public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
         return mClientAlias;
    }

    @Override
    public X509Certificate[] getCertificateChain(String alias) {
          return mCertificateChain;
    }

    @Override
    public PrivateKey getPrivateKey(String alias) {
            return mPrivateKey;
    }
}
like image 141
Nikolay Elenkov Avatar answered Sep 28 '22 21:09

Nikolay Elenkov