使用本地信任时同行握手错误的Android HttpsURLConnection的javax.net

2019-06-28 02:29发布

我在与Android的获取连接到一个简单的麻烦OpenSSL使用服务器HttpsUrlConnection对象(我已经通过StackOverflow的和一堆在线教程的梳理,并随访线的例子相当多行,我仍然想不通为什么当我用我的本地信任我的坏)。

我现在有一个尝试连接到一个简单的安卓活动OpenSSL server (我可以连接到使用OpenSSL的客户我的服务器),一旦HttpsUrlConnection.connect()被调用我收到“ javax.net.ssl.SSLException: Connection closed by peer" error during the SSL handshake. 也许我错误地设置了我的样本服务器?

需要注意的事项:

  • 目前没有客户授权
  • 我能够连接到https://www.google.com加载默认的信任存储时
  • 我不能够使用自签名证书连接到服务器上本地主机
  • 不想信任所有证书
  • 不想使用Apache的HttpClient
  • 希望只使用本地信任
  • 创建的本地信任与充气城堡
  • 我能够正确地信任装入
  • 代理防火墙的后面,代理服务器设为我的Android虚拟设备上
  • AVD设置Android 4.1 API 16

事情我已经尝试:

  • 连接到两个127.0.0.1 and 10.0.2.2
  • 使用新SecureRandom() with the SSLContext.init()
  • 创建与所述URL 'URL u = new URL("https", "10.0.2.2", 443, "/");'
  • 使用TrustManagerFactory.getDefaultAlgorithms()而不是“X509”
    • 给出"Unexpected response code error 503" ,而不是“连接由对等封闭”

预先感谢您在百忙之中阅读我的问题的时候了!

简单的服务器开始使用命令:

$ sudo openssl s_server -accept 443 -cert server-cert.pem -key server-key.pem -pass file:passphrase.txt -state -www -verify 0

客户机连接以命令进行测试:

$ openssl s_client -connect 127.0.0.1:443 

Android的活动代码(编辑删除完整的运行代码简化 - 请让我知道,如果需要更多代码) - 错误输出是下面的代码。

    try {
        TrustManagerFactory tmf;

        // local trust store
        tmf = TrustManagerFactory.getInstance("X509");
        tmf.init(loadLocalKeyStore(getApplicationContext()));

        // default trust store - works for https://www.google.com
        // tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        // tmf.init((KeyStore) null);

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
        URL u = new URL("https://10.0.2.2");

        HttpsURLConnection urlConnection = (HttpsURLConnection) u.openConnection();

        urlConnection.setSSLSocketFactory(context.getSocketFactory());
        urlConnection.setHostnameVerifier(hostnameVerifier);
        urlConnection.connect();

        System.out.println("Response Code: " + urlConnection.getResponseCode());
        System.out.println("Response Code: " + urlConnection.getCipherSuite());
    } 

    ...

    private KeyStore loadLocalKeyStore(Context context) {
        InputStream in = context.getResources().openRawResource(R.raw.newserverkeystore);
        KeyStore trusted = null;
        try {
           trusted = KeyStore.getInstance("BKS");
           trusted.load(in, "thisisasecret".toCharArray());
        } finally {
           in.close();
        }
       return trusted;
    }

正确连接到时输出https://www.google.com :

09-09 21:58:09.947: I/System.out(669): Response Code: 200
09-09 21:58:09.947: I/System.out(669): Response Code: TLS_ECDHE_RSA_WITH_RC4_128_SHA

当输出试图连接到我的自签名证书服务器:

