简体   繁体   English

适用于Android的自签名证书和loopj

[英]Self-signed certificate and loopj for Android

I'm trying to use loopj for making async HTTP requests. 我正在尝试使用loopj来发出异步 HTTP请求。 Works great, except when I try to access https site with self-signed cert. 工作得很好,除非我尝试使用自签名证书访问https站点。 I get 我明白了

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

I guess the default ssl options can be overriding using setSSLSocketFactory(SSLSocketFactory sslSocketFactory) method, but I'm not sure how to do it or it might not be the right way at all. 我想默认的ssl选项可以使用setSSLSocketFactory(SSLSocketFactory sslSocketFactory)方法覆盖,但我不知道该怎么做,或者它可能根本不是正确的方法。

Please suggest how can I solve this issue ? 请建议我该如何解决这个问题?

You do it almost exactly the same as explained here for HttpClient except a little bit simpler - Trusting all certificates using HttpClient over HTTPS 你做的几乎与HttpClient所解释的完全相同,只是稍微简单一点 - 通过HTTPS信任使用HttpClient的所有证书

Create a custom class: 创建自定义类:

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

Then when you create your client instance: 然后在创建客户端实例时:

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

You can use constructor AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort). 您可以使用构造函数AsyncHttpClient(boolean fixNoHttpResponseException,int httpPort,int httpsPort)。 From version loopj library 1.4.4 and bigger. 从版本loopj库1.4.4和更大。 For example 例如

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

and you get warning message to logcat at the Verbose log. 并在详细日志中收到logcat的警告消息。

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

Simpler way is to use built-in MySSLSocketFactory in loopj, so you don't have to create another class 更简单的方法是在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) {}

As explained in many places simply bypassing verification of the certificates is wrong on so many levels. 正如许多地方所解释的那样,在很多层面上绕过证书的验证是错误的 Do not do that! 不要那样做!

What you should do instead is to create .bks file from your cert(for that purpose you gonna need Bouncy Castle ): 你应该做的是从你的证书创建.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

Next place your newly created outputname.bks inside res/raw folder. 接下来将新创建的outputname.bks放在res/raw文件夹中。

Create helper function(it could be inside own class or whatever you like): 创建辅助函数(它可以在自己的类或任何你喜欢的内容):

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

And last but not least, set your AsyncHttpClient to use the new socket factory: 最后但并非最不重要的是,将AsyncHttpClient设置为使用新的套接字工厂:

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

有了Httpscertificate我已经在两个文档HttpsUrlConnectionPortecle的帮助下成功完成了它。

Don't NUKE all SSL certificates.. Trusting all certificates is a BAD PRACTICE!!! 不要NUKE所有SSL证书..信任所有证书是一个不好的做法!

  • Accept only your SSL certificate. 仅接受您的SSL证书。

Take a look at my solution. 看看我的解决方案。 Some contents from this Gist can help your to figure how to do this. 此Gist中的一些内容可以帮助您了解如何执行此操作。

OBS.: I'm using Android Volley . OBS:我正在使用Android Volley

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM