Android 使用自签名证书接受HTTPS连接

Android 使用自签名证书接受HTTPS连接,android,ssl,https,httpclient,ca,Android,Ssl,Https,Httpclient,Ca,我试图使用HttpClientlib建立HTTPS连接,但问题是,由于证书没有由Android可信证书集中列出的、等可识别的证书颁发机构(CA)签名,因此我一直得到javax.net.ssl.SSLException:不受信任的服务器证书 我见过一些简单地接受所有证书的解决方案,但是如果我想问用户怎么办 我想得到一个类似于浏览器的对话框,让用户决定是否继续。我最好使用与浏览器相同的证书存储。有什么想法吗?您需要做的第一件事是设置验证级别。 这样的水平并不重要: 允许所有主机名验证程序 浏览器\

我试图使用
HttpClient
lib建立HTTPS连接,但问题是,由于证书没有由Android可信证书集中列出的、等可识别的证书颁发机构(CA)签名,因此我一直得到
javax.net.ssl.SSLException:不受信任的服务器证书

我见过一些简单地接受所有证书的解决方案,但是如果我想问用户怎么办


我想得到一个类似于浏览器的对话框,让用户决定是否继续。我最好使用与浏览器相同的证书存储。有什么想法吗?

您需要做的第一件事是设置验证级别。 这样的水平并不重要:

  • 允许所有主机名验证程序
  • 浏览器\兼容\主机名\验证器
  • 严格主机名验证程序
虽然setHostnameVerifier()方法对于新库apache已经过时,但是对于Android SDK中的版本来说是正常的。 因此我们采用
ALLOW\u ALL\u HOSTNAME\u VERIFIER
并在方法factory
SSLSocketFactory.setHostnameVerifier()中设置它

接下来,您需要将协议的工厂设置为https。为此,只需调用
SchemeRegistry.register()
方法

