0

I've previously asked this question, but received no help. I've made some progress but am stuck again with a new error.

I must make use of a secure device which keeps key pairs and CA certificates save. I must now use this device when the application makes a TLS connection to a server which requires mutual authentication. I have a class "KeyPair" which is able to communicate with the secure device.

My problem is that when the ssl connection is made I get an error "tlsv1 alert unknown ca" on the client and "SSL3_GET_CLIENT_CERTIFICATE:no certificate returned" on the server. My debug logs show that the SSL context does query my object as the trust manager. First my checkServerTrusted() function is called which passes. Next the chooseClientAlias() function is called followed by the getPrivateKey() function with the client alias. At this point I extract the private key from the device and return it. Next the function getCertificateChain() is called and I get and return the matching certificate and its signer CA in an array. My debug output shows the certs being returned as:

Subject DN: CN=be2576a357228b303189ab62bd2497807d2276493ddfc6fd037a0c8fc6e9ac9a,C=YY,E=a@b.com
Issuer DN: O=myCompany,E=email@mydomain.net,L=myCity,ST=myState,C=XX,CN=LJB
Serial Num = 3877882041

Subject DN = O=myCompany,E=email@mydomain.net,L=myCity,ST=myState,C=XX,CN=LJB
Issuer DN  = O=myCompany,E=email@mydomain.net,L=myCity,ST=myState,C=XX,CN=LJB
Serial Num = 10069430368951295741

The SSL connection then pauses before failing with the above mentioned error.

I've extended the "KeyPair" class to act as a key and trust manager like this:

public class Keypair implements X509KeyManager, X509TrustManager

In this class I override all the functions for these managers to see what gets called when. My application will always be a client working to a server.

So during the creation of this class object and after all the tests of a key pair on the device is successful I create a SSL context which makes use of the particular key pair. For now there is only one key pair on the device. I use,...

sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(new KeyManager[] {this}, new TrustManager[] {this}, new SecureRandom());

Later a task will use this object to create a connection to the server

protected boolean Connect(String host, int port) {
    SSLSocketFactory factory = getKeypair().getSecureSocketFactory();
    socket = (SSLSocket) factory.createSocket(host, port);
    :
}

where the getSecureSocketFactory() function simply return sslCtx.getSocketFactory() from the keypair class object.

I've search a lot and seen code which create factories etc., but none has been able to help me. I do not want to create any key stores - that is the purpose of the device.

So to which 'CA' does the error refer? The server certificate is also directly signed by the above CA. The connection works if I do not require the client to authenticate. I am testing on a phone with Android 4.1.2.

Follow up: I am sure my problem lies with my getCertificateChain() function. I simply include the public and CA certificates which I retrieve from the secure device. It should never make use of any certificate included with Android. So, can anybody give me some pointers on the rules about returning my chain? Do I understand 'chain' correct: This must be the certificate which matches the private key as well as all certificates up to the main CA (self signed)?"

tshepang
  • 12,111
  • 21
  • 91
  • 136
LJB
  • 47
  • 9
  • **'tlsv1 alert unknown ca'** while trying to validate the server certificate. CA refers to the certificate of the Server. – Tobrun Mar 08 '14 at 13:14
  • The server certificate is also directly signed by the above CA, which CA? – Tobrun Mar 08 '14 at 13:15
  • The CA certificate is the one shown in my question with the same subject and issuer. It has signed the certificate on my device and the server certificate. – LJB Mar 08 '14 at 13:25
  • The CA listed above doesnt't seem to be a valid one that is packaged with the OS. The CA should be known on device before it can be resolved. See [here][1] to get a list of the CA's currently available for your device. You can manually add own CA's by configuring these in the security setting of your device. [1]: http://stackoverflow.com/questions/3777058/list-of-trusted-ca-in-android – Tobrun Mar 08 '14 at 13:50
  • That is the point: The CA is also read from the security device which holds the key pair. My trustmanager must use it. The application does not ever make use of default installed CAs or certificates. – LJB Mar 08 '14 at 14:05
  • ...and this is where I think I am making a mistake. I think my application is still trying to access the default installed CAs on android instead of my CA in my TrustManager (hence the message 'unknown ca'). Yet I do see calls to my TrustManager. Don't know. – LJB Mar 08 '14 at 15:24

1 Answers1

0

To set up your TrustManager, I used some utility functions I found somewhere:

  void trustCertificate(X509Certificate cert) {
        if (cert!=null) {
            try {
                KeyStore.TrustedCertificateEntry x = new KeyStore.TrustedCertificateEntry(cert);
                sslKeystore.setEntry(cert.getSubjectDN().getName(), x, null);
            } catch (KeyStoreException e) {
                e.printStackTrace();
            }
            saveKeystore();
        }
    }

    private void createKeystore() {
        try {
            sslKeystore.load(null,null);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init((KeyStore)null);
            // Copy current certs into our keystore so we can use it...
            // TODO: don't actually do this...
            X509TrustManager xtm = (X509TrustManager) tmf.getTrustManagers()[0];
            for (X509Certificate cert : xtm.getAcceptedIssuers()) {
                sslKeystore.setCertificateEntry(cert.getSubjectDN().getName(), cert);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        saveKeystore();
    }

    private void saveKeystore() {
        try {
            sslKeystore.store(new FileOutputStream(keystoreFile), KEYSTORE_PASSWORD.toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

and from the calling class:

 CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream caInput = new BufferedInputStream(cafile);
            Certificate ca = null;
            try {
                ca = cf.generateCertificate(caInput);

            } catch(Exception e) {

            }
            finally {
                caInput.close();
            }
            certManagerCA.trustCertificate((X509Certificate) ca);
            KeyStore keyStoreCA = certManagerCA.sslKeystore;
            tmf = TrustManagerFactory.getInstance("X509");
            tmf.init(keyStoreCA);
Derek
  • 11,715
  • 32
  • 127
  • 228