2013-05-05 15 views
5

Ho questo metodo:Modifica HTTP richiesta POST HTTPS richiesta Inserisci:

public static String getReportMetadata (String reportId, String sessionId, String url) throws Exception{ 

    Map<String, Object> jsonValues = new HashMap<String, Object>(); 
    jsonValues.put("reportID", reportId); 
    jsonValues.put("sessionID", sessionId); 
    JSONObject json = new JSONObject(jsonValues); 

    DefaultHttpClient client = new DefaultHttpClient(); 

    HttpPost post = new HttpPost(url + GET_REPORT_METADATA_ACTION); 

    AbstractHttpEntity entity = new ByteArrayEntity(json.toString().getBytes("UTF8")); 
    entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json")); 
    post.setEntity(entity);   
    HttpResponse response = client.execute(post); 

    return getContent(response);    
} 

che svolgono una richiesta HTTP post che di portate ho eseguito utilizzando AsyncTask per ottenere i dati dal server.

La mia domanda: potrebbe qualcuno si prega di spiegare a me in modo semplice quali sono i passi che devo eseguire per cambiare questo tipo di connessione per una connessione sicura (a.k.a usando HTTPS). Solo dal punto di vista di Android (ovvero l'applicazione client).

UPDATE: Come suggerito ho cercato di cambiare solo il link e aggiungere https invece di http, ma non restituisce una risposta. A quanto ho capito ho bisogno di ottenere e conservare un certificato auto segno al fine di connettersi a lato server

UPDATE2: La soluzione che funziona per me:

EasySSLSocketFactory:

public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory { 

private SSLContext sslcontext = null; 

private static SSLContext createEasySSLContext() throws IOException { 
    try { 
     SSLContext context = SSLContext.getInstance("TLS"); 
     context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null); 
     return context; 
    } catch (Exception e) { 
     throw new IOException(e.getMessage()); 
    } 
} 

private SSLContext getSSLContext() throws IOException { 
    if (this.sslcontext == null) { 
     this.sslcontext = createEasySSLContext(); 
    } 
    return this.sslcontext; 
} 

/** 
* @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int, 
*  java.net.InetAddress, int, org.apache.http.params.HttpParams) 
*/ 
public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort, 
     HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { 
    int connTimeout = HttpConnectionParams.getConnectionTimeout(params); 
    int soTimeout = HttpConnectionParams.getSoTimeout(params); 
    InetSocketAddress remoteAddress = new InetSocketAddress(host, port); 
    SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket()); 

    if ((localAddress != null) || (localPort > 0)) { 
     // we need to bind explicitly 
     if (localPort < 0) { 
      localPort = 0; // indicates "any" 
     } 
     InetSocketAddress isa = new InetSocketAddress(localAddress, localPort); 
     sslsock.bind(isa); 
    } 

    sslsock.connect(remoteAddress, connTimeout); 
    sslsock.setSoTimeout(soTimeout); 
    return sslsock; 

} 

/** 
* @see org.apache.http.conn.scheme.SocketFactory#createSocket() 
*/ 
public Socket createSocket() throws IOException { 
    return getSSLContext().getSocketFactory().createSocket(); 
} 

/** 
* @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket) 
*/ 
public boolean isSecure(Socket socket) throws IllegalArgumentException { 
    return true; 
} 

/** 
* @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int, 
*  boolean) 
*/ 
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, 
     UnknownHostException { 
    return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); 
} 

// ------------------------------------------------------------------- 
// javadoc in org.apache.http.conn.scheme.SocketFactory says : 
// Both Object.equals() and Object.hashCode() must be overridden 
// for the correct operation of some connection managers 
// ------------------------------------------------------------------- 

public boolean equals(Object obj) { 
    return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class)); 
} 

public int hashCode() { 
    return EasySSLSocketFactory.class.hashCode(); 
} 
} 

EasyX509TrustManager:

public class EasyX509TrustManager implements X509TrustManager { 

private X509TrustManager standardTrustManager = null; 

/** 
* Constructor for EasyX509TrustManager. 
*/ 
public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException { 
    super(); 
    TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    factory.init(keystore); 
    TrustManager[] trustmanagers = factory.getTrustManagers(); 
    if (trustmanagers.length == 0) { 
     throw new NoSuchAlgorithmException("no trust manager found"); 
    } 
    this.standardTrustManager = (X509TrustManager) trustmanagers[0]; 
} 

/** 
* @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType) 
*/ 
public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException { 
    standardTrustManager.checkClientTrusted(certificates, authType); 
} 

/** 
* @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType) 
*/ 
public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException { 
    if ((certificates != null) && (certificates.length == 1)) { 
     certificates[0].checkValidity(); 
    } else { 
     standardTrustManager.checkServerTrusted(certificates, authType); 
    } 
} 

/** 
* @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() 
*/ 
public X509Certificate[] getAcceptedIssuers() { 
    return this.standardTrustManager.getAcceptedIssuers(); 
} 
} 

