簡體   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