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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10851486/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 02:50:46  来源:igfitidea点击:

How to use a self signed SSL certificate in Android

javaandroidsslhttpsandroid-networking

提问by zaplec

This thing has been bugging me for couple days now. I've read lots of other questions about this whole issue and still haven't been able to proceed.

这件事已经困扰了我几天了。我已经阅读了很多关于整个问题的其他问题,但仍然无法继续。

I've created a simple test application just to test SSL on Android. The application has only one button and when clicked the application tries to send "Hello World" over SSL encrypted connection to my test server, which then responds with the exact same phrase.

我创建了一个简单的测试应用程序只是为了在 Android 上测试 SSL。该应用程序只有一个按钮,单击该按钮后,该应用程序会尝试通过 SSL 加密连接向我的测试服务器发送“Hello World”,然后该服务器会以完全相同的短语进行响应。

First I created a test key and test certificate for my server using openssl. Then I've been following the instructions showed in Crazy Bob's blog. I got the Bouncy Castle provider directly from Bouncy Castle's site, created a trusted keystore as shown on Crazy Bob's blog and got everything right at that point I believe.

首先,我使用 openssl 为我的服务器创建了一个测试密钥和测试证书。然后我一直按照Crazy Bob 的博客中显示的说明进行操作。我直接从 Bouncy Castle 的站点获得了 Bouncy Castle 提供程序,创建了一个受信任的密钥库,如 Crazy Bob 的博客所示,我相信那时一切都正确。

When I tried to run my code, I got the exception "IOException: Wrong version of key store." Then I found this questionon StackOverflow. There it was suggested that I should try using older Bouncy Castle Providers rather than the newest bcprov-jdk15on-147.jar. I went on with this in mind and actually ended up trying every bcprovider from jdk13-146 to jdk16-146. Still every time I got the same "IOExcpetion: wrong version of key store." exception.

当我尝试运行我的代码时,出现异常“IOException:密钥存储版本错误”。然后我在 StackOverflow 上发现了这个问题。有人建议我应该尝试使用较旧的 Bouncy Castle Providers 而不是最新的bcprov-jdk15on-147.jar。我继续考虑这一点,实际上最终尝试了从 jdk13-146 到 jdk16-146 的每个 bcprovider。仍然每次我得到相同的“IOExcpetion:密钥存储的错误版本”。例外。

Then I found yet another questionabout similar problem on StackOverflow. There someone had managed to get rid of that exception by using 512 bit sized key instead of 1024 sized key. Well I gave it a try and accomplished nothing, but the same exception.

然后我在 StackOverflow 上发现了另一个关于类似问题的问题。有人设法通过使用 512 位大小的密钥而不是 1024 位大小的密钥来摆脱该异常。好吧,我试了一下,什么也没做,但同样的例外。

So here I am now, wondering what to do next. I'm pretty much running out of ideas and google search results.

所以我现在在这里,想知道下一步该怎么做。我的想法和谷歌搜索结果几乎用完了。

My web code is 1 on 1 copy of crazy bob's code and besides that the application has only the activity class that handles the button only. I'm trying to implement this on API level 7.

我的网络代码是疯狂鲍勃代码的一对一副本,除此之外,该应用程序只有处理按钮的活动类。我正在尝试在 API 级别 7 上实现这一点。

Any help would be greatly appreciated. Thanks.

任何帮助将不胜感激。谢谢。

回答by katit

2 choices:

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/SelfSSL1b. 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);
    }
    
  1. 你可以做你所做的并创建你自己的密钥存储,我已经做到了,这里是我存储的代码中的说明(因为让它工作非常耗时):

    生成 PKS:

    1. 在 IIS7 中创建证书,然后导出为 pfx。按照有关 SelfSSL 的说明进行操作:http://www.robbagby.com/iis/self-signed-certificates-on-iis-7-the-easy-way-and-the-most-effective-way/1a。下载工具:http: //cid-3c8d41bb553e84f5.skydrive.live.com/browse.aspx/SelfSSL1b。运行: SelfSSL /N:CN=mydomainname /V:1000 /S:1 /P:8081 我在我的服务器 1c 上使用端口 8181。从 IIS 管理器导出到 cert.pfx
    2. 在 SSL 中运行命令行将文件转换为 X.509: openssl pkcs12 -in C:\cert.pfx -out C:\cert.cer -nodes
    3. 编辑文件并删除所有除了 -----BEGIN.... END CERTIFICATE----- 重要!当我获得适当的 (5) 数量的破折号并将标签和数据放在不同的行上时,它正在工作
    4. 使用密钥工具。C:\Java\JDK\bcprov.jar 单独下载 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. 创建 TRUST ALL KeyStore 并忘记这一切。基本上,您可以毫无错误地使用任何 SSL。如果您真的关心,只需在生产中禁用它。这是我用来准备 SSL 客户端的代码(假设您使用 Apache Http 客户端)

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

回答by venom.unleashed

Well I also faced this same situation and to solve it I took help from same blog post(http://nelenkov.blogspot.in/2011/12/using-custom-certificate-trust-store-on.html) referred by R4j. Following are the steps involved :

好吧,我也遇到了同样的情况,为了解决这个问题,我从R4j引用的同一篇博文(http://nelenkov.blogspot.in/2011/12/using-custom-certificate-trust-store-on.html)中寻求帮助. 以下是涉及的步骤:

  1. Create custom trustore: I used Portecle to create a keystore and imported Public Key Certificate from my server into it.
  2. Create custom keystore with keypair: 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. You then use them as mentioned in nelkov'sblog. You may also need to create your own custom AbstractVerifierin case you land into a situation where certificate is issued for abc.com and the Verifier rejects www.abc.com
  4. Finally to create secure HTTPClient you can do something like this:

            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;
    }
    
  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" -keypasswelcome123 -validity 365 -storetype pkcs12 -keystore g:\mordor_key_store.pfx -storepasswelcome123 -keysize 2048
  3. 然后,您可以按照nelkov 的博客中所述使用它们。您可能还需要创建自己的自定义AbstractVerifier,以防您遇到为 abc.com 颁发证书而 Verifier 拒绝 www.abc.com 的情况
  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;
    }
    

For detailed reasons of my choices you can refer to this post http://fuking-android.quora.com/Implement-HTTPS-for-android-apps-a-novices-tale.

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

回答by R4j

It is similar my questionwhen I try to request to EWS. You can refer to this linkand download example source codethen modify it like my answer. Hope this helps!

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

Update:
The following command worked for me (I tried it about 2 months ago):

更新
以下命令对我有用(我大约 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

You can see, I use bcprov-jdk16-145.jarand openssl lib. You can try it.
Another tool to create keystore: http://portecle.sourceforge.net/

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