web-dev-qa-db-fra.com

Android SSL HttpGet SSL (Pas de certificat de pair) erreur OR (Connexion fermée par le pair) erreur

J'essaie de faire un simple HttpGet pour lire une page Web. Je travaille sur iOS et sur Android via http, mais pas https.

L'URL est une IP de réseau interne et un port personnalisé, je peux donc lire avec http comme ceci en utilisant un chemin de http://ipaddress:port/MyPage.html

HttpClient httpclient = new DefaultHttpClient(httpParameters);
                    HttpResponse response;
        String responseString = null;
        try {
            // Try connection
            HttpGet get = new HttpGet(params[0].path);
            get.addHeader("Authorization",
                    "Basic "
                            + Base64.encodeBytes(new String(params[0].username + ":" + params[0].password)
                                    .getBytes()));
        response = httpclient.execute(get);
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            response.getEntity().writeTo(out);
            out.close();
            responseString = out.toString();
        } else {
            // Closes the connection.
            response.getEntity().getContent().close();
            throw new IOException(statusLine.getReasonPhrase());
        }
    } catch (ClientProtocolException e) {
        Log.e(TAG, "ClientProtocolException");
        this.e = e;
    } catch (IOException e) {
        Log.e(TAG, "IOException");
        this.e = e;
    }
    return responseString;

Lorsque j'essaie d'utiliser https, j'obtiens l'erreur No peer certificate. J'ai donc essayé d'utiliser ce code: HttpClient httpclient = new DefaultHttpClient(httpParameters);

private HttpClient createHttpClient() {
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);

            SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

            HttpParams params = new BasicHttpParams();
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
            HttpProtocolParams.setUseExpectContinue(params, true);

            SchemeRegistry schReg = new SchemeRegistry();
            schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            schReg.register(new Scheme("https", sf, 8080));
            ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);

            return new DefaultHttpClient(conMgr, params);

        } catch (Exception e) {
            return new DefaultHttpClient();
        }
    }

mais cela me donne une erreur Connection closed by peer.

Qu'est-ce que je fais mal? Je peux ignorer le certificat en toute sécurité, car il s'agit d'un réseau interne avec un certificat auto-signé, mais je n'ai aucun contrôle sur le vert et les utilisateurs de mon application peuvent avoir différents certificats, j'ai donc vraiment besoin de l'accepter ou de le contourner automatiquement.

Merci

ÉDITER ------------------------------

Après avoir essayé la réponse My-Name-Is ci-dessous: J'ai créé une classe CustomX509TrustManager comme suggéré, puis créez un HttpClient personnalisé en l'utilisant comme ceci:

private HttpClient sslClient(HttpClient client) {
        try {
            CustomX509TrustManager tm = new CustomX509TrustManager();
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[] { tm }, null);
            SSLSocketFactory ssf = new MySSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = client.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", ssf, 8080));
            return new DefaultHttpClient(ccm, client.getParams());
        } catch (Exception ex) {
            return null;
        }
    }

Et enfin utiliser ce HttpClient comme ceci:

