¿Cómo puedo aceptar un certificado auto-firmado en Java en Android?

Un código de ejemplo sería perfecto.

He buscado por todas partes en Internet y mientras que algunas personas afirman haber encontrado la solución, o bien no funciona o no existe un código de ejemplo para la copia de seguridad.

InformationsquelleAutor Faisal Abid | 2009-08-01

7 Comentarios

  1. 37

    Tengo esta funcionalidad en exchangeIt, que se conecta a Microsoft exchange a través de WebDav. Aquí un poco de código para crear un HttpClient que se conecte a la auto firmado cert a través de SSL:

    SchemeRegistry schemeRegistry = new SchemeRegistry();
    //http scheme
    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    //https scheme
    schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));
    
    HttpParams params = new BasicHttpParams();
    params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
    params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    
    ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

    La EasySSLSocketFactory es aquí, y el EasyX509TrustManager es aquí.

    El código para exchangeIt es de código abierto, y alojado en googlecode aquí, si usted tiene cualquier problema. No estoy trabajando activamente en más, pero el código debería funcionar.

    Nota que desde Android 2.2 el proceso ha cambiado un poco, a fin de comprobar este para hacer que el código de trabajo antes mencionado.

    • Yo no puedo conseguir que esto funcione. Sigo recibiendo IOException: SSL handshake failure: I/O error during system call, Broken pipe en 2.2. Sabes por casualidad una forma de evitar esto?
    • No me gusta confiar en CUALQUIER certificado auto-firmado. Hay una manera de agregar un Certificado de Autoridad a fin de evitar que el Hombre-en-el-medio de los ataques?
    • Esto funcionó como magia….
    • son los tres params.setParameter() requiere ? El tercero se requiere el uso de HTTP 1.1
    • podemos usar este procedimiento para hosting normal y para facebook aplicaciones
    • Como @NicolasMarchildon dicho, esto es peligroso, ya que expone su aplicación al hombre en el centro de los ataques. Aquí es una buena explicación para que en Cert fijación.
    • Yarger puede usted por favor explique por qué registrado «http» para el puerto 80? tnx

  2. 37

    Como EJP correctamente comentó, «los Lectores deben tener en cuenta que esta técnica es radicalmente inseguro. SSL no es seguro, a menos que al menos uno de los pares es autenticado. Consulte RFC 2246.»

    Habiendo dicho eso, he aquí otra forma, sin ningún extra clases:

    import java.security.SecureRandom;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.X509TrustManager;
    
    private void trustEveryone() {
        try {
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }});
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, new X509TrustManager[]{new X509TrustManager(){
                public void checkClientTrusted(X509Certificate[] chain,
                        String authType) throws CertificateException {}
                public void checkServerTrusted(X509Certificate[] chain,
                        String authType) throws CertificateException {}
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }}}, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(
                    context.getSocketFactory());
        } catch (Exception e) { //should never happen
            e.printStackTrace();
        }
    }
    • Donde se llama a este método?
    • Usted podría llamar en cualquier lugar antes de la apertura de una conexión https. Cualquier conexión con la URL.openConnection / HttpsURLConnection debe ser afectada.
    • Yo no podía llegar en cualquier solución para el trabajo, excepto este. Es un terrible hachazo, pero gracias.
    • Los lectores deben tener en cuenta que esta técnica es radicalmente inseguro. SSL no es seguro, a menos que al menos uno de los pares es autenticado. Consulte RFC 2246.
    • Un hack sin la seguridad de que es malo 🙂
    • Es una mala idea para deshabilitar la comprobación de SSL.
    • Se aceptarán todos los CA.no podemos hacer como que
    • link

  3. 35

    Me enfrenté a este problema de ayer, mientras que la migración de nuestra empresa API RESTful para HTTPS, pero el uso de certificados SSL autofirmados.

    He buscando por todas partes, pero todos los «correcta» marcado respuestas que he encontrado consistió de deshabilitar la validación de certificados, claramente reemplazar todo el sentido de SSL.

    Finalmente llegué a una solución:

    1. Crear Local Del Almacén De Claves

      Habilitar la aplicación para validar sus certificados autofirmados, usted necesita proporcionar un almacén de claves personalizado con los certificados de una manera que Android puede confiar en su extremo.

    El formato que para tal costumbre almacenes de claves es «BKS» de BouncyCastle, por lo que necesita el 1,46 versión de BouncyCastleProvider que usted puede descargar aquí.

    También necesita su certificado auto-firmado, voy a asumir que es llamado self_cert.pem.

    Ahora el comando para la creación de su almacén de claves es:

    <!-- language: lang-sh -->
    
        $ keytool -import -v -trustcacerts -alias 0 \
        -file *PATH_TO_SELF_CERT.PEM* \
        -keystore *PATH_TO_KEYSTORE* \
        -storetype BKS \
        -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
        -providerpath *PATH_TO_bcprov-jdk15on-146.jar* \
        -storepass *STOREPASS*

    PATH_TO_KEYSTORE puntos a un archivo donde el almacén de claves será creado. Es NO DEBE EXISTIR.

    PATH_TO_bcprov-jdk15on-146.jar.JAR es la ruta de acceso para la descarga .tarro de biblioteca.

    STOREPASS es tu nueva contraseña del almacén de claves.

    1. Incluyen Almacén de claves en su Aplicación

    Copia de su recién creado almacén de claves de PATH_TO_KEYSTORE a res/raw/certs.bks (certs.bks es sólo el nombre de archivo, usted puede usar cualquier nombre que desee)

    Crear una clave en res/values/strings.xml con

    <!-- language: lang-xml -->
    
        <resources>
        ...
            <string name="store_pass">*STOREPASS*</string>
        ...
        </resources>
    1. Crear esta clase que hereda DefaultHttpClient

      import android.content.Context;
      import android.util.Log;
      import org.apache.http.conn.scheme.PlainSocketFactory;
      import org.apache.http.conn.scheme.Scheme;
      import org.apache.http.conn.scheme.SchemeRegistry;
      import org.apache.http.conn.ssl.SSLSocketFactory;
      import org.apache.http.impl.client.DefaultHttpClient;
      import org.apache.http.params.HttpParams;
      import java.io.IOException;
      import java.io.InputStream;
      import java.security.*;
      public class MyHttpClient extends DefaultHttpClient {
      private static Context appContext = null;
      private static HttpParams params = null;
      private static SchemeRegistry schmReg = null;
      private static Scheme httpsScheme = null;
      private static Scheme httpScheme = null;
      private static String TAG = "MyHttpClient";
      public MyHttpClient(Context myContext) {
      appContext = myContext;
      if (httpScheme == null || httpsScheme == null) {
      httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
      httpsScheme = new Scheme("https", mySSLSocketFactory(), 443);
      }
      getConnectionManager().getSchemeRegistry().register(httpScheme);
      getConnectionManager().getSchemeRegistry().register(httpsScheme);
      }
      private SSLSocketFactory mySSLSocketFactory() {
      SSLSocketFactory ret = null;
      try {
      final KeyStore ks = KeyStore.getInstance("BKS");
      final InputStream inputStream = appContext.getResources().openRawResource(R.raw.certs);
      ks.load(inputStream, appContext.getString(R.string.store_pass).toCharArray());
      inputStream.close();
      ret = new SSLSocketFactory(ks);
      } catch (UnrecoverableKeyException ex) {
      Log.d(TAG, ex.getMessage());
      } catch (KeyStoreException ex) {
      Log.d(TAG, ex.getMessage());
      } catch (KeyManagementException ex) {
      Log.d(TAG, ex.getMessage());
      } catch (NoSuchAlgorithmException ex) {
      Log.d(TAG, ex.getMessage());
      } catch (IOException ex) {
      Log.d(TAG, ex.getMessage());
      } catch (Exception ex) {
      Log.d(TAG, ex.getMessage());
      } finally {
      return ret;
      }
      }
      }

    Ahora simplemente utilizar una instancia de **MyHttpClient** como lo haría con **DefaultHttpClient** para hacer su HTTPS consultas, y se va a utilizar y validar correctamente sus certificados SSL autofirmados.

    HttpResponse httpResponse;
    HttpPost httpQuery = new HttpPost("https://yourserver.com");
    ... set up your query ...
    MyHttpClient myClient = new MyHttpClient(myContext);
    try{
    httpResponse = myClient.(peticionHttp);
    //Check for 200 OK code
    if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {
    ... do whatever you want with your response ...
    }
    }catch (Exception ex){
    Log.d("httpError", ex.getMessage());
    }
    • No me puedo creer que alguien utiliza la misma (DefaultHttpClient). Realmente aprecio su esfuerzo, voy a tratar de adaptarlo con mi Proyecto Android. Gracias!!!
    • proximamente actualizaré la clase completa, con algunas mejoras. || pronto voy a actualizar mi clase con algunas mejoras.
    • He utilizado una clase muy similar a la tuya con algunas actualizaciones. Es la una de la stackoverflow.com/questions/4065379/…
    • que es una buena respuesta, he votado a favor hasta que !!! gracias…
    • Creación del almacén de claves para conexiones seguras utilizando un programa descargado a través de una conexión insegura pierde el punto.
    • No lo entiendo, he podrían descargado BouncyCastle través de ssh y sigue siendo inseguro, el punto es garantizar la seguridad entre la Aplicación de la<->canal del Servidor…
    • Estoy usando tu código en un proyecto de mi mina. Quiero saber si el almacén de claves paso es simplemente confiar en el certificado sin validarlo ? Son sus pasos lo certificate pinning hace ? También, si usted podría, por favor indique cómo certificado de validación puede ser añadido a su código ?
    • Básicamente, esto permite asegurar la conexión entre el servidor y el dispositivo, independientemente de su cert ser auto-firmado o emitidos por trusthworthy CA. Creo (puede que esté equivocado) no limpio la validación de su cert, como explícitamente se piden y esperan SU certificado de servidor (el mismo que se incluye en la aplicación)
    • El archivo self_cert.pem, es el auto firmado el certificado utilizado por el servidor ? O voy a tener que generar uno nuevo para mi aplicación para android? Y si es la generación de uno nuevo, ¿cómo será mi servidor de confiar en ella? Hay parámetros que debe ser el mismo en ambos certificados autofirmados?
    • sí, es el certificado de servidor que su aplicación será de confianza.
    • Bien. Muchas gracias por la respuesta.
    • ¿Qué sucede cuando necesito renovar mi certificado? se esta todavía funcionan? Gracias
    • Bueno, que implica también la necesidad de actualizar su aplicación para utilizar el nuevo certificado.

  4. 15

    A menos que me he perdido algo, las otras respuestas en esta página son PELIGROSOS, y son funcionalmente equivalentes a los que no utiliza SSL en todos. Si usted confía en los certificados auto-firmados sin hacer más comprobaciones para asegurarse de que los certificados son los que te están esperando, entonces cualquier persona puede crear un certificado auto-firmado y puede fingir ser su servidor. En ese punto, usted no tiene ninguna seguridad real.

    La sólo forma legítima de hacer esto (sin necesidad de escribir una completa SSL de pila) es agregar un adicional de confianza ancla de confianza durante el certificado de verificación del proceso. Ambos implican codificar la confianza de anclaje certificado en su aplicación y añadiendo a lo que sea de confianza anclajes de que el sistema operativo proporciona (o de lo contrario no será capaz de conectarse a su sitio web si usted recibe un certificado real).

    Soy consciente de dos maneras de hacer esto:

    1. Crear una costumbre de almacén de confianza, como se describe en http://www.ibm.com/developerworks/java/library/j-customssl/#8

    2. Crear una instancia de X509TrustManager y anular el getAcceptedIssuers método que devuelve un array que contiene su certificado:

      public X509Certificate[] getAcceptedIssuers()
      {
      X509Certificate[] trustedAnchors =
      super.getAcceptedIssuers();
      /* Create a new array with room for an additional trusted certificate. */
      X509Certificate[] myTrustedAnchors = new X509Certificate[trustedAnchors.length + 1];
      System.arraycopy(trustedAnchors, 0, myTrustedAnchors, 0, trustedAnchors.length);  
      /* Load your certificate.
      Thanks to http://stackoverflow.com/questions/11857417/x509trustmanager-override-without-allowing-all-certs
      for this bit.
      */
      InputStream inStream = new FileInputStream("fileName-of-cert");
      CertificateFactory cf = CertificateFactory.getInstance("X.509");
      X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
      inStream.close();
      /* Add your anchor cert as the last item in the array. */
      myTrustedAnchors[trustedAnchors.length] = cert;
      return myTrustedAnchors;
      }

    Tenga en cuenta que este código es completamente probados y no puede ni siquiera compilar, pero al menos debe dirigir en la dirección correcta.

    • Hay algunas cosas en las noticias últimamente acerca de los desarrolladores de android no la comprobación de sus certificados. Por lo que este se ve bien.
    • Bien, resulta X509TrustManager es una interfaz, por lo que no se puede crear subclases de él.
    • Usted puede crear su propio administrador de confianza. publib.boulder.ibm.com/infocenter/javasdk/v6r0/…
    • Para agregar a @Peterdk ‘s primer comentario: véase este artículo llamado «¿por Qué Eva y Mallory Amor de Android: Un Análisis de Android SSL (In)Seguridad».
    • ¿Qué tan necesario es esto, realmente? Parece que esto es algo que debe ser la práctica común.
    • Ahora es qué?

  5. 4

    Brian Yarger la respuesta funciona en Android 2.2, así que si se modifica la más grande createSocket sobrecarga del método de la siguiente manera. Me tomó un tiempo para conseguir la auto-firmado SSLs de trabajo.

    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
    }
  6. 0

    En Android, HttpProtocolParams acepta ProtocolVersion en lugar de HttpVersion.

    ProtocolVersion pv = new ProtocolVersion("HTTP", 1, 1);
    HttpProtocolParams.setVersion(params, pv);
  7. 0

    @Chris – Publicar esto como una respuesta, ya que no puede añadir comentarios (aún). Me pregunto si su enfoque se supone que funciona cuando se utiliza un webView. No me puedo hacerlo en Android 2.3 – lugar acabo de llegar de una pantalla en blanco.

    Después de algunos más búsqueda, me encontré con este una solución sencilla para el manejo de errores SSL en un webView que trabajó como un encanto para mí.

    En el controlador puedo comprobar si estoy en un especial dev modo y llame controlador.proceder(), de lo contrario me llame controlador.cancel(). Esto me permite hacer el desarrollo en contra de un certificado autofirmado en un sitio web local.

Dejar respuesta

Please enter your comment!
Please enter your name here