2010-10-31 97 views
49

我正在編寫一個需要SSL客戶端身份驗證的Android應用程序。我知道如何爲桌面Java應用程序創建JKS密鑰庫,但Android僅支持BKS格式。每次這樣,我一直在努力,創建以下錯誤密鑰庫結果:
handling exception: javax.net.ssl.SSLHandshakeException: null cert chain如何創建包含客戶端證書鏈的BKS(BouncyCastle)格式Java Keystore

所以它看起來像客戶端發送從來沒有一個適當的證書鏈,可能是因爲我沒有正確創建密鑰庫。我無法啓動SSL調試,就像我可以在dekstop上一樣,所以這使得這比應該更困難。

僅供參考以下是努力創造一個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 
+17

認真沒有人有BKS格式的經驗?唉,爲什麼Android不能使用標準的JKS格式,或者至少要記錄這種格式,因爲它們都支持它?這應該很簡單... – 2010-11-07 23:46:10

回答

58

一步的指示,詳細步驟我跟着從 http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar 實現這一

  • 下載BouncyCastle的JAR或將其從 「文檔」 文件夾中。
  • 使用以下某種方法配置PC的BouncyCastle。
    • 添加BC提供靜態(推薦)
      • 複製bcprov-EXT-jdk15on-1.46.jar到每個
        • d:\工具\ jdk1.5.0_09 \ JRE \ lib中\分機(JDK (捆綁JRE)
        • d:\工具\ jre1.5.0_09 \ lib中\分機(JRE)
        • C:(在環境變量中使用的位置)\
      • 修改java.security文件在之下10
        • d:\工具\ jdk1.5.0_09 \ JRE \ lib \ security中
        • d:\工具\ jre1.5.0_09 \ lib \ security中
        • 並添加以下條目
          • security.provider 0.7 = org.bouncycastle.jce.provider.BouncyCastleProvider
      • 添加以下環境變量中的 「用戶變量」 部分
        • CL ASSPATH =%CLASSPATH%; C:\ bcprov-EXT-jdk15on-1.46.jar
    • 添加bcprov-EXT-jdk15on-1.46。罐子到項目的CLASSPATH,並添加以下行代碼中的
      • Security.addProvider(新BouncyCastleProvider());
  • 使用充氣城堡
    • 執行如下命令生成密鑰庫
      • 密鑰工具-genkey -alias的myproject -keystore C:/myproject.keystore -storepass的myproject -storetype BKS -provider有機.bouncycastle.jce.provider.BouncyCastleProvider
    • 這將生成文件C:\ myproject.keystore
    • 執行如下命令,以檢查是否正確地產生或不
      • 密鑰工具-list -keystore C:\ myproject.keystore -storetype BKS
  • 配置BouncyCastle的用於TOMCAT

    • 打開D:\ tools \ apache-tomcat-6.0.35 \ conf \ server.xml並添加以下條目

      • <連接器 端口= 「8443」 keystorePass = 「myproject的」 別名= 「myproject的」 密鑰庫= 「C:/myproject.keystore」 keystoreType = 「BKS」 SSLEnabled = 「真」 clientAuth =」假」 協議= 「HTTP/1.1」 方案= 「HTTPS」 安全= 「真」 sslProtocol = 「TLS」 sslImplementationName = 「org.bouncycastle.jce.provider.BouncyCastleProvider」/ >
    • 這些更改後重新啓動服務器。

  • 配置BouncyCastle的爲Android客戶端
    • 沒有必要由於Android配置支持充氣城堡1.46版本在內部提供的 「的android.jar」。
    • 只實現你的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(...); 
+0

我按照上面的過程,我做到了「使用Bouncy Castle生成密鑰庫」這一步,在這裏運行命令keytool時,它給出了以下錯誤,keytool錯誤:java.lang.ClassNotFoundException:org.bouncycastle.jce。 provider.BouncyCastleProvider,請建議 – candy 2012-05-04 10:49:09

+0

通過在keytool命令中指定BouncyCastle庫路徑,我完成了這一步,謝謝 – candy 2012-05-04 13:54:05

+0

我不再在這個項目上工作,所以我沒有自己驗證過這個,但我接受了這個作爲答案,因爲根據糖果和你的經驗,你應該得到一些答案,這似乎是一個有效的解決方案。感謝您的貢獻。 – 2012-08-31 19:51:55

0

您用於創建密鑰庫BKS命令看起來是正確的對M即

如何初始化密鑰庫。

您需要創建並傳遞您自己的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); 
    } 
} 

