如何创建包含客户端证书链的BKS(BouncyCastle)格式Java密钥库

Ben*_*ron 51 java ssl android

我正在编写一个需要SSL客户端身份验证的Android应用.我知道如何为桌面Java应用程序创建JKS密钥库,但Android仅支持BKS格式.我试图创建密钥库的每一种方式都会导致以下错误:
handling exception: javax.net.ssl.SSLHandshakeException: null cert chain

所以看起来客户端永远不会发送正确的证书链,可能是因为我没有正确创建密钥库.我无法在桌面上启用SSL调试,因此这使得它比应该更加困难.

作为参考,以下是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


这是我尝试过的命令,它无法创建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
Run Code Online (Sandbox Code Playgroud)

Vip*_*pul 62

为实现这一目标,我遵循详细的逐步说明

  • http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar下载bouncycastle JAR, 或从"doc"文件夹中下载.
  • 使用以下方法之一配置BouncyCastle for PC.
    • 静态添加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:\工具\ jdk1.5.0_09\JRE\lib\security中
        • d:\工具\ 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添加到项目的CLASSPATH中,并在代码中添加以下行
      • Security.addProvider(new BouncyCastleProvider());
  • 使用Bouncy Castle生成Keystore
    • 运行以下命令
      • 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"scheme ="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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如何在Activity类中调用上面的代码:

DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);
Run Code Online (Sandbox Code Playgroud)

  • 如果你得到ClassNotFoundException:org.bouncycastle.jce.provider.BouncyCastleProvider然后使用-providerpath <path> /bcprov-jdk15on-149.jar (4认同)
  • +1很好的答案.不要设置类路径,只需在两个逗号中使用`-providerpath c:\ bcprov-ext-jdk15on-1.46.jar -provider org.bouncycastle.jce.provider.BouncyCastleProvider`.此外,在检查时,系统会要求您输入密码.这不是你输入的那个.它是`storepass`之后的那个,它是`myproject`. (3认同)
  • 如果在创建密钥库时遇到`java.security.InvalidKeyException:Illegal key size`异常,则可能必须用所谓的_Unlimited Strength Jurisdiction Policy File_替换您的JCE策略文件.[链接到Java 7的文件](http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html) (3认同)

gnc*_*ais 26

我使用Portecle,它就像一个魅力.

  • 我也使用过Portecle并确认它解决了我的问题!!! 我确实必须在我的Android项目中使用HTTPSUrlConnection并且稍微使用TrustManagerFactory. (2认同)

Cth*_*lhu 5

我认为您的问题不在于 BouncyCastle 密钥库;我认为问题在于 Android 中的 javax.net.ssl 包损坏。BouncyCastle 密钥库是一个极大的烦恼,因为 Android 更改了默认的 Java 行为而没有在任何地方记录它 - 并删除了默认的提供程序 - 但它确实有效。

请注意,对于 SSL 身份验证,您可能需要 2 个密钥库。“TrustManager”密钥库,其中包含 CA 证书,以及“KeyManager”密钥库,其中包含您的客户端站点公钥/私钥。(文档对于 KeyManager 密钥库中需要包含的内容有些模糊。)理论上,如果您的所有证书均由“知名”证书颁发机构(例如 Verisign、Thawte、等等。让我知道这对你有用。您的服务器还需要 CA 来对您的客户端进行签名。

我根本无法使用 javax.net.ssl 创建 SSL 连接。我在服务器端禁用了客户端 SSL 身份验证,但仍然无法创建连接。由于我的最终目标是 HTTPS GET,因此我尝试使用与 Android 捆绑在一起的 Apache HTTP 客户端。这确实有效。我可以建立 HTTPS 连接,但仍然无法使用 SSL 身份验证。如果我在服务器上启用客户端 SSL 身份验证,连接将会失败。我还没有检查 Apache HTTP 客户端代码,但我怀疑他们正在使用自己的 SSL 实现,并且不使用 javax.net.ssl。


Fei*_*Fei 5

不确定你是否解决了这个问题,但这就是我的做法,并且它在 Android 上有效:

  1. 使用 openssl 将客户端的证书(证书必须由服务器接受的 CA 签名)和私钥合并为 PCKS12 格式的密钥对: openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
  2. 您可能需要将 JRE 修补为无限强度加密,具体取决于您的密钥强度:从JCE 5.0 无限强度管辖策略文件复制 jar 文件并覆盖 JRE 中的文件(例如 C:\Program Files\Java\jre6\lib\security )
  3. 使用上面提到的Portecle工具并创建一个BKS格式的新密钥库
  4. 导入步骤 1 中生成的 PCKS12 密钥对并将其保存为 BKS 密钥库。此密钥库适用于 Android 客户端身份验证。
  5. 如果您需要做证书链,您可以使用这个IBM工具:KeyMan将客户端的PCKS12密钥对与CA证书合并。但它只生成 JKS 密钥库,因此您再次需要 Protecle 将其转换为 BKS 格式。