代码之家  ›  专栏  ›  技术社区  ›  Ben Baron

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

  •  51
  • Ben Baron  · 技术社区  · 14 年前

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

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

    信任库 :
    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
    
    7 回复  |  直到 6 年前
        1
  •  62
  •   Vipul    12 年前

    为了达到这个目的,我遵循了详细的逐步说明

    • 从下载bouncycastle JAR http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar 或者从“doc”文件夹中取出。
    • 使用以下方法之一为PC配置BouncyCastle。
      • 静态添加BC提供程序(推荐)
          • 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.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider网站
          • 类路径=%CLASSPATH%;c:\ bcprov-ext-jdk15 on-1.46.jar
      • 将bcprov-ext-jdk15on-1.46.jar添加到项目的类路径中,并在代码中添加以下行
        • Security.addProvider(new BouncyCastleProvider());
      • 运行以下命令
        • 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
      • 打开D:\tools\apache-tomcat-6.0.35\conf\server.xml并添加以下条目

        • <连接器 port=“8443” alias=“我的项目” keystoreType=“BKS”键 SSLEnabled=“真” clientAuth=“假” protocol=“HTTP/1.1”协议 scheme=“https” secure=“正确” sslImplementationName=“org.bouncycastle.jce.provider.BouncyCastleProvider”/>
      • 更改后重新启动服务器。

      • 无需配置,因为Android在提供的“Android.jar”中内部支持BouncyCastle1.46版本。
      • 只需实现您的HTTP客户端版本(MyHttpClient.java可以在下面找到),并在代码中设置以下内容
      • 如果你不这样做,它会给出一个例外如下
      • 在生产模式下,将上述代码更改为
        • SSLSocketFactory.sethostnamevirifier(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);
            }
        }
    }
    

    如何在活动类中调用上述代码:

    DefaultHttpClient client = new MyHttpClient(getApplicationContext());
    HttpResponse response = client.execute(...);
    
        2
  •  26
  •   gnclmorais    14 年前

    我用 Portecle ,它就像一个符咒。

        3
  •  4
  •   Cthulhu    14 年前

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

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

        4
  •  4
  •   Fei    13 年前

    1. 使用openssl将客户端的证书(证书必须由服务器接受的CA签名)和私钥合并到PCKS12格式的密钥对中: openssl pkcs12-export-in clientcert.pem-inkey clientkey.pem-out client.p12
    2. JCE 5.0 unlimited strength Jurisdiction Policy FIles 并重写JRE中的那些文件(例如C:\ Program Files\Java\jre6\lib\security)
    3. 使用上面提到的Portecle工具创建一个新的BKS格式的密钥库
    4. 导入步骤1中生成的PCKS12密钥对,并将其保存为BKS密钥库。这个密钥库与Android客户端身份验证一起工作。
    5. KeyMan 要将客户端的PCKS12密钥对与CA证书合并,但它只生成JKS密钥库,因此您再次需要Protecle将其转换为BKS格式。
        5
  •  2
  •   Andrew    10 年前

    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
  •  0
  •   saxos    14 年前

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

    如何初始化密钥库。

    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
  •  0
  •   pwb    9 年前

    使用本手册 http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/ 首先导入最低的中间CA证书,然后一直导入到根CA证书 .