請讓我知道它的工作。

+0

我發佈的命令對於創建信任存儲是正確的。它正在爲此目的而工作。我正在嘗試創建一個密鑰存儲以使用不起作用的客戶端身份驗證。我已經更新了我的問題,以包含我嘗試過的不起作用的命令。 – 2010-11-17 02:09:46

4

我不認爲你的問題是與BouncyCastle keystore;我認爲問題出在Android中的javax.net.ssl包中。 BouncyCastle密鑰庫是一個極大的煩惱,因爲Android改變了默認的Java行爲而沒有記錄任何地方 - 並且刪除了默認的提供者 - 但它確實有效。

請注意,對於SSL身份驗證,您可能需要2個密鑰庫。包含CA證書的「TrustManager」密鑰庫和包含客戶端公鑰/私鑰的「KeyManager」密鑰庫。 (該文檔對於KeyManager密鑰庫中需要的內容有些模糊。)理論上,如果您的所有證書都由「知名」證書頒發機構(例如Verisign,Thawte等)簽署,則不需要TrustManager密鑰庫。等等。讓我知道這對你有用。您的服務器還需要CA用於簽署客戶端的任何內容。

我根本無法使用javax.net.ssl創建SSL連接。我禁用了服務器端的客戶端SSL認證,並且仍然無法創建連接。由於我的最終目標是HTTPS GET,因此我嘗試使用與Android捆綁在一起的Apache HTTP Client。這種工作。我可以創建HTTPS連接,但我仍然無法使用SSL身份驗證。如果我在我的服務器上啓用了客戶端SSL身份驗證,則連接將失敗。我沒有檢查Apache HTTP Client代碼,但我懷疑他們正在使用他們自己的SSL實現,並且不使用javax.net.ssl。

+0

是的,我正在使用一個單獨的信任庫(使用我的問題中的命令創建)以及使用我的問題中的第二個命令創建的密鑰庫(還試圖通過幾種其他方式創建它們都沒有成功)。我沒有意識到Android中的ssl包可能是罪魁禍首。我會看看是否可以從Android人員得到任何迴應。 – 2010-11-19 05:21:07

22

我用Portecle,它的功能就像一個魅力。

+0

我正在使用它,因爲我也在使用KeyStore,並且它可以正常工作。好吧,這不是完美的,但它有很大的幫助。 – gnclmorais 2011-02-04 19:53:24

+2

我也使用Portecle,並確認它解決了我的問題!我不得不在我的Android項目中使用HTTPSUrlConnection,並且有點煩亂了TrustManagerFactory。 – Exegesis 2011-06-15 18:05:49

+1

我只是想說明,作爲寫作的最新的Portecle 1.7有一個過時的bouncycastle,它無法打開更新的bks密鑰庫。 – Chris 2013-05-29 16:57:02

4

不知道你解決了這個問題,或者沒有,但我這是怎麼做到這一點,它適用於Android:

  1. 使用OpenSSL合併客戶證書(證書必須由服務器接受了一個CA簽署)和私鑰成PCKS12格式的密鑰對: OpenSSL的PKCS12 -export -in clientcert.pem -inkey clientkey.pem退房手續client.p12
  2. 您可能需要修補JRE到umlimited強度加密取決於你的鑰匙強度:複製JCE 5.0 unlimited strength Jurisdiction Policy FIles中的jar文件並覆蓋JRE中的jar文件(eg.C:\ Program Files \ Java \ jre6 \ lib \ securi ty)
  3. 使用上述Portecle工具並使用BKS格式創建新密鑰庫
  4. 導入在步驟1中生成的PCKS12密鑰對並將其保存爲BKS密鑰庫。此密鑰庫適用於Android客戶端身份驗證。
  5. 如果您需要執行證書鏈,則可以使用此IBM工具:KeyMan將客戶端的PCKS12密鑰對與CA證書合併。但它只生成JKS密鑰庫,所以你需要再次使用Protecle將其轉換爲BKS格式。
2

命令行:

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