2011-08-19 19 views
14

Tengo 2 aplicaciones, una es Servlet/Tomcat Server y la otra es una aplicación de Android.Android: conexión HTTPS (SSL) usando HttpsURLConnection

Quiero utilizar HttpURLConnection para enviar y recibir XML entre ambos.

Código:

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

Ahora, tengo que usar SSL para enviar los XMLs para la seguridad.

Primero, utilizo Java Keytool para generar el archivo .keystore.

Keytool -keygen -alias tomcat -keyalg RSA 

Entonces poner el código XML en el archivo server.xml de Tomcat para utilizar 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" 
/> 

Entonces, lo cambio por el 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 

y añadir un trustAllHosts método para confiar en cada servidor (no verificar ningún certificado)

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

Esos cambios funcionaron muy bien, pero no quiero confiar en cada servidor. Quiero usar mi archivo de almacén de claves para validar la conexión y usar SSL de la manera correcta. He leído mucho en Internet y he realizado muchas pruebas, pero no puedo entender lo que tengo que hacer y cómo hacerlo.

¿Alguien me puede ayudar?

Muchas gracias

Lo siento por mi mala Inglés

------------------------- ACTUALIZACIÓN 2011/08/24 ------------------------------------------------ -

Bueno, todavía estoy trabajando en esto. Hice un nuevo método para establecer el almacén de claves, InputStream, etc

El método es el siguiente:

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

Primero tuve un montón de problemas con la clave y el certificado, pero ahora se está trabajando (Creo que sí)

Mi problema en este momento es una excepción TimeOut. No sé por qué se genera. Creo que es algo con la escritura de datos, pero no puedo resolverlo todavía.

¿Alguna idea?

+0

¿Está utilizando un certificado firmado por una autoridad de certificación conocido o está utilizando un certificado autofirmado? –

Respuesta

6

Debe crear un archivo de tienda de confianza para su certificado autofirmado como se describe here. Úselo en el lado del cliente para conectarse con su servidor. Realmente no importa si usas JKS u otro formato, por ahora asumiré JKS.

Para lograr lo que tiene en mente, necesita una TrustManager diferente, obviamente. Puede usar TrustManagerFactory y alimentar su configuración de confianza con su tienda de confianza recién creada.

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

Uso tms a init su SSLContext lugar para los nuevos ajustes de seguridad que se utilizará para la conexión SSL/TLS.

También debe asegurarse de que la parte CN del certificado TLS del servidor sea igual al FQDN (nombre de dominio completo) de su servidor, p. si la URL base de su servidor es 'https://www.example.com', entonces el CN del certificado debe ser 'www.example.com'. Esto es necesario para la verificación de nombre de host, una función que evita los ataques de hombre en el medio. Puede desactivar esto, pero solo cuando lo use su conexión será realmente segura.

+0

Creo que necesita configurar el almacén de confianza del cliente de Android correctamente, no del servidor. –

+0

Tienes toda la razón: ¡Cambiaré esto, gracias! – emboss

+0

Gracias por la ayuda. Trataré de entender todo esto y hacer algunas pruebas. ¡Muchas gracias! –

0

Crea tu tienda de confianza, almacena como activo y úsala para inicializar SocketFactory. Luego use la fábrica en lugar de la suya propia.

+0

Gracias, ¿tiene algún ejemplo? –

+0

No es un ejemplo completo, pero una vez que haya cargado el almacén de confianza (que contiene el certificado del servidor), simplemente páselo al constructor junto con la contraseña. Luego puede pasar la instancia 'SocketFactory' resultante a' HttpsURLConnection.setDefaultSSLSocketFactory() '. –

Cuestiones relacionadas