web-dev-qa-db-fra.com

Comment faire un HTTPS POST depuis Android?

Je souhaite utiliser une méthode de publication HTTPS pour envoyer des données de mon Android sur mon site Web).

J'ai d'abord utilisé HttpURLConnection et cela fonctionne très bien avec mon URL HTTP. Mon site de production est sur HTTPS et je veux envoyer le même POST using HttpsURLConnection. Quelqu'un peut-il m'aider à utiliser la classe correctement?

J'ai trouvé une source sur ce lien :

KeyStore keyStore = ...;    
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");    
tmf.init(keyStore);

SSLContext context = SSLContext.getInstance("TLS");   
context.init(null, tmf.getTrustManagers(), null);

URL url = new URL("https://www.example.com/");   
HttpsURLConnection urlConnection = (HttpsURLConnection)
url.openConnection();   
urlConnection.setSSLSocketFactory(context.getSocketFactory());   
InputStream in = urlConnection.getInputStream();

Quelle devrait être la valeur de KeyStore keyStore = ...;?

J'ai essayé d'envoyer les données en utilisant le même HttpURLConnection, mais je vois des données POST manquées ou erronées).

J'ai essayé la méthode de cette question . Je colle mon code ci-dessous

String urlParameters="dateTime=" + URLEncoder.encode(dateTime,"UTF-8")+
    "&mobileNum="+URLEncoder.encode(mobileNum,"UTF-8");

URL url = new URL(myurl);
HttpsURLConnection conn;
conn=(HttpsURLConnection)url.openConnection();

// Create the SSL connection
SSLContext sc;
sc = SSLContext.getInstance("TLS");
sc.init(null, null, new Java.security.SecureRandom());
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setConnectTimeout(HTTP_CONNECT_TIME_OUT);
conn.setReadTimeout(HTTP_READ_TIME_OUT);

//set the output to true, indicating you are outputting(uploading) POST data
conn.setDoOutput(true);
//once you set the output to true, you don't really need to set the request method to post, but I'm doing it anyway
conn.setRequestMethod("POST");
conn.setFixedLengthStreamingMode(urlParameters.getBytes().length);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

PrintWriter out = new PrintWriter(conn.getOutputStream());
out.print(urlParameters);
out.close();

InputStream is = conn.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String inputLine;
while ((inputLine = in.readLine()) != null) {
  response += inputLine;            
}                   

L'erreur que je reçois est ci-dessous:

05-12 19:36:10.758: W/System.err(1123): Java.io.FileNotFoundException: https://www.myurl.com/fms/test
05-12 19:36:10.758: W/System.err(1123):     at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.Java:177)
05-12 19:36:10.758: W/System.err(1123):     at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.Java:270)
05-12 19:36:10.758: W/System.err(1123):     at .httpRequest(SMSToDBService.Java:490)
05-12 19:36:10.758: W/System.err(1123):     at com..access$0(SMSToDBService.Java:424)
05-12 19:36:10.758: W/System.err(1123):     at com.$ChildThread$1.handleMessage(SMSToDBService.Java:182)
05-12 19:36:10.758: W/System.err(1123):     at Android.os.Handler.dispatchMessage(Handler.Java:99)
05-12 19:36:10.758: W/System.err(1123):     at Android.os.Looper.loop(Looper.Java:156)
05-12 19:36:10.758: W/System.err(1123):     at com.$ChildThread.run(SMSToDBService.Java:303)
15
Dino

Vous pouvez utiliser les autorités de certification par défaut définies dans l'appareil Android, ce qui convient parfaitement à tout site Web public.

Si vous avez un certificat auto-signé, vous pouvez soit accepter tous les certificats (risqués, ouverts aux attaques de l'homme du milieu) ou créer votre propre TrustManagerFactory, ce qui est un peu hors de cette portée.

Voici du code pour utiliser les autorités de certification par défaut pour un appel https POST:

private InputStream getInputStream(String urlStr, String user, String password) throws IOException
{
    URL url = new URL(urlStr);
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();

    // Create the SSL connection
    SSLContext sc;
    sc = SSLContext.getInstance("TLS");
    sc.init(null, null, new Java.security.SecureRandom());
    conn.setSSLSocketFactory(sc.getSocketFactory());

    // Use this if you need SSL authentication
    String userpass = user + ":" + password;
    String basicAuth = "Basic " + Base64.encodeToString(userpass.getBytes(), Base64.DEFAULT);
    conn.setRequestProperty("Authorization", basicAuth);

    // set Timeout and method
    conn.setReadTimeout(7000);
    conn.setConnectTimeout(7000);
    conn.setRequestMethod("POST");
    conn.setDoInput(true);

    // Add any data you wish to post here

    conn.connect();
    return conn.getInputStream();
}   

Pour lire la réponse:

        String result = new String();
        InputStream is = getInputStream(urlStr, user, password);
        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            result += inputLine;            
        }       
18
tbkn23

Vous pouvez jeter un œil à cette question que j'ai posée il y a quelques jours:

Changer la demande de publication HTTP en demande de publication HTTPS:

J'ai fourni là une solution qui a fonctionné pour moi, qui accepte essentiellement tout certificat auto-signé. Comme cela a été dit ici, cette solution n'est pas recommandée car elle n'est pas sécurisée et ouverte aux attaques d'un homme du milieu.

Voici le code:

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

Et j'ai ajouté cette méthode: 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();
    }
}

Enfin pour chaque place dans mon code que j'avais:

DefaultHttpClient client = new DefaultHttpClient();

Je le remplace par:

HttpClient client = getNewHttpClient();
4
Emil Adz

Voici un Android HttpsUrlConnection POST complet avec épinglage de certificat, timeouts côté code serveur et configurations.

La variable params doit être sous la forme username = demo & password = abc123 &.

@Override
public String sendHttpRequest(String params) {
    String result = "";
    try {
        URL url = new URL(AUTHENTICATION_SERVER_ADDRESS);
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setSSLSocketFactory(KeyPinStore.getInstance().getContext().getSocketFactory()); // Tell the URLConnection to use a SocketFactory from our SSLContext
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        connection.setConnectTimeout(10000);
        connection.setReadTimeout(10000);
        PrintWriter out = new PrintWriter(connection.getOutputStream());
        out.println(params);
        out.close();
        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()), 8192);
        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            result = result.concat(inputLine);
        }
        in.close();
        //} catch (IOException e) {
    } catch (IOException | KeyStoreException | CertificateException | KeyManagementException | NoSuchAlgorithmException e) {
        result = e.toString();
        e.printStackTrace();
    }
    return result;
}
4
threecz