然后您需要使用
SingleClientConnManager
创建一个
DefaultHttpClient
。 在下面的代码中,您还可以看到,默认情况下,还将通过方法
HttpsURLConnection.setDefaultHostnameVerifier()使用我们的标志(
ALLOW\u ALL\u HOSTNAME\u VERIFIER

以下代码适用于我:

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

您需要做的第一件事是设置验证级别。 这样的水平并不重要:

  • 允许所有主机名验证程序
  • 浏览器\兼容\主机名\验证器
  • 严格主机名验证程序
虽然setHostnameVerifier()方法对于新库apache已经过时,但是对于Android SDK中的版本来说是正常的。 因此我们采用
ALLOW\u ALL\u HOSTNAME\u VERIFIER
并在方法factory
SSLSocketFactory.setHostnameVerifier()中设置它

接下来,您需要将协议的工厂设置为https。为此,只需调用
SchemeRegistry.register()
方法

然后您需要使用
SingleClientConnManager
创建一个
DefaultHttpClient
。 在下面的代码中,您还可以看到,默认情况下,还将通过方法
HttpsURLConnection.setDefaultHostnameVerifier()使用我们的标志(
ALLOW\u ALL\u HOSTNAME\u VERIFIER

以下代码适用于我:

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

以下主要步骤是从android平台不信任的认证机构实现安全连接所必需的

根据许多用户的要求,我在此处镜像了我的系统中最重要的部分:

  • 获取所有必需的证书(根证书和任何中间证书)
  • 使用keytool和提供程序创建密钥库并导入证书
  • 在android应用程序中加载密钥库并将其用于安全连接(我建议使用而不是标准的
    java.net.ssl.HttpsURLConnection
    (更容易理解,性能更高)
  • 拿到证书 您必须获取从端点证书一直到根CA的所有构建链的证书。这意味着,任何(如果存在)中间CA证书以及根CA证书。您不需要获取端点证书

    创建密钥库 下载并将其存储到已知位置。 还要确保可以调用keytool命令(通常位于JRE安装的bin文件夹下)

    现在将获得的证书(不导入端点证书)导入到BouncyCastle格式的密钥库中

    我没有测试它,但我认为导入证书的顺序很重要。这意味着,首先导入最低的中间CA证书,然后一直导入根CA证书

    使用以下命令,将创建密码为mysecret的新密钥库(如果尚未存在),并导入中间CA证书。我还定义了BouncyCastle提供程序,可以在我的文件系统和密钥库格式中找到它。对链中的每个证书执行此命令

    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
    
    验证证书是否已正确导入密钥库:

    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
    
    应输出整个链:

    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
    
    现在,您可以将密钥库作为原始资源复制到android应用程序的
    res/raw/

    使用应用程序中的密钥库 首先,我们必须创建一个自定义Apache HttpClient,它使用我们的密钥库进行HTTPS连接:

    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);
            }
        }
    }
    
    我们已经创建了自定义HttpClient,现在可以将其用于安全连接。例如,当我们对REST资源进行GET调用时:

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

    就是这样;)

    要从android平台不信任的认证机构实现安全连接,需要以下主要步骤

    根据许多用户的要求,我在此处镜像了我的系统中最重要的部分:

  • 获取所有必需的证书(根证书和任何中间证书)
  • 使用keytool和提供程序创建密钥库并导入证书
  • 在android应用程序中加载密钥库并将其用于安全连接(我建议使用而不是标准的
    java.net.ssl.HttpsURLConnection
    (更容易理解,性能更高)
  • 拿到证书 您必须获取从端点证书一直到根CA的所有构建链的证书。这意味着,任何(如果存在)中间CA证书以及根CA证书。您不需要获取端点证书

    创建密钥库 下载并将其存储到已知位置。 还要确保可以调用keytool命令(通常位于bin文件夹下)
    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();
    
     /**
     * Set up a connection to myservice.domain using HTTPS. An entire function
     * is needed to do this because myservice.domain has a self-signed certificate.
     * 
     * The caller of the function would do something like:
     * HttpsURLConnection urlConnection = setUpHttpsConnection("https://littlesvr.ca");
     * InputStream in = urlConnection.getInputStream();
     * And read from that "in" as usual in Java
     * 
     * Based on code from:
     * https://developer.android.com/training/articles/security-ssl.html#SelfSigned
     */
    public static HttpsURLConnection setUpHttpsConnection(String urlString)
    {
        try
        {
            // Load CAs from an InputStream
            // (could be from a resource or ByteArrayInputStream or ...)
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
    
            // My CRT file that I put in the assets folder
            // I got this file by following these steps:
            // * Go to https://littlesvr.ca using Firefox
            // * Click the padlock/More/Security/View Certificate/Details/Export
            // * Saved the file as littlesvr.crt (type X.509 Certificate (PEM))
            // The MainActivity.context is declared as:
            // public static Context context;
            // And initialized in MainActivity.onCreate() as:
            // MainActivity.context = getApplicationContext();
            InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("littlesvr.crt"));
            Certificate ca = cf.generateCertificate(caInput);
            System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
    
            // Create a KeyStore containing our trusted CAs
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);
    
            // Create a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);
    
            // Create an SSLContext that uses our TrustManager
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);
    
            // Tell the URLConnection to use a SocketFactory from our SSLContext
            URL url = new URL(urlString);
            HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
            urlConnection.setSSLSocketFactory(context.getSocketFactory());
    
            return urlConnection;
        }
        catch (Exception ex)
        {
            Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString());
            return null;
        }
    }
    
    OkHttpClient client = new OkHttpClient();
    SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
    client.setSslSocketFactory(sslContext.getSocketFactory());
    
    <?php
    
    caronte ("https://www.secure-example.com/page.php");
    
    function caronte($url) {
        // build curl request
        $ch = curl_init();
        foreach ($_POST as $a => $b) {
            $post[htmlentities($a)]=htmlentities($b);
        }
        curl_setopt($ch, CURLOPT_URL,$url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post));
    
        // receive server response ...
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $server_output = curl_exec ($ch);
        curl_close ($ch);
    
        echo $server_output;
    }
    
    ?>
    
      private fun disableSSLCertificateChecking() {
            val hostnameVerifier = object: HostnameVerifier {
                override fun verify(s:String, sslSession: SSLSession):Boolean {
                    return true
                }
            }
            val trustAllCerts = arrayOf<TrustManager>(object: X509TrustManager {
                override fun getAcceptedIssuers(): Array<X509Certificate> {
                    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
                }
    
                //val acceptedIssuers:Array<X509Certificate> = null
                @Throws(CertificateException::class)
                override fun checkClientTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
                }
                @Throws(CertificateException::class)
                override fun checkServerTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
                }
            })
            try
            {
                val sc = SSLContext.getInstance("TLS")
                sc.init(null, trustAllCerts, java.security.SecureRandom())
                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory())
                HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier)
            }
            catch (e: KeyManagementException) {
                e.printStackTrace()
            }
            catch (e: NoSuchAlgorithmException) {
                e.printStackTrace()
            }
        }