private class httpGETTask extends AsyncTask<GetParams, Void, String> {
private Exception e = null;

@Override
protected String doInBackground(GetParams... params) {
    // Set connection parameters
    HttpParams httpParameters = new BasicHttpParams();
    int timeoutConnection = 15000;
    HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
    int timeoutSocket = 15000;
    HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

    Log.v(TAG, params[0].path);
    HttpClient httpclient = new DefaultHttpClient(httpParameters);
    httpclient = sslClient(httpclient);

    HttpResponse response;
    String responseString = null;
    try {
        // Try connection
        HttpGet get = new HttpGet(params[0].path);
        get.addHeader("Authorization",
                "Basic "
                        + Base64.encodeBytes(new String(params[0].username + ":" + params[0].password)
                                .getBytes()));

        response = httpclient.execute(get);
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            response.getEntity().writeTo(out);
            out.close();
            responseString = out.toString();
        } else {
            // Closes the connection.
            response.getEntity().getContent().close();
            throw new IOException(statusLine.getReasonPhrase());
        }
    } catch (ClientProtocolException e) {
        Log.e(TAG, "ClientProtocolException");
        this.e = e;
    } catch (IOException e) {
        Log.e(TAG, "IOException");
        this.e = e;
    }
    return responseString;

Le chemin enregistré est au format https://ipaddress:8080/Page.html Mais j'obtiens une erreur Connection closed By Peer:

05-24 08: 20: 32.500: E/ConnectionHelper (1129): IOException 05-24 08: 20: 32.550: E/ConnectionHelper (1129): Contenu de chargement d'exception 05-24 08: 20: 32.550: E/ConnectionHelper (1129 ): javax.net.ssl.SSLException: connexion fermée par l'homologue 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake (méthode native) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake (OpenSSLSocketImpl.Java:410) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLInputStream. (OpenSSLSocketImpl.Java:643) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache. harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream (OpenSSLSocketImpl.Java:614) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): at org.Apache.http.impl.io.SocketInputBuffer. (SocketInputBuffer. Java: 70) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.Soc ketHttpClientConnection.createSessionInputBuffer (SocketHttpClientConnection.Java:83) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer (DefaultClientConnection.170-24) 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.SocketHttpClientConnection.bind (SocketHttpClientConnection.Java:106) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): chez org .Apache.http.impl.conn.DefaultClientConnection.openCompleted (DefaultClientConnection.Java:129) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.conn.DefaultClientConnectionOperator.openConnection ( DefaultClientConnectionOperator.Java:172) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): at org.Apache.http.impl.conn.AbstractPoolEntry.open (AbstractPoolEntry.Java:164) 05-24 08:20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.conn.AbstractPooledConnAdapter.open (AbstractPooledConnAdapter.Java:119) 05-24 08: 20: 32.550: E/ConnectionHelper ( 1129): sur org.Apache.http.impl.client.DefaultRequestDirector.execute (DefaultRequestDirector.Java:360) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.client .AbstractHttpClient.execute (AbstractHttpClient.Java:555) 05-24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.client.AbstractHttpClient.execute (AbstractHttpClient.Java:487) 05- 24 08: 20: 32.550: E/ConnectionHelper (1129): sur org.Apache.http.impl.client.AbstractHttpClient.execute (AbstractHttpClient.Java:465) 05-24 08: 20: 32.550: E/ConnectionHelper (1129) : sur com.d_apps.my_app.connection_helpers.ConnectionHelper $ httpGETTask.doInBackground (ConnectionHelper.Java:114)

28
Darren

La source suivante devrait résoudre votre problème.

import Android.app.Activity;
import Android.widget.EditText;
import Android.os.Bundle;
import org.Apache.http.HttpResponse;
import org.Apache.http.Header
import Java.io.InputStream;
import Java.io.BufferedReader;
import Java.io.InputStreamReader;
import Android.util.Log;
import Android.view.Menu;
public class MainActivity extends Activity {

    private EditText text;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (EditText) findViewById(R.id.editText1);
        connect();
    }

    private void connect(){
        try {
            DataLoader dl = new DataLoader();
            String url = "https://IpAddress";
            HttpResponse response = dl.secureLoadData(url); 

            StringBuilder sb = new StringBuilder();
            sb.append("HEADERS:\n\n");

            Header[] headers = response.getAllHeaders();
            for (int i = 0; i < headers.length; i++) {
                Header h = headers[i];
                sb.append(h.getName()).append(":\t").append(h.getValue()).append("\n");
            }

            InputStream is = response.getEntity().getContent();
            StringBuilder out = new StringBuilder();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            for (String line = br.readLine(); line != null; line = br.readLine())
                out.append(line);
            br.close();

            sb.append("\n\nCONTENT:\n\n").append(out.toString()); 

            Log.i("response", sb.toString());
            text.setText(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

}


import Android.app.Application;
import Android.content.Context;
import Java.io.InputStream;
public class MeaApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        MeaApplication.context = getApplicationContext();
    }

    public static Context getAppContext() {
        return MeaApplication.context;
    }

    public static InputStream loadCertAsInputStream() {
        return MeaApplication.context.getResources().openRawResource(
                R.raw.meacert);
    }

}


