[英]How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain
I'm writing an Android app that requires SSL client authentication. 我正在编写一个需要SSL客户端身份验证的Android应用。 I know how to create a JKS keystore for a desktop Java application, but Android only supports the BKS format.
我知道如何为桌面Java应用程序创建JKS密钥库,但Android仅支持BKS格式。 Every way I've tried to create the keystore results in the following error:
我试图创建密钥库的每一种方式都会导致以下错误:
handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
So it looks like the client is never sending a proper certificate chain, probably because I'm not creating the keystore properly. 所以看起来客户端永远不会发送正确的证书链,可能是因为我没有正确创建密钥库。 I'm unable to enable SSL debugging like I can on the desktop, so that's making this much more difficult than it should be.
我无法在桌面上启用SSL调试,因此这使得它比应该更加困难。
For reference the following is the command that IS working to create a BKS truststore : 作为参考,以下是IS用于创建BKS 信任库的命令:
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
Here is the command I've tried that is NOT working to create a BKS client keystore : 这是我尝试过的命令,它无法创建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
Detailed Step by Step instructions I followed to achieve this 为实现这一目标,我遵循详细的逐步说明
Configure BouncyCastle for TOMCAT 为TOMCAT配置BouncyCastle
Open D:\\tools\\apache-tomcat-6.0.35\\conf\\server.xml and add the following entry 打开D:\\ tools \\ apache-tomcat-6.0.35 \\ conf \\ server.xml并添加以下条目
Restart the server after these changes. 在这些更改后重新启动服务器。
MyHttpClient.java 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);
}
}
}
How to invoke the above code in your Activity class: 如何在Activity类中调用上面的代码:
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);
我使用Portecle ,它就像一个魅力。
I don't think your problem is with the BouncyCastle keystore; 我认为您的问题不在于BouncyCastle密钥库; I think the problem is with a broken javax.net.ssl package in Android.
我认为问题在于Android中破坏的javax.net.ssl包。 The BouncyCastle keystore is a supreme annoyance because Android changed a default Java behavior without documenting it anywhere -- and removed the default provider -- but it does work.
BouncyCastle密钥库是一个至高无上的烦恼,因为Android更改了默认的Java行为,而没有在任何地方记录它 - 并删除了默认提供程序 - 但它确实有效。
Note that for SSL authentication you may require 2 keystores. 请注意,对于SSL身份验证,您可能需要2个密钥库。 The "TrustManager" keystore, which contains the CA certs, and the "KeyManager" keystore, which contains your client-site public/private keys.
“TrustManager”密钥库(包含CA证书)和“KeyManager”密钥库(包含客户端站点公钥/私钥)。 (The documentation is somewhat vague on what needs to be in the KeyManager keystore.) In theory, you shouldn't need the TrustManager keystore if all of your certficates are signed by "well-known" Certifcate Authorities, eg, Verisign, Thawte, and so on.
(文档对于KeyManager密钥库中需要的内容有些模糊。)理论上,如果所有证书都由“众所周知的”证书颁发机构签署,例如Verisign,Thawte,则不需要TrustManager密钥库。等等。 Let me know how that works for you.
让我知道这对你有用。 Your server will also require the CA for whatever was used to sign your client.
您的服务器还将要求CA用于签署客户端的任何内容。
I could not create an SSL connection using javax.net.ssl at all. 我根本无法使用javax.net.ssl创建SSL连接。 I disabled the client SSL authentication on the server side, and I still could not create the connection.
我在服务器端禁用了客户端SSL身份验证,但仍然无法创建连接。 Since my end goal was an HTTPS GET, I punted and tried using the Apache HTTP Client that's bundled with Android.
由于我的最终目标是HTTPS GET,因此我尝试使用与Android捆绑在一起的Apache HTTP客户端。 That sort-of worked.
那种工作。 I could make the HTTPS conection, but I still could not use SSL auth.
我可以进行HTTPS连接,但我仍然无法使用SSL身份验证。 If I enabled the client SSL authentication on my server, the connection would fail.
如果我在服务器上启用了客户端SSL身份验证,则连接将失败。 I haven't checked the Apache HTTP Client code, but I suspect they are using their own SSL implementation, and don't use javax.net.ssl.
我没有检查Apache HTTP客户端代码,但我怀疑他们正在使用自己的SSL实现,并且不使用javax.net.ssl。
Not sure you resolved this issue or not, but this is how I do it and it works on Android: 不确定您是否解决了这个问题,但这是我如何做到的,它适用于Android:
命令行:
keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS
Use this manual http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/ This guide really helped me. 使用本手册http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/本指南对我很有帮助 。 It is important to observe a sequence of certificates in the store.
在商店中观察一系列证书非常重要。 For example: import the lowermost Intermediate CA certificate first and then all the way up to the Root CA certificate .
例如:首先导入最低级的中级CA证书,然后一直导入根CA证书 。
Your command for creating the BKS keystore looks correct for me. 您创建BKS密钥库的命令对我来说是正确的。
How do you initialize the keystore. 你如何初始化密钥库。
You need to craeate and pass your own SSLSocketFactory. 你需要创建并传递自己的SSLSocketFactory。 Here is an example which uses Apache's
org.apache.http.conn.ssl.SSLSocketFactory
这是一个使用Apache的
org.apache.http.conn.ssl.SSLSocketFactory
的示例
But I think you can do pretty the same on the javax.net.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);
}
}
Please let me know if it worked. 如果有效,请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.