Android: HTTPS (SSL) connection using HttpsURLConnection

2022-09-03 07:55:12

我有2个应用程序,一个是Servlet / Tomcat服务器,另一个是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来发送XL以确保安全。

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

Keytool  -keygen -alias tomcat -keyalg RSA

然后我把XML代码放在服务器上.xml Tomcat的文件来使用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 for 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 方法添加到 Trust 每个服务器(不检查任何证书)

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

好吧,我还在努力。我制作了一个新方法来设置密钥库,输入流等

该方法如下所示:

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

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

为了实现你所想到的,你需要一个不同的,显然。您可以使用新创建的信任存储并将其提供给其信任设置。TrustManagerTrustManagerFactory

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

用于初始化要用于 SSL/TLS 连接的新信任设置。tmsSSLContext

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


答案 2

创建您的信任存储,将 存储在作为资产,然后使用它初始化此 SocketFactory。然后使用工厂而不是你自己的“信任每个人”的工厂。