Android:使用 HttpsURLConnection 的 HTTPS (SSL) 连接

Posted

技术标签:

【中文标题】Android:使用 HttpsURLConnection 的 HTTPS (SSL) 连接【英文标题】:Android: HTTPS (SSL) connection using HttpsURLConnection 【发布时间】:2011-10-30 19:35:10 【问题描述】:

我有 2 个应用,一个是 Servlet/Tomcat Server,另一个是 android 应用。

我想使用 HttpURLConnection 在两者之间发送和接收 XML。

代码:

    private String sendPostRequest(String requeststring) 

    DataInputStream dis = null;
    StringBuffer messagebuffer = new StringBuffer();

    HttpURLConnection urlConnection = null;

    try 
        URL url = new URL(this.getServerURL());

        urlConnection = (HttpURLConnection) url.openConnection();           

        urlConnection.setDoOutput(true);

        urlConnection.setRequestMethod("POST");

        OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());

        out.write(requeststring.getBytes());

        out.flush();

        InputStream in = new BufferedInputStream(urlConnection.getInputStream());

        dis = new DataInputStream(in);

        int ch;

        long len = urlConnection.getContentLength();

        if (len != -1) 

            for (int i = 0; i < len; i++)

                if ((ch = dis.read()) != -1) 

                    messagebuffer.append((char) ch);
                
         else 

            while ((ch = dis.read()) != -1)
                messagebuffer.append((char) ch);
        

        dis.close();

     catch (Exception e) 

        e.printStackTrace();

     finally 

        urlConnection.disconnect();
    

    return messagebuffer.toString();

现在,为了安全起见,我需要使用 SSL 发送 XML。

首先,我使用 Java Keytool 生成 .keystore 文件。

Keytool  -keygen -alias tomcat -keyalg RSA

然后我将 XML 代码放在 Tomcat 的 server.xml 文件中以使用 SSL

<Connector 
port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="c:/Documents and Settings/MyUser/.keystore"
keystorePass="password"
clientAuth="false" sslProtocol="TLS" 
/>

然后,我将其更改为 HttpURLConnection 为 HttpsURLConnection

    private String sendPostRequest(String requeststring) 

    DataInputStream dis = null;
    StringBuffer messagebuffer = new StringBuffer();

    HttpURLConnection urlConnection = null;

    //Conexion por HTTPS
    HttpsURLConnection urlHttpsConnection = null;

    try 
        URL url = new URL(this.getServerURL());

        //urlConnection = (HttpURLConnection) url.openConnection();         

        //Si necesito usar HTTPS
        if (url.getProtocol().toLowerCase().equals("https")) 

            trustAllHosts();

            //Creo la Conexion
            urlHttpsConnection = (HttpsURLConnection) url.openConnection();

            //Seteo la verificacion para que NO verifique nada!!
            urlHttpsConnection.setHostnameVerifier(DO_NOT_VERIFY);

            //Asigno a la otra variable para usar simpre la mism
            urlConnection = urlHttpsConnection;

         else 

            urlConnection = (HttpURLConnection) url.openConnection();
        

//Do the same like up

并添加一个 trustAllHosts 方法来信任每个服务器(不检查任何证书)