import org.Apache.http.conn.ssl.SSLSocketFactory;
import javax.net.ssl.SSLContext;
import Java.security.KeyStore;
import Java.security.NoSuchAlgorithmException;
import Java.security.KeyManagementException;
import Java.security.KeyStoreException;
import Java.security.UnrecoverableKeyException;
import javax.net.ssl.TrustManager;
import Java.net.Socket;
import Java.io.IOException;
import Java.net.UnknownHostException;
/**
 * Taken from: http://janis.peisenieks.lv/en/76/english-making-an-ssl-connection-via-Android/
 *
 */
public class CustomSSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public CustomSSLSocketFactory(KeyStore truststore)
            throws NoSuchAlgorithmException, KeyManagementException,
            KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new CustomX509TrustManager();

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    public CustomSSLSocketFactory(SSLContext context)
            throws KeyManagementException, NoSuchAlgorithmException,
            KeyStoreException, UnrecoverableKeyException {
        super(null);
        sslContext = context;
    }

    @Override
    public Socket createSocket(Socket socket, String Host, int port,
            boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, Host, port,
                autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}


import javax.net.ssl.X509TrustManager;
import Java.security.cert.CertificateException;
import Java.security.cert.X509Certificate;
import Java.io.IOException;
import Java.io.InputStream;
import Java.security.cert.CertificateFactory;
public class CustomX509TrustManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    @Override
    public void checkServerTrusted(Java.security.cert.X509Certificate[] certs,
            String authType) throws CertificateException {

        // Here you can verify the servers certificate. (e.g. against one which is stored on mobile device)

        // InputStream inStream = null;
        // try {
        // inStream = MeaApplication.loadCertAsInputStream();
        // CertificateFactory cf = CertificateFactory.getInstance("X.509");
        // X509Certificate ca = (X509Certificate)
        // cf.generateCertificate(inStream);
        // inStream.close();
        //
        // for (X509Certificate cert : certs) {
        // // Verifing by public key
        // cert.verify(ca.getPublicKey());
        // }
        // } catch (Exception e) {
        // throw new IllegalArgumentException("Untrusted Certificate!");
        // } finally {
        // try {
        // inStream.close();
        // } catch (IOException e) {
        // e.printStackTrace();
        // }
        // }
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }

}


import org.Apache.http.HttpResponse;
import org.Apache.http.client.ClientProtocolException;
import Java.io.IOException;
import Java.security.NoSuchAlgorithmException;
import Java.security.KeyManagementException;
import Java.net.URISyntaxException;
import Java.security.KeyStoreException;
import Java.security.UnrecoverableKeyException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import Java.security.SecureRandom;
import org.Apache.http.client.HttpClient;
import org.Apache.http.impl.client.DefaultHttpClient;
import org.Apache.http.conn.ssl.SSLSocketFactory;
import org.Apache.http.conn.ClientConnectionManager;
import org.Apache.http.conn.scheme.Scheme;
import org.Apache.http.conn.scheme.SchemeRegistry;
import org.Apache.http.client.methods.HttpGet;
public class DataLoader {

    public HttpResponse secureLoadData(String url)
            throws ClientProtocolException, IOException,
            NoSuchAlgorithmException, KeyManagementException,
            URISyntaxException, KeyStoreException, UnrecoverableKeyException {
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(null, new TrustManager[] { new CustomX509TrustManager() },
                new SecureRandom());

        HttpClient client = new DefaultHttpClient();

        SSLSocketFactory ssf = new CustomSSLSocketFactory(ctx);
        ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        ClientConnectionManager ccm = client.getConnectionManager();
        SchemeRegistry sr = ccm.getSchemeRegistry();
        sr.register(new Scheme("https", ssf, 443));
        DefaultHttpClient sslClient = new DefaultHttpClient(ccm,
                client.getParams());

        HttpGet get = new HttpGet(new URI(url));
        HttpResponse response = sslClient.execute(get);

        return response;
    }

}
56
My-Name-Is

Si vous utilisez un certificat "Not Trusted" (développeur), voici la solution. Nous devons faire confiance à tous les certificats, et voici la façon de le faire. Pour les certificats de confiance, cela fonctionne sans ajouter de fonctionnalité ci-dessous, nous devons simplement changer http en https et cela fonctionnera.

