【问题标题】:Self-signed certificate and loopj for Android适用于 Android 的自签名证书和 loopj
【发布时间】:2012-07-19 08:40:59
【问题描述】:

我正在尝试使用 loopj 发出 async HTTP 请求。效果很好,除非我尝试使用自签名证书访问 https 站点。我明白了

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate.

我猜默认的 ssl 选项可以使用setSSLSocketFactory(SSLSocketFactory sslSocketFactory) 方法覆盖,但我不知道该怎么做,或者它可能根本不是正确的方法。

请建议我该如何解决这个问题?

【问题讨论】:

标签: android loopj


【解决方案1】:

Httpscertificate 的帮助下,我在HttpsUrlConnectionPortecle 两个文档的帮助下成功地做到了。

【讨论】:

    【解决方案2】:

    您的操作几乎与此处对 HttpClient 的说明完全相同,只是稍微简单一点 - Trusting all certificates using HttpClient over HTTPS

    创建自定义类:

    import java.io.IOException;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.security.KeyManagementException;
    import java.security.KeyStore;
    import java.security.KeyStoreException;
    import java.security.NoSuchAlgorithmException;
    import java.security.UnrecoverableKeyException;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    
    import org.apache.http.conn.ssl.SSLSocketFactory;
    public class MySSLSocketFactory extends SSLSocketFactory {
        SSLContext sslContext = SSLContext.getInstance("TLS");
    
        public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
            super(truststore);
    
            TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }
    
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }
    
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
    
            sslContext.init(null, new TrustManager[] { tm }, null);
        }
    
        @Override
        public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
        }
    
        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }
    }
    

    然后当你创建你的客户端实例时:

    try {
          KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
          trustStore.load(null, null);
          sf = new MySSLSocketFactory(trustStore);
          sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
          client.setSSLSocketFactory(sf);   
        }
        catch (Exception e) {   
        }
    

    【讨论】:

    • 这很有魅力!我喜欢您引用了另一个示例,但在此处专门对其进行了修改以与 loopj 的异步 http 客户端一起使用。谢谢!
    • 我必须补充一点,我最终对 loopj 库感到沮丧并改用 basic-http-client 库 - code.google.com/p/basic-http-client
    • 这样做是删除所有安全功能。出现该错误消息是有原因的 - 您应该拥有正确的证书而不是禁用证书检查!
    • 我开始以这种方式实施解决方法,然后我阅读了developer.android.com/training/articles/security-ssl.html,其中特别警告不要提供什么都不做的 TrustManager。由于我的应用程序无论如何都需要有效的客户端证书,因此我使用的是该文章中的“默认”TM。在我的 SSLSocketFactory 构造函数中:TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); sslContext.init(null, tmf.getTrustManagers(), null);
    • 这个例子信任每一个证书,但是如果我只想信任我自己的服务器证书怎么办呢??
    【解决方案3】:

    您可以使用构造函数 AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort)。从版本 loopj 库 1.4.4 和更大。例如

    mClient = new AsyncHttpClient(true, 80, 443);
    

    并且您会在详细日志中收到警告消息给 logcat。

    Beware! Using the fix is insecure, as it doesn't verify SSL certificates.
    

    【讨论】:

    • 你怎么知道httpPorthttpsPort使用什么?
    • 当您查看文档 loopj.com/android-async-http/doc/com/loopj/android/http/… 时。当您在 REST API 上连接时,这些端口正在使用您的服务器,并且您知道端口如何使用您的服务器;)。当然,您可以更通用,您可以实现 config.xml 并将其切换为依赖构建 gradle 风格或在 gradle 中使用常量并引用您的 BuildConfig 对象。或者最简单的你可以使用不带参数的构造函数 new AsyncHttpClient() 因为http端口一般是80和https 443是一样的或者直接写到url
    • 我在 4.3 中仍然遇到 SSL 握手错误,你知道吗?
    【解决方案4】:

    更简单的方法是在loopj中使用内置的MySSLSocketFactory,所以你不必创建另一个类

    try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
            MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            client.setSSLSocketFactory(sf);
    }
    catch (Exception e) {}
    

    【讨论】:

    • 谢谢,成功了!我在 AsyncHttpClient client = new AsyncHttpClient(); 实例化之后和 client.post(...) 调用之前放置了这段代码。
    【解决方案5】:

    正如许多地方所解释的那样,在很多层面上,绕过证书验证是错误的。不要那样做!

    您应该做的是从您的证书创建.bks 文件(为此,您需要Bouncy Castle):

    keytool -importcert -v -trustcacerts -file "path/to/certfile/certfile.crt" -alias IntermediateCA -keystore "outputname.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path/to/bouncycastle/bcprov-jdk15on-154.jar" -storetype BKS -storepass atleastsix
    

    接下来将您新创建的outputname.bks 放入res/raw 文件夹中。

    创建辅助函数(可以在自己的类中或任何你喜欢的):

    private static SSLSocketFactory getSocketFactory(Context ctx) {
            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 = ctx.getResources().openRawResource(R.raw.outputname); //name of your keystore file here
                try {
                    // Initialize the keystore with the provided trusted certificates
                    // Provide the password of the keystore
                    trusted.load(in, "atleastsix".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); // This can be changed to less stricter verifiers, according to need
                return sf;
            } catch (Exception e) {
                throw new AssertionError(e);
            }
        }
    

    最后但同样重要的是,设置您的 AsyncHttpClient 以使用新的套接字工厂:

    AsyncHttpClient client = new AsyncHttpClient();
    client.setSSLSocketFactory(getSocketFactory(context));
    

    【讨论】:

    • 这很棒!太感谢了!我遇到的问题是这条线:MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
    • 谢谢!这是第一个实际上是安全的答案。所有其他答案似乎都接受所有证书。这就像根本不使用 https。
    • 谢谢!我认为我们必须学习 ssl/openssl/keytool/KeyStore.getINstance("{KeyStore type}") 作为 "BKS","AndroidCAStore","AndroidKeyStore","BCPKCS12","BKS","BouncyCastle","PKCS12 ","PKCS12-DEF" 优先。现在我将使用 Asynchttp 更改为 Retrofit2。
    • 谢谢,它有效。我不同意 insomnia,BKS 是 Android 中常见的格式,所以我看不出有什么问题。我只有一个投诉 - SSLSocketFactory 现在已被弃用。这可能是库中的错误。 SSLSocketFactory 指向 SSLConnectionSocketFactory,但 AsyncHttpClient 中的所有代码都链接到已弃用的类 SSLSocketFactory。所以我们不能将 SSLConnectionSocketFactory 与 AsyncHttpClient 一起使用。
    【解决方案6】:

    不要 NUKE 所有 SSL 证书。信任所有证书是一种不好的做法!!!

    • 只接受您的 SSL 证书。

    看看我的解决方案。此 Gist 中的一些内容可以帮助您了解如何执行此操作。

    OBS.:我正在使用 Android Volley

    https://gist.github.com/ivanlmj/f11fb50d35fa1f2b9698bfb06aedcbcd

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-19
      • 2021-10-03
      • 1970-01-01
      • 2021-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多