accepting HTTPS connections with self-signed certificates

前端 未结 13 2097
小蘑菇
小蘑菇 2020-11-22 04:20

I\'m trying to make HTTPS connections, using HttpClient lib, but the problem is that, since the certificate isn\'t signed by a recognized Certificate Authority

相关标签:
13条回答
  • 2020-11-22 04:46

    The first thing you need to do is to set the level of verification. Such levels is not so much:

    • ALLOW_ALL_HOSTNAME_VERIFIER
    • BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
    • STRICT_HOSTNAME_VERIFIER

    Although the method setHostnameVerifier() is obsolete for new library apache, but for version in Android SDK is normal. And so we take ALLOW_ALL_HOSTNAME_VERIFIER and set it in the method factory SSLSocketFactory.setHostnameVerifier().

    Next, You need set our factory for the protocol to https. To do this, simply call the SchemeRegistry.register() method.

    Then you need to create a DefaultHttpClient with SingleClientConnManager. Also in the code below you can see that on default will also use our flag (ALLOW_ALL_HOSTNAME_VERIFIER) by the method HttpsURLConnection.setDefaultHostnameVerifier()

    Below code works for me:

    HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
    
    DefaultHttpClient client = new DefaultHttpClient();
    
    SchemeRegistry registry = new SchemeRegistry();
    SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
    socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
    registry.register(new Scheme("https", socketFactory, 443));
    SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
    DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());
    
    // Set verifier     
    HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
    
    // Example send http request
    final String url = "https://encrypted.google.com/";
    HttpPost httpPost = new HttpPost(url);
    HttpResponse response = httpClient.execute(httpPost);
    
    0 讨论(0)
  • 2020-11-22 04:51

    Google recommends the usage of Android Volley for HTTP/HTTPS connections, since that HttpClient is deprecated. So, you know the right choice :).

    And also, NEVER NUKE SSL Certificates (NEVER!!!).

    To nuke SSL Certificates, is totally against the purpose of SSL, which is promoting security. There's no sense of using SSL, if you're planning to bomb all SSL certificates that comes. A better solution would be, not using SSL, or a better solution, would be creating a custom TrustManager on your App + using Android Volley for HTTP/HTTPS connections.

    Here's a Gist which I created, with a basic LoginApp, performing HTTPS connections, using a Self-Signed Certificate on the server-side, accepted on the App.

    Here's also another Gist that may help, for creating Self-Signed SSL Certificates for setting up on your Server and also using the certificate on your App. Very important: you must copy the .crt file which was generated by the script above, to the "raw" directory from your Android project.

    0 讨论(0)
  • 2020-11-22 04:55

    This is problem resulting from lack of SNI(Server Name Identification) support inA,ndroid 2.x. I was struggling with this problem for a week until I came across the following question, which not only gives a good background of the problem but also provides a working and effective solution devoid of any security holes.

    'No peer certificate' error in Android 2.3 but NOT in 4

    0 讨论(0)
  • 2020-11-22 04:56

    The following main steps are required to achieve a secured connection from Certification Authorities which are not considered as trusted by the android platform.

    As requested by many users, I've mirrored the most important parts from my blog article here:

    1. Grab all required certificates (root and any intermediate CA’s)
    2. Create a keystore with keytool and the BouncyCastle provider and import the certs
    3. Load the keystore in your android app and use it for the secured connections (I recommend to use the Apache HttpClient instead of the standard java.net.ssl.HttpsURLConnection (easier to understand, more performant)

    Grab the certs

    You have to obtain all certificates that build a chain from the endpoint certificate the whole way up to the Root CA. This means, any (if present) Intermediate CA certs and also the Root CA cert. You don’t need to obtain the endpoint certificate.

    Create the keystore

    Download the BouncyCastle Provider and store it to a known location. Also ensure that you can invoke the keytool command (usually located under the bin folder of your JRE installation).

    Now import the obtained certs (don’t import the endpoint cert) into a BouncyCastle formatted keystore.

    I didn’t test it, but I think the order of importing the certificates is important. This means, import the lowermost Intermediate CA certificate first and then all the way up to the Root CA certificate.

    With the following command a new keystore (if not already present) with the password mysecret will be created and the Intermediate CA certificate will be imported. I also defined the BouncyCastle provider, where it can be found on my file system and the keystore format. Execute this command for each certificate in the chain.

    keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
    

    Verify if the certificates were imported correctly into the keystore:

    keytool -list -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
    

    Should output the whole chain:

    RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
    IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43
    

    Now you can copy the keystore as a raw resource in your android app under res/raw/

    Use the keystore in your app

    First of all we have to create a custom Apache HttpClient that uses our keystore for HTTPS connections:

    import org.apache.http.*
    
    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.mykeystore);
                try {
                    // Initialize the keystore with the provided trusted certificates
                    // Also provide the password of the keystore
                    trusted.load(in, "mysecret".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);
            }
        }
    }
    

    We have created our custom HttpClient, now we can use it for secure connections. For example when we make a GET call to a REST resource:

    // Instantiate the custom HttpClient
    DefaultHttpClient client = new MyHttpClient(getApplicationContext());
    HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
    // Execute the GET call and obtain the response
    HttpResponse getResponse = client.execute(get);
    HttpEntity responseEntity = getResponse.getEntity();
    

    That's it ;)

    0 讨论(0)
  • 2020-11-22 05:01

    The top answer didn´t work for me. After some investigation I found the required information on "Android Developer": https://developer.android.com/training/articles/security-ssl.html#SelfSigned

    Creating an empty implementation of X509TrustManager did the trick:

    private static class MyTrustManager implements X509TrustManager
    {
    
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
             throws CertificateException
        {
        }
    
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException
        {
        }
    
        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return null;
        }
    
    }
    
    ...
    
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    try
    {
        // Create an SSLContext that uses our TrustManager
        SSLContext context = SSLContext.getInstance("TLS");
        TrustManager[] tmlist = {new MyTrustManager()};
        context.init(null, tmlist, null);
        conn.setSSLSocketFactory(context.getSocketFactory());
    }
    catch (NoSuchAlgorithmException e)
    {
        throw new IOException(e);
    } catch (KeyManagementException e)
    {
        throw new IOException(e);
    }
    conn.setRequestMethod("GET");
    int rcode = conn.getResponseCode();
    

    Please be aware that this empty implementation of TustManager is just an example and using it in a productive environment would cause a severe security threat!

    0 讨论(0)
  • 2020-11-22 05:01

    Simplest way for create SSL certificate

    Open Firefox (I suppose it's also possible with Chrome, but it's easier for me with FF)

    Visit your development site with a self-signed SSL certificate.

    Click on the certificate (next to the site name)

    Click on "More information"

    Click on "View certificate"

    Click on "Details"

    Click on "Export..."

    Choose "X.509 Certificate whith chain (PEM)", select the folder and name to save it and click "Save"

    Go to command line, to the directory where you downloaded the pem file and execute "openssl x509 -inform PEM -outform DM -in .pem -out .crt"

    Copy the .crt file to the root of the /sdcard folder inside your Android device Inside your Android device, Settings > Security > Install from storage.

    It should detect the certificate and let you add it to the device Browse to your development site.

    The first time it should ask you to confirm the security exception. That's all.

    The certificate should work with any browser installed on your Android (Browser, Chrome, Opera, Dolphin...)

    Remember that if you're serving your static files from a different domain (we all are page speed bitches) you also need to add the certificate for that domain.

    0 讨论(0)
提交回复
热议问题