如何在Android中使用自签名的SSL证书

4
这个问题已经困扰我几天了。我已经阅读了很多关于这个问题的其他问题,但仍然无法继续。
我创建了一个简单的测试应用程序来测试Android上的SSL。该应用程序只有一个按钮,当单击该按钮时,应用程序尝试通过SSL加密连接将“Hello World”发送到我的测试服务器,然后服务器响应相同的短语。
首先,我使用openssl为我的服务器创建了一个测试密钥和测试证书。然后,我按照Crazy Bob的博客中显示的说明进行操作。我直接从Bouncy Castle的网站获取了Bouncy Castle提供程序,按照Crazy Bob的博客中显示的方式创建了受信任的密钥库,并且我认为在那一点上一切都正确。
当我尝试运行我的代码时,我得到了异常“IOException:Wrong version of key store.” 然后我在StackOverflow上找到了这个问题。建议我尝试使用旧版本的Bouncy Castle Providers而不是最新的bcprov-jdk15on-147.jar。我以此为基础继续前进,实际上尝试了从jdk13-146到jdk16-146的每个bcprovider。但每次都会得到相同的“IOExcpetion:wrong version of key store.” 异常。
然后我在StackOverflow上找到了另一个类似问题。有人通过使用512位大小的密钥而不是1024大小的密钥成功摆脱了那个异常。好吧,我尝试了一下,什么也没做到,但是还是出现了同样的异常。
所以现在我在这里,想知道下一步该怎么做。我的想法已经基本用完了,搜索结果也几乎没有了。
我的网络代码是Crazy Bob的代码的1对1副本,除此之外,应用程序只有处理按钮的活动类。我正在尝试在API级别7上实现这一点。
任何帮助将不胜感激。谢谢。

如果您将目标API移动到10或14之类的版本,它是否开始工作?在处理旧的API级别时,测试新的API级别并确保它不是兼容性问题是个好主意。 - matt5784
我需要为我的Android项目创建一个".pem"文件,用于Brain Tree。我安装了openssl并运行了以下命令:openssl> s_client -connect $https://xxx.xxx.xx:443 2>&1 |
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem但是我在s_client中遇到了错误,请帮助我。我从Crazy Bob的博客中获取了该命令。
- M.A.Murali
3个回答

2

两个选择:

  1. You can do what you do and create your own key store and I've done that, here is instructions from my code that I stored (because it was so time consuming to get it to work):

    To generate PKS:

    1. Created cert in IIS7 and then exported as pfx. Follow instruction on SelfSSL: http://www.robbagby.com/iis/self-signed-certificates-on-iis-7-the-easy-way-and-the-most-effective-way/ 1a. Download tool: http://cid-3c8d41bb553e84f5.skydrive.live.com/browse.aspx/SelfSSL 1b. Run: SelfSSL /N:CN=mydomainname /V:1000 /S:1 /P:8081 I use port 8181 on my server 1c. Export from IIS manager to cert.pfx
    2. Run command line in SSL to convert file into X.509: openssl pkcs12 -in C:\cert.pfx -out C:\cert.cer -nodes
    3. Edit file and delete all except -----BEGIN.... END CERTIFICATE----- IMPORTANT! It was working when I got proper (5) amount of dashes and put tags and data on separate lines
    4. use keytool. C:\Java\JDK\bcprov.jar was downloaded separately C:\Users>keytool -import -v -trustcacerts -alias key_alias -file C:\cert.cer -keystore C:\mystore.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath C:\Java\JDK\bcprov.jar -storepass 123456
  2. Create TRUST ALL KeyStore and forget about all this. Basically, you can use any SSL without errors. Just disable it in production if you really care. Here is code I use to get SSL client prepared (assuming you use Apache Http client)

    private HttpClient getHttpClient()
    {
        HttpParams params = new BasicHttpParams();
    
        //Set main protocol parameters
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
        HttpProtocolParams.setUseExpectContinue(params, true);
    
        // Turn off stale checking.  Our connections break all the time anyway, and it's not worth it to pay the penalty of checking every time.
        HttpConnectionParams.setStaleCheckingEnabled(params, false);
        // FIX v2.2.1+ - Set timeout to 30 seconds, seems like 5 seconds was not enough for good communication
        HttpConnectionParams.setConnectionTimeout(params, 30 * 1000);
        HttpConnectionParams.setSoTimeout(params, 30 * 1000);
        HttpConnectionParams.setSocketBufferSize(params, 8192);
    
        // Don't handle redirects -- return them to the caller.  Our code often wants to re-POST after a redirect, which we must do ourselves.
        HttpClientParams.setRedirecting(params, false);
    
        // Register our own "trust-all" SSL scheme
        SchemeRegistry schReg = new SchemeRegistry();
        try
        {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
    
            TrustAllSSLSocketFactory sslSocketFactory = new TrustAllSSLSocketFactory(trustStore);
            sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    
            Scheme sslTrustAllScheme = new Scheme("https", sslSocketFactory, 443);
            schReg.register(sslTrustAllScheme);
        }
        catch (Exception ex)
        {
            LogData.e(LOG_TAG, ex, LogData.Priority.None);
        }
    
        ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params,schReg);
        return new DefaultHttpClient(conMgr, params);
    }
    