Voici la solution pour les certificats non approuvés.

De la manière HttpClient, vous devez créer une classe personnalisée à partir de org.Apache.http.conn.ssl.SSLSocketFactory, et non celle org.Apache.http.conn.ssl.SSLSocketFactory elle-même

l'exemple est comme ...

import Java.io.IOException;
import Java.net.Socket;
import Java.net.UnknownHostException;
import Java.security.KeyManagementException;
import Java.security.KeyStore;
import Java.security.KeyStoreException;
import Java.security.NoSuchAlgorithmException;
import Java.security.UnrecoverableKeyException;
import Java.security.cert.CertificateException;
import Java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.Apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public Socket createSocket(Socket socket, String Host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, Host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

et utilisez cette classe lors de la création d'une instance de HttpClient.

public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}
14
santhosh

Vérifiez la date de votre appareil. Vous l'avez peut-être changé pour une année différente. Je reçois constamment ce problème lorsque les enfants utilisent mon téléphone.

7
Kris Yu

Si vous avez accès au serveur avec un certificat de confiance, une bien meilleure solution consiste à configurer correctement le SSL car Android est plus restreint que iOS et le navigateur de bureau concernant la vérification SSL

Cette solution ne nécessite aucune modification dans votre application Android, elle est donc plus propre.

Voici un exemple de configuration SSL pour Apache (ajoutez-le à votre définition VirtualHost, par exemple/etc/Apache2/sites-enabled)

SSLEngine on
SSLCertificateFile    YOUR_CERT_PATH
SSLCACertificateFile  CA_ROOT_CERT_PATH
SSLCertificateKeyFile KEY_PATH

J'ai eu la même erreur et lorsque j'ai ajouté le certificat racine CA, l'erreur a disparu et Android ne s'est plus plaint. Fournissez les chemins d'accès corrects pour ces fichiers, redémarrez Apache et testez à nouveau.

Le fichier du certificat racine de l'autorité de certification peut contenir à la fois le certificat racine et le certificat intermédiaire

Vous pouvez tester votre configuration SSL avec ce site et assurez-vous que dans le résultat sous Chemins de certification que le serveur envoie tous les certificats requis.

3
iTech

Lorsque vous recherchez cette exception, tout ce que vous obtenez est le conseil d'implémenter "autoriser tous les certificats".

Le javadoc pour SSLPeerUnverifiedException indique:

Lorsque l'homologue n'a pas pu s'identifier (par exemple; aucun certificat, la suite de chiffrement particulière utilisée ne prend pas en charge l'authentification, ou aucune authentification d'homologue n'a été établie pendant l'établissement de liaison SSL), cette exception est levée.

L'erreur pourrait donc être que la connexion est si instable/instable et que les demandes prennent un temps ridicule. Dans notre application mobile, nous rencontrons parfois beaucoup de connexions/Socket-Timeouts en combinaison avec ces SSLPeerUnverifiedException. Certaines demandes passent mais prennent 60s + - la connexion réseau aspire simplement au-delà de tous les moyens dans ces cas.

Il est simplement déconseillé de forcer brutalement ceci avec le "autoriser tous les certificats" dans de tels cas - implémentez plutôt une stratégie de nouvelle tentative appropriée.

1
icyerasor

Autorisez le fournisseur de sécurité d'Android à se mettre à jour au démarrage de votre application.

Le fournisseur par défaut avant 5.0+ ne désactive pas SSLv3. Si vous avez accès aux services Google Play, il est relativement simple de corriger le fournisseur de sécurité d'Android à partir de votre application.

  private void updateAndroidSecurityProvider(Activity callingActivity) {
    try {
        ProviderInstaller.installIfNeeded(this);
    } catch (GooglePlayServicesRepairableException e) {
      // Thrown when Google Play Services is not installed, up-to-date, or  enabled
        // Show dialog to allow users to install, update, or otherwise    enable Google Play services.
       GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), callingActivity, 0);
    } catch (GooglePlayServicesNotAvailableException e) {
        Log.e("SecurityException", "Google Play Services not available.");
    }
}

Source: Patcher le fournisseur de sécurité avec le fournisseur ProviderInstaller

1
nani