Android Https连接自签名证书:hostName未验证

问题描述:

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

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

所以,当我连接,我得到: 了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; 
    } 
} 

我从http://developer.android.com/training/articles/security-ssl.html#SelfSigned

得到这个代码,我得到这个错误对三星Galaxy S4搭载Android 4.3

我对HTTPS没有太多的经验,所以在这里我想实现的是使用应用程序验证服务器证书的证书。 请如果有人可以建议我可以在代码中修改。

我也有一个疑问,因为我的服务器是一个.local服务器,但我使用IP连接,目标是能够连接使用两个IP主机名,这将是问题时,找到主机名? 感谢很多提前

编辑:我添加代码来获取主机名:

InetAddress addr = InetAddress.getByName(SERVER_URL); 
String hostName = addr.getHostName(); 

我一直在使用主机名而不是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) 

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

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 /应用程序的原始文件夹

那么常见我已经把我的服务器的FQDN如host.name.com名称字段,然后,我更新了cerificate和在/ etc键设置/ Apache2的/网站可用/默认的SSL

这一切都归功于网站https://library.linode.com/web-servers/apache/ssl-guides/ubuntu-10.04-lucid

+0

在windows中是什么解决方案? – CoronaPintu

+0

对不起,我不使用窗口,所以不知道,你应该创建一个新的职位,因为你的问题可能完全不同于我的。另外要注意的是,在Android中,有时候IP地址被“拒绝”,所以你应该提供你自己的主机名称验证器 – vallllll