Now I have followed this tutorial for generate clientAuth keys: http://www.maximporges.com/2009/11/18/configuring-tomcat-ssl-clientserver-authentication/
The only difference is that I don't use the same store for both key and trust store. Now, with this new certificate alias isn't null, I got "clientkey" as alias. Executing your method I got the following log: alias=clientkey privatekey=RSA cert subject=CN=MT Tablet Client, OU=IT, O=CPMAPAVE, L=Bienno, ST=BS, C=IT cert issuer =CN=MT Tablet Client, OU=IT, O=CPMAPAVE, L=Bienno, ST=BS, C=IT -------------------------------- alias=servercert cert subject=CN=MT Tablet Server, OU=IT, O=CPMAPAVE, L=Bienno, ST=BS, C=IT cert issuer =CN=MT Tablet Server, OU=IT, O=CPMAPAVE, L=Bienno, ST=BS, C=IT But now I got I/O error: Fatal alert received bad_certificate; nested exception is javax.net.ssl.SSLException: Fatal alert received bad_certificate 2012/8/27 Brian Carlstrom <[email protected]> > I would use something like this to dump information from your KeyStore > instance. you could also just print the toString of the certs for more > general info, or just the "getSigAlgName" to see what algorithm was > used. > > -bri > > Enumeration<String> e = keyStore.aliases(); > while (e.hasMoreElements()) { > String alias = e.nextElement(); > System.out.println("--------------------------------"); > System.out.println("alias=" + alias); > if (keyStore.entryInstanceOf(alias, > KeyStore.PrivateKeyEntry.class)) { > System.out.println("privatekey=" + > keyStore.getKey(alias, keyStorePassword).getAlgorithm()); > for (Certificate certificate : > keyStore.getCertificateChain(alias)) { > X509Certificate cert = (X509Certificate) certificate; > System.out.println("cert subject=" + > cert.getSubjectX500Principal()); > System.out.println("cert issuer =" + > cert.getIssuerX500Principal()); > } > } else if (keyStore.entryInstanceOf(alias, > KeyStore.SecretKeyEntry.class)) { > System.out.println("secretkey=" + > keyStore.getKey(alias, keyStorePassword).getAlgorithm()); > } else if (keyStore.entryInstanceOf(alias, > KeyStore.TrustedCertificateEntry.class)) { > Certificate certificate = keyStore.getCertificate(alias); > X509Certificate cert = (X509Certificate) certificate; > System.out.println("cert subject=" + > cert.getSubjectX500Principal()); > System.out.println("cert issuer =" + > cert.getIssuerX500Principal()); > } > } > > > On Sun, Aug 26, 2012 at 11:38 PM, Marco Serioli <[email protected]> > wrote: > > Yes, chooseClientAlias returned null! > > > > I think that may be a problem with my certificate. > > > > I check it and let you know! > > Marco > > > > > > 2012/8/24 Brian Carlstrom <[email protected]> > >> > >> yes, basically during the handshake if a client cert is requested, the > >> chooseClientAlias is called to select one. the selected key is > >> specified by the returned String alias. Then it calls back with that > >> alias to lookup the private key and certs. Since you are receiving a > >> null in getPrivateKey, that seems to imply that chooseClientAlias > >> returned null. Can you confirm that? > >> > >> So the next question is why it can't choose one. the arguments are > >> used to filter the KeyStore contents. so it will look for an RSA or > >> DSA cert, issued by one of the specified issuers. I will say that the > >> issuers list is concerning. typically servers don't actually send that > >> and if the server is sending something bogus, it might be over > >> constraining the chooseClientAlias function. However, you can of > >> course workaround by having your proxy implement your own logic for > >> selecting the alias to use from the KeyStore, even if you can't change > >> the server configuration. > >> > >> -bri > >> > >> On Fri, Aug 24, 2012 at 3:04 AM, Marco Serioli <[email protected]> > wrote: > >> > I've tryed to implement X509KeyManager in my own MyX509KeyManager > class: > >> > > >> > class MyX509KeyManager implements X509KeyManager { > >> > > >> > private X509KeyManager defaultKeyManager; > >> > public MyX509KeyManager(KeyManager[] keyManagers){ > >> > for (KeyManager keyManager : keyManagers){ > >> > if (keyManager instanceof X509KeyManager){ > >> > defaultKeyManager = (X509KeyManager) keyManager; > >> > } > >> > } > >> > > >> > } > >> > @Override > >> > public String chooseClientAlias(String[] keyType, Principal[] issuers, > >> > Socket socket) { > >> > return defaultKeyManager.chooseClientAlias(keyType, issuers, socket); > >> > } > >> > > >> > @Override > >> > public String chooseServerAlias(String keyType, Principal[] issuers, > >> > Socket > >> > socket) { > >> > return defaultKeyManager.chooseServerAlias(keyType, issuers, socket); > >> > } > >> > > >> > @Override > >> > public X509Certificate[] getCertificateChain(String alias) { > >> > return defaultKeyManager.getCertificateChain(alias); > >> > } > >> > > >> > @Override > >> > public String[] getClientAliases(String keyType, Principal[] issuers) > { > >> > return defaultKeyManager.getClientAliases(keyType, issuers); > >> > } > >> > > >> > @Override > >> > public PrivateKey getPrivateKey(String alias) { > >> > return defaultKeyManager.getPrivateKey("tomcat"); > >> > } > >> > > >> > @Override > >> > public String[] getServerAliases(String keyType, Principal[] issuers) > { > >> > return defaultKeyManager.getServerAliases(keyType, issuers); > >> > } > >> > } > >> > > >> > > >> > And to do: > >> > > >> > final KeyManagerFactory kmf = > >> > > KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); > >> > kmf.init(keyStore, keyStorePassword.toCharArray()); > >> > KeyManager keyManager = new MyX509KeyManager(kmf.getKeyManagers()); > >> > > >> > KeyManager[] keyManagerArray = kmf.getKeyManagers(); > >> > keyManagerArray[0] = keyManager; > >> > sslCtx.init(keyManagerArray, tmf.getTrustManagers(), new > >> > SecureRandom()); > >> > > >> > During debugging I can see that first call is on > >> > > >> > public String chooseClientAlias(String[] keyType, Principal[] issuers, > >> > Socket socket) > >> > > >> > called with parameter > >> > > >> > keyType[0]="RSA" > >> > keyType[1]="DSA" > >> > > >> > issuers[0] ->X500Principal -> toString() = > >> > > >> > > OID.1.2.840.113549.1.9.1=#16156D2E736572696F6C694063706D61706176652E6974, > >> > CN=www.apaveitaliacpm.it, OU=IT, O=Apave Italia CPM, L=Bienno, > >> > ST=Brescia, > >> > C=IT > >> > issuers[1] ->X500Principal -> toString() = CN=Apave Italia CPM, OU=IT, > >> > O=Apave Italia CPM, L=Bienno, ST=Brescia, C=IT > >> > > >> > socket = SSL socket over > >> > Socket[addr=192.168.168.13/192.168.168.13,port=8443,localport=48330] > >> > > >> > The second method called is: > >> > > >> > public PrivateKey getPrivateKey(String alias) > >> > > >> > > >> > where I have noticed that alias parameter is null. > >> > > >> > Could it be a problem? > >> > > >> > Il giorno venerdì 24 agosto 2012 07:49:19 UTC+2, Brian Carlstrom ha > >> > scritto: > >> >> > >> >> Well, the non-OpenSSL provider seems to be hitting another issue > >> >> > >> >> http://code.google.com/p/android/issues/detail?id=31903 > >> >> > >> >> that is not fixed even it the 4.1 release. However, the nature of > that > >> >> bug seems to indicate the problem, that no client certificate was > >> >> returned by the KeyManager. > >> >> > >> >> I'd advise writing a proxy X509KeyManager > >> >> > >> >> ( > http://developer.android.com/reference/javax/net/ssl/X509KeyManager.html) > >> >> as a wrapper around the result returned from kmf.getKeyManagers(). > >> >> getKeyManagers is going to return a length 1 array with a > >> >> X509KeyManager. Just replace the element with your own that > implements > >> >> each method by just logging and then calling through the original > one. > >> >> Then we can see if your key manager really is getting called. you can > >> >> do the same with the SSLSocketFactory passed to setSSLSocketFactory > to > >> >> make sure that it really is calling your SSLSocketFactory. > >> >> > >> >> -bri > >> >> > >> >> On Thu, Aug 23, 2012 at 9:36 PM, Marco Serioli <[email protected]> > >> >> wrote: > >> >> > Thank you for your quick answer! > >> >> > > >> >> > I've tried to get the non-OpenSSL provider and then log the error > >> >> > message. > >> >> > Here is the result: > >> >> > > >> >> > java.lang.NullPointerException > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.xnet.provider.jsse.ClientHandshakeImpl.processServerHelloDone(ClientHandshakeImpl.java:515) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.xnet.provider.jsse.ClientHandshakeImpl.unwrap(ClientHandshakeImpl.java:297) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.xnet.provider.jsse.SSLRecordProtocol.unwrap(SSLRecordProtocol.java:408) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.xnet.provider.jsse.SSLSocketImpl.doHandshake(SSLSocketImpl.java:737) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.xnet.provider.jsse.SSLSocketImpl.startHandshake(SSLSocketImpl.java:446) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.setupSecureSocket(HttpConnection.java:167) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:479) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeConnection(HttpsURLConnectionImpl.java:419) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:217) > >> >> > at > >> >> > > >> >> > > >> >> > > org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:177) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:72) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:63) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:91) > >> >> > at > >> >> > > >> >> > > >> >> > > it.cpmapave.mt.interceptors.MyClientHttpRequestInterceptor.intercept(MyClientHttpRequestInterceptor.java:29) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:67) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:63) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:475) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.web.client.RestTemplate.execute(RestTemplate.java:438) > >> >> > at > >> >> > > >> >> > > >> >> > > org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:414) > >> >> > at > >> >> > > >> >> > > it.cpmapave.mt.rest.OrderRestClient_.getOrders(OrderRestClient_.java:58) > >> >> > at > >> >> > > >> >> > > >> >> > > it.cpmapave.mt.ui.MainActivity$FetchSecuredResourceTask.doInBackground(MainActivity.java:144) > >> >> > at > >> >> > > >> >> > > >> >> > > it.cpmapave.mt.ui.MainActivity$FetchSecuredResourceTask.doInBackground(MainActivity.java:1) > >> >> > at android.os.AsyncTask$2.call(AsyncTask.java:252) > >> >> > at > >> >> > java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) > >> >> > at java.util.concurrent.FutureTask.run(FutureTask.java:137) > >> >> > at > >> >> > > >> >> > > >> >> > > java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1081) > >> >> > at > >> >> > > >> >> > > >> >> > > java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:574) > >> >> > at java.lang.Thread.run(Thread.java:1020) > >> >> > > >> >> > Please let me know how to take a tcpdump of the SSL handshake if > you > >> >> > feel it > >> >> > useful to solve the problem! > >> >> > > >> >> > Thank you! > >> >> > Marco > >> >> > > >> >> > 2012/8/24 Brian Carlstrom <[email protected]> > >> >> >> > >> >> >> I'm responsible for the SSLSocket code and more recently for the > >> >> >> HttpURLConnection code. That code does look right to me on a quick > >> >> >> review. One thing you could try to see if you can get a better > >> >> >> diagnostic using the non-OpenSSL provider by saying > >> >> >> SSLContext.getInstance("TLS", "HarmonyJSSE"); > >> >> >> > >> >> >> I'd also try to get a tcpdump of the handshake to see what might > be > >> >> >> going wrong in case the issue is just problem negotiating an > cipher > >> >> >> suite in common between both sides. I think the emulator might > have > >> >> >> tcpdump installed, I could provide some notes on how to run it. > >> >> >> > >> >> >> I might also log with Log.e(TAG, "message", e); so that it will > >> >> >> print > >> >> >> the full stack of the exception in case their is more detail, but > >> >> >> i'm > >> >> >> not expecting much here. I'm really hoping that the other > SSLSocket > >> >> >> impl which give more user friendly messages than OpenSSL > >> >> >> > >> >> >> -bri > >> >> >> > >> >> >> On Thu, Aug 23, 2012 at 4:21 AM, Marco Serioli <[email protected] > > > >> >> >> wrote: > >> >> >> > I'm developing an android application on v13 target sdk and I'm > >> >> >> > trying > >> >> >> > to > >> >> >> > secure connection from android device to my tomcat server v6 > with > >> >> >> > SSL > >> >> >> > enabling also clientAuth. I'm using self-signed certificates. > >> >> >> > > >> >> >> > Only for introduce my project (I think the error is not due to > >> >> >> > this): > >> >> >> > I'm > >> >> >> > using spring-android RestTemplate using a custom > >> >> >> > ClientHttpRequestFactory. > >> >> >> > Because of android sdk version I'm sure that spring will use > >> >> >> > HttpUrlConnection and not HttpClient! So I'm extending > >> >> >> > SimpleclientHttpRequestFactory and overriding the > >> >> >> > openConnectionMethod. > >> >> >> > I > >> >> >> > need to do this to trust my self-signed certificates and to use > my > >> >> >> > client > >> >> >> > authentication certificate! > >> >> >> > > >> >> >> > So I init my sslContext and set to HttpURLConnection in this > way: > >> >> >> > > >> >> >> > private SSLSocketFactory getSSLSocketFactory() throws > >> >> >> > KeyStoreException, > >> >> >> > KeyManagementException, NoSuchAlgorithmException, > >> >> >> > CertificateException, > >> >> >> > IOException, UnrecoverableKeyException{ > >> >> >> > final InputStream trustStoreLocation = > >> >> >> > mContext.getResources().openRawResource(R.raw.trust_store); > >> >> >> > final String trustStorePassword = "........"; > >> >> >> > > >> >> >> > final InputStream keyStoreLocation = > >> >> >> > mContext.getResources().openRawResource(R.raw.key_store); > >> >> >> > final String keyStorePassword = "........"; > >> >> >> > > >> >> >> > final KeyStore trustStore = KeyStore.getInstance("BKS"); > >> >> >> > trustStore.load(trustStoreLocation, > >> >> >> > trustStorePassword.toCharArray()); > >> >> >> > > >> >> >> > final KeyStore keyStore = KeyStore.getInstance("BKS"); > >> >> >> > keyStore.load(keyStoreLocation, > >> >> >> > keyStorePassword.toCharArray()); > >> >> >> > > >> >> >> > final TrustManagerFactory tmf = > >> >> >> > > >> >> >> > > >> >> >> > > >> >> >> > > TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); > >> >> >> > tmf.init(trustStore); > >> >> >> > > >> >> >> > final KeyManagerFactory kmf = > >> >> >> > > >> >> >> > > >> >> >> > > KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); > >> >> >> > kmf.init(keyStore, keyStorePassword.toCharArray()); > >> >> >> > > >> >> >> > final SSLContext sslCtx = SSLContext.getInstance("TLS"); > >> >> >> > sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), > new > >> >> >> > SecureRandom()); > >> >> >> > > >> >> >> > return sslCtx.getSocketFactory(); > >> >> >> > } > >> >> >> > > >> >> >> > @Override > >> >> >> > protected HttpURLConnection openConnection(URL url, Proxy proxy) > >> >> >> > throws > >> >> >> > IOException { > >> >> >> > final HttpURLConnection httpUrlConnection = > >> >> >> > super.openConnection(url, > >> >> >> > proxy); > >> >> >> > if (url.getProtocol().toLowerCase().equals("https")) { > >> >> >> > try { > >> >> >> > > >> >> >> > > >> >> >> > > >> >> >> > > >> >> >> > > ((HttpsURLConnection)httpUrlConnection).setSSLSocketFactory(getSSLSocketFactory()); > >> >> >> > > >> >> >> > ((HttpsURLConnection)httpUrlConnection).setHostnameVerifier(new > >> >> >> > NullHostnameVerifier()); > >> >> >> > } catch (Exception e) { > >> >> >> > if (LogConfig.ERROR_LOGS_ENABLED){ > >> >> >> > Log.e(LOG_TAG, e.getMessage()); > >> >> >> > } > >> >> >> > > >> >> >> > } > >> >> >> > return httpUrlConnection; > >> >> >> > } > >> >> >> > > >> >> >> > private static class NullHostnameVerifier implements > >> >> >> > HostnameVerifier > >> >> >> > { > >> >> >> > public boolean verify(String hostname, SSLSession session) { > >> >> >> > return true; > >> >> >> > } > >> >> >> > } > >> >> >> > > >> >> >> > When tomcat clientAuth is disabled it works fine. > >> >> >> > > >> >> >> > But when tomcat client authentication is enabled, trying to > >> >> >> > establish > >> >> >> > connection from android device I got this exception: > >> >> >> > > >> >> >> > error:140943F2:SSL routines:SSL3_READ_BYTES:sslv3 alert > unexpected > >> >> >> > message > >> >> >> > (external/openssl/ssl/s3_pkt.c:1232 0x19bf40:0x00000003); nested > >> >> >> > exception > >> >> >> > is javax.net.ssl.SSLProtocolException: SSL handshake terminated: > >> >> >> > ssl=0x182c70: Failure in SSL library, usually a protocol error > >> >> >> > > >> >> >> > I've tryed to install the client certificate on my web browser > for > >> >> >> > testing > >> >> >> > purpose and everything goes ok! So I think it's a problem of my > >> >> >> > android > >> >> >> > application! > >> >> >> > > >> >> >> > Have you ever got this kind of exception? > >> >> >> > > >> >> >> > -- > >> >> >> > You received this message because you are subscribed to the > Google > >> >> >> > Groups > >> >> >> > "Android Security Discussions" group. > >> >> >> > To view this discussion on the web visit > >> >> >> > > >> >> >> > > >> >> >> > > https://groups.google.com/d/msg/android-security-discuss/-/82sSkozTixAJ. > >> >> >> > To post to this group, send email to > >> >> >> > [email protected]. > >> >> >> > To unsubscribe from this group, send email to > >> >> >> > [email protected]. > >> >> >> > For more options, visit this group at > >> >> >> > http://groups.google.com/group/android-security-discuss?hl=en. > >> >> > > >> >> > > >> > > >> > -- > >> > You received this message because you are subscribed to the Google > >> > Groups > >> > "Android Security Discussions" group. > >> > To view this discussion on the web visit > >> > > https://groups.google.com/d/msg/android-security-discuss/-/PT7WLNR-HJkJ. > >> > > >> > To post to this group, send email to > >> > [email protected]. > >> > To unsubscribe from this group, send email to > >> > [email protected]. > >> > For more options, visit this group at > >> > http://groups.google.com/group/android-security-discuss?hl=en. > > > > > -- You received this message because you are subscribed to the Google Groups "Android Security Discussions" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/android-security-discuss?hl=en.