private static void trustAllHosts() 

    X509TrustManager easyTrustManager = new X509TrustManager() 

        public void checkClientTrusted(
                X509Certificate[] chain,
                String authType) throws CertificateException 
            // Oh, I am easy!
        

        public void checkServerTrusted(
                X509Certificate[] chain,
                String authType) throws CertificateException 
            // Oh, I am easy!
        

        public X509Certificate[] getAcceptedIssuers() 
            return null;
        

    ;

    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] easyTrustManager;

    // Install the all-trusting trust manager
    try 
        SSLContext sc = SSLContext.getInstance("TLS");

        sc.init(null, trustAllCerts, new java.security.SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

     catch (Exception e) 
            e.printStackTrace();
    

这些更改效果很好,但我不想信任每个服务器。我想使用我的密钥库文件来验证连接并以正确的方式使用 SSL。 我在互联网上阅读了很多内容并进行了很多测试,但我无法理解我必须做什么以及如何去做。

有人可以帮帮我吗?

非常感谢

对不起我的英语不好

--------------更新 2011/08/24---- ----------------------------------

嗯,我还在努力。我做了一个新方法来设置 KeyStore、InputStream 等

方法如下:

private static void trustIFNetServer() 

    try 
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

        KeyStore ks = KeyStore.getInstance("BKS");

        InputStream in = context.getResources().openRawResource(R.raw.mykeystore);

        String keyPassword = "password"; 

        ks.load(in, keyPassword.toCharArray());

        in.close();

        tmf.init(ks);

        TrustManager[] tms = tmf.getTrustManagers();    

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

    sc.init(null, tms, new java.security.SecureRandom());

     catch (Exception e) 
    e.printStackTrace();
    

首先我在密钥和证书方面遇到了很多问题,但现在它可以工作了(我想是的)

我现在的问题是超时异常。我不知道它为什么会生成。我认为这是数据写入的问题,但我还不能解决。

有什么想法吗?

【问题讨论】:

您是使用知名证书颁发机构签署的证书还是使用自签名证书? 【参考方案1】:

创建您的信任存储,作为资产存储并使用它来初始化此SocketFactory。然后使用工厂而不是您自己的“信任所有人”。

【讨论】:

谢谢,你有例子吗? 不是一个完整的例子,但是一旦你加载了你的信任存储(包含服务器证书),只需将它与密码一起传递给构造函数。然后您可以将生成的SocketFactory 实例传递给HttpsURLConnection.setDefaultSSLSocketFactory()【参考方案2】:

您需要按照here 的描述为您的自签名证书创建一个信任存储文件。 在客户端使用它来连接您的服务器。如果您使用 JKS 或其他格式并不重要,我现在假设 JKS。

显然,要完成您的想法,您需要一个不同的TrustManager。您可以使用 TrustManagerFactory 并将其信任设置提供给您新创建的信任存储。

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("<path to your key store>");
ks.load(in, "password".toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();

使用tms 来初始化您的SSLContext,而不是为您的SSL/TLS 连接使用新的信任设置。

您还应确保服务器 TLS 证书的 CN 部分等于您服务器的 FQDN(完全限定域名),例如如果您的服务器基本 URL 是“https://www.example.com”,那么证书的 CN 应该是“www.example.com”。这是主机名验证所必需的,该功能可防止中间人攻击。您可以禁用此功能,但只有在使用此功能时,您的连接才会真正安全。

【讨论】:

我认为他需要正确配置android客户端的信任库,而不是服务器的。 感谢您的帮助。我将尝试了解这一切并进行一些测试。非常感谢! 我有一个问题,当你说: FileInputStream in = new FileInputStream("");我必须使用什么文件?我有“keystore”(不是扩展名)、“truststore”(不是扩展名)和“test.cer” 我还有一些问题,但这是正确的道路。真的谢谢 嗯-我提交了评论,但似乎没有出现...抱歉。是的,这是您在这种情况下需要使用的信任库文件。【参考方案3】:

如果你想忽略所有证书,忽略握手,那么这个工作: HttpsURLConnection and intermittent connections

【讨论】:

在没有证书和握手的情况下传输到我的服务器是否安全? @GeekGuy 不,不要这样做

以上是关于Android:使用 HttpsURLConnection 的 HTTPS (SSL) 连接的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

Android之SharedPreferences使用

想要使用cordova/android禁用android的HardBack按钮

如何在Mac中使用Android SDK

何时使用“?android”或“@android”?

Android 安装包优化Android 中使用 SVG 图片 ( 使用 appcompat 支持库兼容 5.0 以下版本的 Android 系统使用矢量图 )