2015-08-31 1 views
0

Modifier: BNK dans les commentaires a lié à une solution trouvée here.Certificat auto-signé - Ancrage Trust non trouvé

J'envoie des requêtes POST via REST à un serveur dorsal (sur un réseau local), toutes effectuées via HTTPS. Ce serveur a un certificat auto-signé en tant que fichier .pem, tout fonctionne bien.

J'essaie maintenant de me connecter à un autre serveur web (via WAN, via DNS), un certificat auto-signé aussi mais sous la forme d'un fichier .crt (standard, format BER/DER). Cependant maintenant, bien que le code est le même, je reçois l'exception suivante:

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 

Je ne sais pas pourquoi un serveur est correct de se connecter, mais pas l'autre. Je ne veux pas faire confiance à tous les certificats car cela va passer par Internet.

Mon code réseau:

public HttpsURLConnection setUpHttpsConnection(String urlString) 
{ 
    try 
    { 
     // Load CAs from an InputStream 
     CertificateFactory cf = CertificateFactory.getInstance("X.509"); 

     InputStream caInput = new BufferedInputStream(context.getAssets().open("server.crt")); 
     Certificate ca = cf.generateCertificate(caInput); 
     System.out.println("ca=" + ((java.security.cert.X509Certificate) ca).getSubjectDN()); 

     // Create a KeyStore containing our trusted CAs 
     String keyStoreType = KeyStore.getDefaultType(); 
     KeyStore keyStore = KeyStore.getInstance(keyStoreType); 
     keyStore.load(null, null); 
     keyStore.setCertificateEntry("ca", ca); 

     // Create a TrustManager that trusts the CAs in our KeyStore 
     String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); 
     tmf.init(keyStore); 

     // Create an SSLContext that uses our TrustManager 
     SSLContext sslContext = SSLContext.getInstance("TLS"); 
     sslContext.init(null, tmf.getTrustManagers(), null); 

     // Create all-trusting host name verifier 
     // to avoid the following : 
     // java.security.cert.CertificateException: No name matching 
     // This is because Java by default verifies that the certificate CN (Common Name) is 
     // the same as host name in the URL. If they are not, the web service client fails. 
     HostnameVerifier allHostsValid = new HostnameVerifier() { 
      @Override 
      public boolean verify(String arg0, SSLSession arg1) { 
       return true; 
      } 
     }; 
     // Install it 
     HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); 

     // Tell the URLConnection to use a SocketFactory from our SSLContext 
     URL url = new URL(urlString); 
     HttpsURLConnection urlConnection = null; 
     urlConnection = (HttpsURLConnection)url.openConnection(); 
     urlConnection.setSSLSocketFactory(sslContext.getSocketFactory()); 

     return urlConnection; 
    } 
    catch (Exception ex) 
    { 
     Log.e("NetworkManager", "Failed to establish SSL connection to server: " + ex.toString()); 
     return null; 
    } 
} 

/** 
* Represents an asynchronous login/registration task used to authenticate 
* the user. 
*/ 
public class POSTTask extends AsyncTask<POSTRequest, Void, StringBuilder> 
{ 
    POSTTask() 
    { 
    } 

    @Override 
    protected void onPreExecute() {} 

    @Override 
    protected StringBuilder doInBackground(POSTRequest... params) 
    { 
     OutputStream os = null; 

     try { 
      HttpsURLConnection urlConnection = setUpHttpsConnection(params[0].url); 
      //Sets the maximum time to wait for an input stream read to complete before giving up. 
      urlConnection.setReadTimeout(30000); 
      //Sets the maximum time in milliseconds to wait while connecting. 
      urlConnection.setConnectTimeout(20000); 
      urlConnection.setRequestMethod("POST"); 
      urlConnection.setDoInput(true); 
      urlConnection.setDoOutput(true); 

      UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(params[0].nameValuePairs); 
      os = urlConnection.getOutputStream(); 
      formEntity.writeTo(os); 

      InputStream in = urlConnection.getInputStream(); 
      StringBuilder ret = inputStreamToString(in); 

      return ret; 

     } catch (IOException e) { 
      Log.i("NetworkError", e.toString()); 
     } catch (Exception e) { 

     } finally { 
      if (os != null) { 
       try { 
        os.close(); 
       } catch (IOException ex) { 
       } 
      } 
     }    
     return null; 
    } 

    @Override 
    protected void onPostExecute(StringBuilder result) { 
    } 

    @Override 
    protected void onCancelled() { 
    } 
} 
+0

Le serveur a-t-il obtenu cette exception ou le client? – EJP

+0

@EJP, désolé, le client (mon application Android). – LKB

+2

Non lié à la question mais à votre code: désactiver la vérification du nom d'hôte comme si vous faisiez effectivement un attaquant pour utiliser son certificat ** any ** signé par une autorité de certification de confiance pour une attaque man-in-the-middle. Comme un tel certificat est trivialement à obtenir (le hacker doit posséder un nom de domaine), vous désactivez effectivement tout type de vérification de certificat de cette façon. –

Répondre

3

Si je comprends bien votre idée de « toute confiance », qui est vérificateur de nom d'hôte dans votre code, vous pouvez vous référer à ce qui suit:

Supposons que votre serveur application héberge à l'intérieur de IIS qui a un certificat de serveur dans lequel "Issued to" est "localhost", par exemple. Ensuite, à l'intérieur de la méthode de vérification, vous pouvez vérifier "localhost".

HostnameVerifier hostnameVerifier = new HostnameVerifier() { 
    @Override 
    public boolean verify(String hostname, SSLSession session) { 
     HostnameVerifier hv = 
      HttpsURLConnection.getDefaultHostnameVerifier(); 
     return hv.verify("localhost", session); 
    } 
};