09-09 22:03:23.377: D/HttpsProxy(717): Https Request error
09-09 22:03:23.377: D/HttpsProxy(717): javax.net.ssl.SSLException: Connection closed by peer
09-09 22:03:23.377: D/HttpsProxy(717):  at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
09-09 22:03:23.377: D/HttpsProxy(717):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:395)
09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:210)
09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:442)
09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:289)
09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:239)
09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:165)
09-09 22:03:23.377: D/HttpsProxy(717):  at com.example.myfirstapp.HttpsUrlConnectionActivity$3.doInBackground(HttpsUrlConnectionActivity.java:257)
09-09 22:03:23.377: D/HttpsProxy(717):  at com.example.myfirstapp.HttpsUrlConnectionActivity$3.doInBackground(HttpsUrlConnectionActivity.java:1)
09-09 22:03:23.377: D/HttpsProxy(717):  at android.os.AsyncTask$2.call(AsyncTask.java:287)
09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.FutureTask.run(FutureTask.java:137)
09-09 22:03:23.377: D/HttpsProxy(717):  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
09-09 22:03:23.377: D/HttpsProxy(717):  at java.lang.Thread.run(Thread.java:856)

再次感谢!!

Answer 1:

我解决我的问题 - 我需要使用证书与10.0.2.2作为公共名称(CN),所以它匹配的10.0.2.2,而不是“本地主机”或“127.0.0.1”的Android本地主机的IP地址。

编辑:你也许可以创建localhost作为CN和“127.0.0.1” '10证书.0.2.2' 作为主题备用名称(SAN)。

有一次,我创建10.0.2.2证书和私钥PEM文件,我可以打我的服务器使用下面的命令运行:

openssl s_server -accept 8888 -cert 10.0.2.2-cert.pem -key 10.0.2.2-key.pem  -state -www

如果要强制客户端提供证书(虽然它不会被选中),标志添加-Verify 1上面的命令。

为了你可以使用下面的命令行测试服务器(注OpenSSL是能够通过127.0.0.1连接):

openssl s_client -connect 127.0.0.1:8888

并且如果服务器需要将它添加一个客户端证书,添加标志-cert client-cert.pem -key client-key.pem

在我的Android客户端我用下面的代码连接(检查删除错误):

// use local trust store (CA)
TrustManagerFactory tmf;
KeyStore trustedStore = null;
InputStream in = context.getResources().openRawResource(R.raw.mycatruststore); // BKS in res/raw
trustedStore = KeyStore.getInstance("BKS");
trustedStore.load(in, "insertBksPasswordHere".toCharArray());
tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustedStore);

// load client certificate
KeyStore clientKeyStore = loadClientKeyStore(getApplicationContext());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(clientKeyStore, "insertPasswordHere".toCharArray());

SSLContext context = SSLContext.getInstance("TLS");

// provide client cert - if server requires client cert this will pass
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;

// connect to url
URL u = new URL("https://10.0.2.2:8888/");
HttpsURLConnection urlConnection = (HttpsURLConnection) u.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setHostnameVerifier(hostnameVerifier);
urlConnection.connect();
System.out.println("Response Code: " + urlConnection.getResponseCode());

你应该得到的200的响应代码,并且可以剖析从那里的响应。

下面的代码加载客户端的凭据,这是相同的加载服务器密钥存储,但使用不同的资源文件名和密码:

private KeyStore loadClientKeyStore(Context context) {
    InputStream in = context.getResources().openRawResource(R.yourKeyStoreFile);
    KeyStore trusted = null;
    trusted = KeyStore.getInstance("BKS");
    trusted.load(in, "yourClientPassword".toCharArray());
    in.close();
    return trusted;
}


Answer 2:

我浪费了我的6 - 7小时固定的这个问题,并最终将其与合作

public void URLConnection(String webUrl) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        //TLSSocketFactory objTlsSocketFactory = new TLSSocketFactory();
        URL url = new URL(webUrl);
        HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
        urlConnection.setRequestMethod("GET");
        //urlConnection.setSSLSocketFactory(objTlsSocketFactory);

        int responseCode = urlConnection.getResponseCode();
        System.out.println("\nSending 'GET' request to URL : " + url);
        System.out.println("Response Code : " + responseCode);

        BufferedReader in = new BufferedReader(
                new InputStreamReader(urlConnection.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        //print result
        System.out.println(response.toString());
    }

和它的工作!!!!!!



文章来源: Android HttpsUrlConnection javax.net.ssl.SSLException Connection closed by peer handshake error when using local truststore