E ho aggiunto questo metodo: getNewHttpClient()

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

Infine per ogni posto nel mio codice che ho avuto:

DefaultHttpClient client = new DefaultHttpClient(); 

ho sostituirla con:

HttpClient client = getNewHttpClient(); 

Ora sono in grado di ricevere i dati dal lato server, l'ultima domanda è: è quello che ho fatto è sicuro? o accetta ogni certificato autofirmato? se questo è il caso, cosa dovrebbe essere fatto per cambiarlo?

Qualsiasi aiuto sarebbe apprezzato.

+0

Forse javadoc ti aiuterà: [SchemeRegistry] (http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/scheme/SchemeRegistry.html) [SSLSocketFactory] (http: //hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLSocketFactory.html) – mleczey

+1

Secondo la [Guida SSL di HTTPClient] (htt p: //hc.apache.org/httpclient-legacy/sslguide.html), l'unica cosa che devi fare è inserire "https: //" nella stringa dell'URL. Ciò fornirà una connessione HTTPS con un livello di autenticazione equivalente al browser (ad esempio, una qualsiasi delle centinaia di CA può firmare il certificato del server). "SchemeRegistry" e "SSLSocketFactory" entrano in gioco solo se si desidera personalizzare la gestione SSL, in genere per implementare il blocco SSL (ovvero, utilizzare un vincolo di autenticità più forte). Guarda il github di Moxie per un buon pinner per Android ssl con licenza LGPL. – Barend

+0

@Barend, ho provato a cambiare solo il collegamento e aggiungere https anziché http ma non restituisce una risposta. Come ho capito ho bisogno di ottenere e memorizzare un certificato di autodidatta per connettersi al lato server. –

risposta

5

Da Apache HttpClient SSL guide:

la comunicazione HTTP protetta su SSL deve essere semplice come una semplice comunicazione HTTP.

Così semplicemente dovrebbe cambiare il http: // XXXX per https: // XXXX

EDIT: Ho appena visto la risposta s' @Barend che è più completo

+0

Ho provato a modificare solo il collegamento e aggiungere https anziché http ma non restituisce una risposta. Come ho capito ho bisogno di ottenere un certificato di autodidatta per connettersi al lato server. –

+0

Se il server ha un certificato autofirmato, è necessario comunicare al proprio HttpClient di accettarlo. Se vuoi farlo, puoi guardare qui http://stackoverflow.com/questions/1217141/self-signed-ssl-acceptance-android – user2340612

5

Prima di tutto il necessario per creare l'oggetto SchemeRegistry e registrare nuovo schema utilizzando SSLSocketFactory:

SchemeRegistry schemeRegistry = new SchemeRegistry(); 
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); 

Quindi è possibile creare il SingleClientConnManager utilizzando SchemeRegistry oggetto:

SingleClientConnManager mgr = new SingleClientConnManager(schemeRegistry); 

E infine si crea il DefaultHttpClient con SingleClientConnManager:

HttpClient client = new DefaultHttpClient(mgr); 
+0

ti dispiacerebbe elaborare un po 'di più su quegli oggetti? dovrei eseguire i passaggi descritti prima di ogni accesso al servizio web https? Come ho capito, dovrei ricevere un certificato dal server e memorizzarlo sul dispositivo, ho ragione? –

+0

@EmilAdz Sì, è necessario eseguire questi passaggi prima di ogni accesso https. 'SchemeRegistry' è solo un contenitore per tutti gli oggetti' Scheme' che descrivono i parametri di connessione HTTPS, quindi è possibile utilizzare un'istanza dell'oggetto 'SchemeRegisty' per tutte le chiamate. Ma avrai bisogno di un nuovo 'SingleClientConnManager' per ogni chiamata, perché può mantenere solo una connessione alla volta. Penso che la memorizzazione dei certificati SSL sia fornita da Android stesso, quindi non devi preoccuparti di questo. – nemezis