如何创建包含客户端证书链的 BKS(BouncyCastle)格式的 Java Keystore
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4065379/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain
提问by Ben Baron
I'm writing an Android app that requires SSL client authentication. I know how to create a JKS keystore for a desktop Java application, but Android only supports the BKS format. Every way I've tried to create the keystore results in the following error:handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
我正在编写一个需要 SSL 客户端身份验证的 Android 应用程序。我知道如何为桌面 Java 应用程序创建 JKS 密钥库,但 Android 仅支持 BKS 格式。我尝试创建密钥库的每一种方式都会导致以下错误: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: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
以下是用于创建 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
采纳答案by Vipul
Detailed Step by Step instructions I followed to achieve this
我为实现这一目标而遵循的详细分步说明
- Download bouncycastle JAR from http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jaror take it from the "doc" folder.
- Configure BouncyCastle for PC using one of the below methods.
- Adding the BC Provider Statically (Recommended)
- Copy the bcprov-ext-jdk15on-1.46.jar to each
- D:\tools\jdk1.5.0_09\jre\lib\ext (JDK (bundled JRE)
- D:\tools\jre1.5.0_09\lib\ext (JRE)
- C:\ (location to be used in env variable)
- Modify the java.security file under
- D:\tools\jdk1.5.0_09\jre\lib\security
- D:\tools\jre1.5.0_09\lib\security
- and add the following entry
- security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
- Add the following environment variable in "User Variables" section
- CLASSPATH=%CLASSPATH%;c:\bcprov-ext-jdk15on-1.46.jar
- Copy the bcprov-ext-jdk15on-1.46.jar to each
- Add bcprov-ext-jdk15on-1.46.jar to CLASSPATH of your project and Add the following line in your code
- Security.addProvider(new BouncyCastleProvider());
- Adding the BC Provider Statically (Recommended)
- Generate the Keystore using Bouncy Castle
- Run the following command
- keytool -genkey -alias myproject -keystore C:/myproject.keystore -storepass myproject -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
- This generates the file C:\myproject.keystore
- Run the following command to check if it is properly generated or not
- keytool -list -keystore C:\myproject.keystore -storetype BKS
- Run the following command
Configure BouncyCastle for TOMCAT
Open D:\tools\apache-tomcat-6.0.35\conf\server.xml and add the following entry
- <Connector port="8443" keystorePass="myproject" alias="myproject" keystore="c:/myproject.keystore" keystoreType="BKS" SSLEnabled="true" clientAuth="false" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS" sslImplementationName="org.bouncycastle.jce.provider.BouncyCastleProvider"/>
Restart the server after these changes.
- Configure BouncyCastle for Android Client
- No need to configure since Android supports Bouncy Castle Version 1.46 internally in the provided "android.jar".
- Just implement your version of HTTP Client (MyHttpClient.java can be found below) and set the following in code
- SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
- If you don't do this, it gives an exception as below
- javax.net.ssl.SSLException: hostname in certificate didn't match: <192.168.104.66> !=
- In production mode, change the above code to
- SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
- 从http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar下载 bouncycastle JAR 或从“doc”文件夹中获取它。
- 使用以下方法之一为 PC 配置 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:\(在 env 变量中使用的位置)
- 修改下的java.security文件
- D:\tools\jdk1.5.0_09\jre\lib\security
- D:\tools\jre1.5.0_09\lib\security
- 并添加以下条目
- security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
- 在“用户变量”部分添加以下环境变量
- 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
- 运行以下命令
为 TOMCAT 配置 BouncyCastle
打开 D:\tools\apache-tomcat-6.0.35\conf\server.xml 并添加以下条目
- <Connector port="8443" keystorePass="myproject" alias="myproject" keystore="c:/myproject.keystore" keystoreType="BKS" SSLEnabled="true" clientAuth="false" protocol="HTTP/1.1" 方案="https" secure="true" sslProtocol="TLS" sslImplementationName="org.bouncycastle.jce.provider.BouncyCastleProvider"/>
在这些更改后重新启动服务器。
- 为 Android 客户端配置 BouncyCastle
- 无需配置,因为 Android 在提供的“android.jar”中支持 Bouncy Castle 1.46 版。
- 只需实现您的 HTTP 客户端版本(MyHttpClient.java 可以在下面找到)并在代码中设置以下内容
- SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
- 如果你不这样做,它会给出一个例外如下
- javax.net.ssl.SSLException:证书中的主机名不匹配:<192.168.104.66> !=
- 在生产模式下,将上面的代码更改为
- SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
MyHttpClient.java
我的HttpClient.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(...);
回答by saxos
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. Here is an example which uses Apache's org.apache.http.conn.ssl.SSLSocketFactory
您需要创建并传递您自己的 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.
请让我知道它是否有效。
回答by Cthulhu
I don't think your problem is with the BouncyCastle keystore; I think the problem is with a broken javax.net.ssl package in Android. 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 中的 javax.net.ssl 包损坏。BouncyCastle 密钥库是一个最大的烦恼,因为 Android 更改了默认的 Java 行为而没有在任何地方记录它——并删除了默认的提供者——但它确实有效。
Note that for SSL authentication you may require 2 keystores. The "TrustManager" keystore, which contains the CA certs, and the "KeyManager" keystore, which contains your client-site public/private keys. (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, e.g., Verisign, Thawte, and so on. Let me know how that works for you. Your server will also require the CA for whatever was used to sign your client.
请注意,对于 SSL 身份验证,您可能需要 2 个密钥库。包含 CA 证书的“TrustManager”密钥库和包含您的客户端站点公钥/私钥的“KeyManager”密钥库。(文档对 KeyManager 密钥库中需要的内容有些含糊。)理论上,如果您的所有证书都由“知名”证书颁发机构(例如 Verisign、Thawte)签名,则您不需要 TrustManager 密钥库。等等。让我知道这对你有什么作用。您的服务器还需要 CA 来为您的客户端签名。
I could not create an SSL connection using javax.net.ssl at all. I disabled the client SSL authentication on the server side, and I still could not create the connection. Since my end goal was an HTTPS GET, I punted and tried using the Apache HTTP Client that's bundled with Android. That sort-of worked. I could make the HTTPS conection, but I still could not use SSL auth. If I enabled the client SSL authentication on my server, the connection would fail. 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.
我根本无法使用 javax.net.ssl 创建 SSL 连接。我在服务器端禁用了客户端 SSL 身份验证,但仍然无法创建连接。由于我的最终目标是 HTTPS GET,因此我尝试使用与 Android 捆绑的 Apache HTTP 客户端。那种工作。我可以进行 HTTPS 连接,但我仍然无法使用 SSL 身份验证。如果我在服务器上启用了客户端 SSL 身份验证,则连接将失败。我没有检查 Apache HTTP 客户端代码,但我怀疑他们正在使用他们自己的 SSL 实现,并且不使用 javax.net.ssl。
回答by Fei
Not sure you resolved this issue or not, but this is how I do it and it works on Android:
不确定你是否解决了这个问题,但这就是我的做法,它适用于 Android:
- Use openssl to merge client's cert(cert must be signed by a CA that accepted by server) and private key into a PCKS12 format key pair: openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
- You may need patch your JRE to umlimited strength encryption depends on your key strength: copy the jar files fromJCE 5.0 unlimited strength Jurisdiction Policy FIlesand override those in your JRE (eg.C:\Program Files\Java\jre6\lib\security)
- Use Portecle tool mentioned above and create a new keystore with BKS format
- Import PCKS12 key pair generated in step 1 and save it as BKS keystore. This keystore works with Android client authentication.
- If you need to do certificate chain, you can use this IBM tool:KeyManto merge client's PCKS12 key pair with CA cert. But it only generate JKS keystore, so you again need Protecle to convert it to BKS format.
- 使用 openssl 将客户端的证书(证书必须由服务器接受的 CA 签名)和私钥合并为 PCKS12 格式的密钥对: openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
- 根据您的密钥强度,您可能需要将 JRE 修补为无限强度加密:从JCE 5.0 无限强度管辖权策略文件中复制 jar 文件并覆盖 JRE 中的那些文件(例如 C:\Program Files\Java\jre6\lib\security )
- 使用上面提到的 Portecle 工具并创建一个 BKS 格式的新密钥库
- 导入在步骤 1 中生成的 PCKS12 密钥对并将其保存为 BKS 密钥库。此密钥库适用于 Android 客户端身份验证。
- 如果需要做证书链,可以使用IBM这个工具:KeyMan将客户端的PCKS12密钥对与CA证书合并。但它只生成 JKS 密钥库,因此您再次需要 Protecle 将其转换为 BKS 格式。
回答by Andrey
command line:
命令行:
keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS
回答by pwb
Use this manual http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/This guide really helped me. 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.
使用本手册http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/本指南对我很有帮助。观察存储中的证书序列很重要。例如:首先导入最下面的中间 CA 证书,然后一直导入到根 CA 证书。