如何创build包含客户端证书链的BKS(BouncyCastle)格式的Java Keystore
我正在编写一个需要SSL客户端身份validation的Android应用程序。 我知道如何为桌面Java应用程序创build一个JKS密钥库,但是Android只支持BKS格式。 我试图创build密钥库结果的每一种方法导致以下错误:
handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
所以看起来客户端永远不会发送正确的证书链,可能是因为我没有正确地创build密钥存储。 我无法启动SSLdebugging,就像我可以在dekstop上一样,所以这使得这比应该更困难。
以下是创buildBKS 信任库的命令:
keytool -importcert -v -trustcacerts -file "cacert.pem" -alias ca -keystore "mySrvTruststore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest
这是我试过的命令不能创build一个BKS客户机密钥库 :
cat clientkey.pem clientcert.pem cacert.pem > client.pem keytool -import -v -file <(openssl x509 -in client.pem) -alias client -keystore "clientkeystore" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest
我详细的一步一步的指示,以实现这一点
- 从http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar下载bouncycastle JAR,或者从“doc”文件夹下载。
- 使用以下方法之一configurationPC的BouncyCastle。
- 静态添加BC提供者(推荐)
- 将bcprov-ext-jdk15on-1.46.jar复制到每个
- D:\ tools \ jdk1.5.0_09 \ jre \ lib \ ext(JDK(捆绑的JRE)
- D:\ tools \ jre1.5.0_09 \ lib \ ext(JRE)
- C:\(要在envvariables中使用的位置)
- 修改下的java.security文件
- d:\工具\ jdk1.5.0_09 \ JRE \ lib \ security中
- d:\工具\ jre1.5.0_09 \ lib \ security中
- 并添加以下条目
- security.provider.7 = org.bouncycastle.jce.provider.BouncyCastleProvider
- 在“用户variables”部分添加以下环境variables
- CLASSPATH =%CLASSPATH%; C:\ bcprov-EXT-jdk15on-1.46.jar
- 将bcprov-ext-jdk15on-1.46.jar复制到每个
- 将bcprov-ext-jdk15on-1.46.jar添加到项目的CLASSPATH中,并在代码中添加以下行
- Security.addProvider(new BouncyCastleProvider());
- 静态添加BC提供者(推荐)
- 使用Bouncy Castle生成密钥库
- 运行以下命令
- keytool -genkey -alias myproject -keystore C:/myproject.keystore -storepass myproject -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
- 这将生成文件C:\ myproject.keystore
- 运行以下命令来检查是否正确生成
- keytool -list -keystore C:\ myproject.keystore -storetype BKS
- 运行以下命令
-
为TOMCATconfigurationBouncyCastle
-
打开D:\ tools \ apache-tomcat-6.0.35 \ conf \ server.xml并添加以下条目
- 协议=“HTTP / 1.1”scheme<connection port =“8443”keystorePass =“myproject”alias =“myproject”keystore =“c:/myproject.keystore”keystoreType =“BKS”SSLEnabled =“true” =“https”secure =“true”sslProtocol =“TLS”sslImplementationName =“org.bouncycastle.jce.provider.BouncyCastleProvider”/>
-
这些更改后重新启动服务器。
-
- 为Android客户端configurationBouncyCastle
- 无需configuration,因为Android在提供的“android.jar”内部支持Bouncy Castle Version 1.46。
- 只要实现你的HTTP Client版本(MyHttpClient.java可以在下面find)并在代码中设置以下内容
- SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
- 如果你不这样做,它会给出一个例外,如下所示
- javax.net.ssl.SSLException:证书中的主机名不匹配:<192.168.104.66>!=
- 在生产模式下,将上面的代码更改为
- SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
MyHttpClient.java
package com.arisglobal.aglite.network; import java.io.InputStream; import java.security.KeyStore; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.SingleClientConnManager; import com.arisglobal.aglite.activity.R; import android.content.Context; public class MyHttpClient extends DefaultHttpClient { final Context context; public MyHttpClient(Context context) { this.context = context; } @Override protected ClientConnectionManager createClientConnectionManager() { SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); // Register for port 443 our SSLSocketFactory with our keystore to the ConnectionManager registry.register(new Scheme("https", newSslSocketFactory(), 443)); return new SingleClientConnManager(getParams(), registry); } private SSLSocketFactory newSslSocketFactory() { try { // Get an instance of the Bouncy Castle KeyStore format KeyStore trusted = KeyStore.getInstance("BKS"); // Get the raw resource, which contains the keystore with your trusted certificates (root and any intermediate certs) InputStream in = context.getResources().openRawResource(R.raw.aglite); try { // Initialize the keystore with the provided trusted certificates. // Also provide the password of the keystore trusted.load(in, "aglite".toCharArray()); } finally { in.close(); } // Pass the keystore to the SSLSocketFactory. The factory is responsible for the verification of the server certificate. SSLSocketFactory sf = new SSLSocketFactory(trusted); // Hostname verification from certificate // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506 sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); return sf; } catch (Exception e) { throw new AssertionError(e); } } }
如何在Activity类中调用上面的代码:
DefaultHttpClient client = new MyHttpClient(getApplicationContext()); HttpResponse response = client.execute(...);
我使用Portecle ,它像一个魅力。
我不认为你的问题是与BouncyCastle密钥库; 我认为这个问题是由Android中的javax.net.ssl包破坏的。 BouncyCastle密钥库是一个极大的烦恼,因为Android改变了默认的Java行为而没有logging任何地方 – 并删除了默认的提供者 – 但它确实有效。
请注意,对于SSL身份validation,您可能需要2个密钥库。 包含CA证书的“TrustManager”密钥库和包含客户端公钥/私钥的“KeyManager”密钥库。 (该文档在KeyManager密钥库中需要的内容有点模糊。)理论上,如果您的所有证书都由“知名”证书颁发机构签名,则不需要TrustManager密钥库,例如Verisign,Thawte,等等。 让我知道这对你有用。 您的服务器还需要CA用于签署客户端的任何内容。
我根本无法使用javax.net.ssl创buildSSL连接。 我禁用了服务器端的客户端SSLauthentication,而且我仍然无法创build连接。 由于我的最终目标是HTTPS GET,因此我尝试使用与Android捆绑在一起的Apache HTTP Client。 那种工作。 我可以使HTTPS连接,但我仍然无法使用SSL身份validation。 如果我在我的服务器上启用了客户端SSL身份validation,则连接将失败。 我没有检查Apache HTTP客户端代码,但我怀疑他们正在使用他们自己的SSL实现,并且不使用javax.net.ssl。
不知道你是否解决了这个问题,但这是我如何做,它适用于Android:
- 使用openssl将客户端的证书(证书必须由服务器接受的CA签名)和私钥合并为PCKS12格式的密钥对: openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
- 您可能需要将您的JRE打补丁到强度encryption取决于您的关键优势:从JCE 5.0复制JESS的文件无限强度Jurisdiction策略文件并覆盖JRE中的文件(例如:C:\ Program Files \ Java \ jre6 \ lib \ security )
- 使用上面提到的Portecle工具并用BKS格式创build一个新的密钥库
- 导入步骤1中生成的PCKS12密钥对并保存为BKS密钥库。 此密钥库适用于Android客户端身份validation。
- 如果您需要执行证书链,则可以使用此IBM工具: KeyMan将客户端的PCKS12密钥对与CA证书进行合并。 但是它只生成JKS密钥库,所以你再次需要Protecle把它转换成BKS格式。
命令行:
keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS
您创buildBKS密钥库的命令对我来说看起来是正确的。
你如何初始化密钥库。
您需要craeate并传递您自己的SSLSocketFactory。 这是一个使用Apache的org.apache.http.conn.ssl.SSLSocketFactory
的例子
但是我认为你可以在javax.net.ssl.SSLSocketFactory
上做相同的事情
private SSLSocketFactory newSslSocketFactory() { try { // Get an instance of the Bouncy Castle KeyStore format KeyStore trusted = KeyStore.getInstance("BKS"); // Get the raw resource, which contains the keystore with // your trusted certificates (root and any intermediate certs) InputStream in = context.getResources().openRawResource(R.raw.mykeystore); try { // Initialize the keystore with the provided trusted certificates // Also provide the password of the keystore trusted.load(in, "testtest".toCharArray()); } finally { in.close(); } // Pass the keystore to the SSLSocketFactory. The factory is responsible // for the verification of the server certificate. SSLSocketFactory sf = new SSLSocketFactory(trusted); // Hostname verification from certificate // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506 sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); return sf; } catch (Exception e) { throw new AssertionError(e); } }
请让我知道,如果它的工作。
使用本手册http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/本指南的确帮助了我。; 在商店中观察一系列证书是很重要的。 例如:先导入最下面的中间CA证书,然后导入根CA证书 。