如果我使用TRUST ALL选项,这是否意味着我的应用程序是唯一信任所有站点的应用程序?换句话说,我只是想确保它不会以任何方式影响手机上的其他应用程序。我的应用程序正在使用的URL是硬编码的,因为它永远不会改变,因此允许应用程序信任所有站点可能是安全的,因为它永远不会尝试连接到某些“非受信任”的站点。 - zaplec
是的,您只需将其设置为信任此特定连接的所有证书。其他应用程序不会受到影响。 - katit
这个全信任的解决方案虽然可行,但仍然存在可能受到中间人攻击的漏洞。对于测试目的来说还可以,但我认为我需要继续努力,只让SSL在一个受信任的网站上工作 :) - zaplec
我在#1中给了您指示。对我来说它是可行的。后来我切换到"信任所有",因为中间人不是我的问题。 - katit
是的,我看了你在#1给出的指示,但即使我按照你给出的指示操作,我仍然遇到了“密钥库的错误版本”异常。我将切换到Java 1.6或Java 1.5并再次尝试,因为到目前为止我一直在使用Java 1.7。希望这有所帮助。 - zaplec

0

我也遇到了同样的情况,为了解决它,我参考了R4j提供的同一篇博客文章(http://nelenkov.blogspot.in/2011/12/using-custom-certificate-trust-store-on.html)。以下是涉及的步骤:

  1. 创建自定义信任库:我使用Portecle创建了一个密钥库,并将服务器的公钥证书导入其中。
  2. 创建带有密钥对的自定义密钥库:keytool -genkeypair -alias sample -keyalg RSA -sigalg SHA1withRSA -dname "CN=Nazgul, OU=Assault, O=Sauron Enterprises, L=Mordor, ST=Middle Earth, C=ME" -keypass welcome123 -validity 365 -storetype pkcs12 -keystore g:\mordor_key_store.pfx -storepass welcome123 -keysize 2048
  3. 然后按照nelkov博客中提到的方法使用它们。如果您遇到证书发放给abc.com但Verifier拒绝www.abc.com的情况,则可能需要创建自己的自定义AbstractVerifier
  4. 最后,要创建安全的HTTPClient,您可以执行以下操作:

            public static DefaultHttpClient getSecureHttpClient(){
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        SSLContext sslContext = null;
        try {
            sslContext = createSslContext(true);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        final X509HostnameVerifier delegate = new BrowserCompatHostnameVerifier();
        MySSLSocketFactory socketFactory = new MySSLSocketFactory(sslContext, delegate);
        schemeRegistry.register(new Scheme("https", socketFactory, 443));
    
        DefaultHttpClient client = new DefaultHttpClient();
        HttpParams params = client.getParams();
        client = new DefaultHttpClient(new ThreadSafeClientConnManager(params,
                schemeRegistry), params){
            protected HttpParams determineParams(HttpRequest req) {
                HttpParams params = req.getParams(); // req is an HttpRequest object
                HttpConnectionParams.setSoTimeout(params, 60000);
                HttpConnectionParams.setConnectionTimeout(params, 60000);
                return params;
            }
        };
    
        return client;
    }
    

关于我的选择的详细原因,您可以参考这篇文章http://fuking-android.quora.com/Implement-HTTPS-for-android-apps-a-novices-tale


0

当我尝试请求EWS时,它类似于我的问题。您可以参考此链接并下载示例源代码,然后像我的答案一样进行修改。希望这有所帮助!

更新:
以下命令对我有效(我大约在2个月前尝试过):

  C:\OpenSSL-Win32\bin>keytool -importcert -v -trustcacerts -file "d:/cer.cer" 
  -alias parkgroup_restful -keystore "D:/parkgroup-ws-client.bks" 
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath 
 "D:/bcprov-jdk16-145.jar" -storetype BKS -storepass 1234567

  .... /**It should show the result here**/

   Trust this certificate? [no]:  yes
   Certificate was added to keystore
   [Storing D:/parkgroup-ws-client.bks]

   C:\OpenSSL-Win32\bin>keytool -list -keystore "D:/parkgroup-ws-client.bks" -provi
   der org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "D:/bcprov-
   jdk16-145.jar" -storetype BKS -storepass 1234567

   Keystore type: BKS
   Keystore provider: BC

   Your keystore contains 1 entry

   parkgroup_restful, Apr 10, 2012, trustedCertEntry,
   Certificate fingerprint (MD5): 36:47:88:62:23:1C:F3:52:17:BE:7A:A9:94:56:19:18

您可以看到,我使用了 bcprov-jdk16-145.jar 和 openssl 库。您可以尝试一下。
另一个创建密钥库的工具:http://portecle.sourceforge.net/


谢谢提供的链接,但我仍然无法解决“密钥库的错误版本”异常。 - zaplec
我不确定你在第一行的操作,因为我认为你从未使用那里创建的PEM。你能否解释一下?否则,你的解决方案看起来很像我所尝试的。我已经尝试过bcprov-jdk16-145.jar(和其他版本)以及openssl。 - zaplec
我建议您检查证书文件,因为我遇到了与您相同的问题。然后我尝试使用另一个文件,它成功了。 - ductran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接