Android: HTTPS (SSL) соединение с использованием HttpsURLConnection - PullRequest
14 голосов
/ 19 августа 2011

У меня есть 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 для отправки XML для безопасности.

Сначала я использую Java Keytool для генерации файла .keystore.

Keytool  -keygen -alias tomcat -keyalg RSA

Затем я поместил код XML в файл server.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 для 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();
    }
}

Сначала у меня было много проблем с ключом и сертификатом, но теперь он работает (я так думаю)

Моя проблема сейчас - исключение тайм-аута. Я не знаю, почему это генерируется. Я думаю, что-то с записью данных, но я пока не могу решить.

Есть идеи?

Ответы [ 3 ]

6 голосов
/ 20 августа 2011

Вам необходимо создать файл хранилища доверенных сертификатов для вашего самозаверяющего сертификата, как описано здесь . Используйте его на стороне клиента для соединения с вашим сервером. На самом деле не имеет значения, используете ли вы 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.

Также вы должны убедиться, что часть CN сертификата TLS сервера равна FQDN (полному доменному имени) вашего сервера, например. если базовый URL-адрес вашего сервера равен «https://www.example.com',», тогда CN сертификата должно быть «www.example.com». Это необходимо для проверки имени хоста, функции, которая предотвращает атаки типа «человек посередине». Вы можете отключить это, но только при использовании этого ваше соединение будет действительно безопасным.

0 голосов
/ 15 июля 2012

Если вы хотите игнорировать все сертификаты, игнорировать рукопожатие, то это работает: HttpsURLCподключение и прерывистые соединения

0 голосов
/ 19 августа 2011

Создайте свое доверенное хранилище, храните его в качестве актива и используйте его для инициализации SocketFactory . Тогда используйте фабрику вместо своего собственного «доверяйте всем».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...