【问题标题】:How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain如何创建包含客户端证书链的 BKS (BouncyCastle) 格式 Java 密钥库
【发布时间】:2011-05-03 04:44:01
【问题描述】:

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

所以看起来客户端从未发送正确的证书链,可能是因为我没有正确创建密钥库。我无法像在桌面上那样启用 SSL 调试,所以这比它应该做的要困难得多。

以下是用于创建 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 客户端 keystore 的命令:

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

【问题讨论】:

  • 真的没有人使用过 BKS 格式吗?啊,为什么 Android 不能只使用标准的 JKS 格式,或者至少记录这种格式,因为这是他们支持的全部?这应该很简单...

标签: java android ssl


【解决方案1】:

我遵循的详细分步说明

  • 从以下位置下载 bouncycastle JAR http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.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:\(环境变量中使用的位置)
      • 修改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 添加到项目的 CLASSPATH 并在代码中添加以下行
      • Security.addProvider(new BouncyCastleProvider());
  • 使用 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 并添加以下条目

    • 完成这些更改后重新启动服务器。

  • 为 Android 客户端配置 BouncyCastle
    • 无需配置,因为 Android 在提供的“android.jar”中内部支持 Bouncy Castle 版本 1.46。
    • 只需实现您的HTTP客户端版本(MyHttpClient.java可以在下面找到)并在代码中设置以下内容
      • SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    • 如果您不这样做,则会出现如下异常
      • javax.net.ssl.SSLException:证书中的主机名不匹配: !=
    • 在生产模式下,将上述代码更改为
      • 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(...);

【讨论】:

  • 我按照上面的过程,我在“使用 Bouncy Castle 生成密钥库”这一步做得很好,在这里运行命令 keytool 时,它给出以下错误,keytool 错误:java.lang.ClassNotFoundException : org.bouncycastle.jce.provider.BouncyCastleProvider,请推荐
  • 我通过在 keytool 命令中指定 BouncyCastle 库路径完成了这一步,谢谢
  • 如果你得到 ClassNotFoundException: org.bouncycastle.jce.provider.BouncyCastleProvider 然后使用 -providerpath /bcprov-jdk15on-149.jar
  • +1 不错的答案。无需设置类路径,只需在两个逗号中使用 -providerpath c:\bcprov-ext-jdk15on-1.46.jar -provider org.bouncycastle.jce.provider.BouncyCastleProvider。此外,在检查时,您将被要求输入密码。不是你输入的那个。是storepass之后的那个myproject
  • 如果您在创建密钥库时遇到java.security.InvalidKeyException: Illegal key size 异常,您可能需要用所谓的Unlimited Strength Jurisdiction Policy File 替换您的 JCE 策略文件。 Link to files for Java 7
【解决方案2】:

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

【讨论】:

  • 我正在使用它,因为我也在使用 KeyStores,它可以工作。好的,它并不完美,但它有很大帮助。
  • 我也使用过 Portecle 并确认它解决了我的问题!!!我确实必须在我的 Android 项目中使用 HTTPSUrlConnection 并且稍微弄乱了 TrustManagerFactory。
  • 我只想指出,截至撰写本文时,Portecle 1.7 是最新的,它有一个过时的 bouncycastle,无法打开更新的 bks 密钥库。
  • 谢谢你,克里斯,我不知道。
  • 太棒了!我多年来一直在寻找这种软件。每个人都应该放弃 keytool 并改用 Porteclé。顺便说一句,导入服务器自签名证书并使用来自 saxos 的代码对我来说效果很好。我不知道为什么 SSL 对 Java 来说如此痛苦。 OpenSSL C API 不适合初学者,但至少代码紧凑、一致,您可以在网上找到很多示例(从 1989 年到 2014 年工作,与其他任何不适用于您的安装和需求的 Java 示例不同)新证书...)
【解决方案3】:

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

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

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

【讨论】:

  • 是的,我正在使用单独的信任库(使用我的问题中的命令创建)以及我使用问题中的第二个命令创建的密钥库(也尝试创建其他一些方法都没有成功)。我没有意识到 Android 中的 ssl 包可能是罪魁祸首。我会看看能不能得到 Android 人的任何回应。
【解决方案4】:

不确定你是否解决了这个问题,但我就是这样做的,并且它适用于 Android:

  1. 使用 openssl 将客户端的证书(证书必须由服务器接受的 CA 签名)和私钥合并成一个 PCKS12 格式的密钥对: openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
  2. 您可能需要将您的 JRE 修补为无限强度加密取决于您的密钥强度:从JCE 5.0 unlimited strength Jurisdiction Policy FIles 复制 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 格式。

【讨论】:

    【解决方案5】:

    命令行:

    keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS
    

    【讨论】:

      【解决方案6】:

      您创建 BKS 密钥库的命令对我来说看起来是正确的。

      如何初始化密钥库。

      您需要创建并传递您自己的 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);
          }
      }
      

      如果有效,请告诉我。

      【讨论】:

      • 我发布的命令对于创建信任库是正确的。它正在为此目的而工作。我正在尝试创建一个密钥库以使用不起作用的客户端身份验证。我已经更新了我的问题以包含我尝试过但不起作用的命令。
      【解决方案7】:

      使用本手册http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/ 这个指南真的帮助了我。观察商店中的一系列证书很重要。例如:先导入最底层的中间 CA 证书,然后一直导入到根 CA 证书

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-06
        • 1970-01-01
        • 1970-01-01
        • 2020-06-01
        • 1970-01-01
        • 2011-02-15
        • 2015-02-17
        相关资源
        最近更新 更多