繁体   English   中英

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

[英]How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain

我正在编写一个需要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

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

  • 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);
        }
    }
}

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

DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);

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

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

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

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

不确定您是否解决了这个问题,但这是我如何做到的,它适用于Android:

  1. 使用openssl将客户端的证书(证书必须由服务器接受的CA签名)和私钥合并到PCKS12格式密钥对中: openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
  2. 你可能需要补丁你的JRE到umlimited力量加密取决于你的关键优势:从JCE 5.0复制jar文件无限强度管辖权政策FIles并覆盖你的JRE中的那些(例如:C:\\ Program Files \\ Java \\ jre6 \\ lib \\ security )
  3. 使用上面提到的Portecle工具并创建一个具有BKS格式的新密钥库
  4. 导入步骤1中生成的PCKS12密钥对,并将其保存为BKS密钥库。 此密钥库适用于Android客户端身份验证。
  5. 如果需要进行证书链,可以使用此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

使用本手册http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/本指南对我很有帮助 在商店中观察一系列证书非常重要。 例如:首先导入最低级的中级CA证书,然后一直导入根CA证书

您创建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);
    }
}

如果有效,请告诉我。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM