Java 具有自签名证书的Android Https连接:主机名未验证

Java 具有自签名证书的Android Https连接:主机名未验证,java,android,ssl,https,ubuntu-12.04,Java,Android,Ssl,Https,Ubuntu 12.04,嗨,我正在用证书测试Android连接。 我已经在我的ubuntu桌面上创建了一个默认的ssl服务器。启用ssl并创建默认的自签名证书。然后我已连接到https://localhost使用firefox,将证书添加到异常中,然后我使用firefox将证书另存为.pem文件。 我在res/raw中将certificate.pem添加到我的android项目中 我已经从android开发者网站获得了这段代码,可以使用我的证书通过https进行连接(我不想相信我只想使用原始文件夹中的证书验证certi

嗨,我正在用证书测试Android连接。 我已经在我的ubuntu桌面上创建了一个默认的ssl服务器。启用ssl并创建默认的自签名证书。然后我已连接到
https://localhost
使用firefox,将证书添加到异常中,然后我使用firefox将证书另存为.pem文件。 我在res/raw中将certificate.pem添加到我的android项目中

我已经从android开发者网站获得了这段代码,可以使用我的证书通过https进行连接(我不想相信我只想使用原始文件夹中的证书验证certicate是否正确的一切)

因此,当我连接时,我得到: java.lang.RuntimeException:java.io.IOException:未验证主机名“192.168.1.111”

下面是我用来验证证书的类

public class VerifyKey extends AsyncTask<Void, Void, Void>{

    public static final String CERTIFICATE_TYPE_X_509 = "X.509";
    public static final String CERTIFICATE_ALIAS = "user_desktop";
    public static final String SERVER_URL = "https://192.168.1.111";
    @Override
    protected Void doInBackground(Void... params) {
        // Load CAs from an InputStream
        // (could be from a resource or ByteArrayInputStream or ...)
        CertificateFactory cf = null;
        InputStream certificateInputStream = getApplicationContext().getResources().openRawResource(R.raw.user_desktop);
        Certificate certificate = null;
        try {
            cf = CertificateFactory.getInstance(CERTIFICATE_TYPE_X_509);
            certificate = cf.generateCertificate(certificateInputStream);
            Log.d(TAG, "Certificate : " + certificate.toString());
            Log.d(TAG, "Certificate public key : " + certificate.getPublicKey());
        } catch (CertificateException e) {
            throw new RuntimeException(e);
        } finally {
            if (certificateInputStream != null) {
                try {
                    certificateInputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = null;
        try {
            keyStore = KeyStore.getInstance(keyStoreType);
            if (keyStore != null) {
                keyStore.load(null, null);
                keyStore.setCertificateEntry(CERTIFICATE_ALIAS, certificate);
            } else {
                throw new RuntimeException("KeyStore is null");
            }
        } catch (KeyStoreException e) {
            throw new RuntimeException(e);
        } catch (CertificateException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = null;
        try {
            tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            if (tmf != null) {
                tmf.init(keyStore);
            } else {
                throw new RuntimeException("TrustManagerFactory is null");
            }
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (KeyStoreException e) {
            throw new RuntimeException(e);
        }

        // Create an SSLContext that uses our TrustManager
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");

            TrustManager[] trustManagers = tmf.getTrustManagers();
            sslContext.init(null, trustManagers, null);

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (KeyManagementException e) {
            throw new RuntimeException(e);
        }

        // Tell the URLConnection to use a SocketFactory from our SSLContext
        URL url = null;
        HttpsURLConnection httpsURLConnection =
                null;
        InputStream in = null;
        try {
            url = new URL(SERVER_URL);
            Log.d(TAG, "URL : "+url.toString());
            httpsURLConnection = (HttpsURLConnection)url.openConnection();
            SSLSocketFactory socketFactory = sslContext.getSocketFactory();
            Log.d(TAG, "Socket factory : "+socketFactory.toString());
            httpsURLConnection.setSSLSocketFactory(socketFactory);

            in = httpsURLConnection.getInputStream(); //IOException exception gets triggered here

            Toast.makeText(getApplicationContext(), in.toString(), Toast.LENGTH_SHORT).show();
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (SSLHandshakeException e){
            throw new RuntimeException(e);
        } catch(UnknownHostException e){
            throw new RuntimeException(e);
        } catch (ConnectException e1){
            throw new RuntimeException(e1);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;
    }
}
我尝试过使用主机名而不是ip,但仍然得到相同的异常:

Caused by: java.io.IOException: Hostname '<user.hostname.com>' was not verified
    at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
    at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
    at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
    at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
    at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
    at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
    at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
原因:java.io.IOException:未验证主机名“”
位于libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
位于libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
位于libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
位于libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
位于libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
位于libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
位于libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)

我已经设法解决了这个问题,实际上是证书的问题:

sudo a2enmod ssl
sudo rm -rf /etc/apache2/ssl
sudo mkdir /etc/apache2/ssl
sudo openssl req -new -x509 -days 365 -nodes -out /etc/apache2/ssl/apache.pem -keyout /etc/apache2/ssl/apache.key
将/etc/apache2/ssl/apache.pem复制到其他地方,将其权限更改为777(?) 然后将新的apache.pem添加到应用程序的res/raw文件夹中

然后在common name字段中,我设置了我的服务器(如host.name.com)的FQDN,然后更新了/etc/apache2/sites available/default ssl中的证书和密钥设置


所有这些都要感谢windows中的站点

什么是解决方案?很抱歉,我不使用windows,所以不知道,您应该创建一个新帖子,因为您的问题可能与我的完全不同。还要注意,在Android中,有时ip地址会被“拒绝”,所以您应该提供自己的主机名验证程序
sudo a2enmod ssl
sudo rm -rf /etc/apache2/ssl
sudo mkdir /etc/apache2/ssl
sudo openssl req -new -x509 -days 365 -nodes -out /etc/apache2/ssl/apache.pem -keyout /etc/apache2/ssl/apache.key