java - significa - Android: conexión HTTPS(SSL) con HttpsURLConnection
ssl pinning android (4)
Tengo 2 aplicaciones, una es Servlet / Tomcat Server y la otra es una aplicación de Android.
Quiero usar HttpURLConnection para enviar y recibir XML entre ambos.
Código:
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
try {
URL url = new URL(this.getServerURL());
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(requeststring.getBytes());
out.flush();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
dis = new DataInputStream(in);
int ch;
long len = urlConnection.getContentLength();
if (len != -1) {
for (int i = 0; i < len; i++)
if ((ch = dis.read()) != -1) {
messagebuffer.append((char) ch);
}
} else {
while ((ch = dis.read()) != -1)
messagebuffer.append((char) ch);
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return messagebuffer.toString();
}
Ahora, necesito usar SSL para enviar los XML por seguridad.
Primero, uso Java Keytool para generar el archivo .keystore.
Keytool -keygen -alias tomcat -keyalg RSA
Luego puse el código XML en el archivo server.xml de Tomcat para usar SSL
<Connector
port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="c:/Documents and Settings/MyUser/.keystore"
keystorePass="password"
clientAuth="false" sslProtocol="TLS"
/>
Luego, lo cambio a HttpURLConnection for HttpsURLConnection
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
//Conexion por HTTPS
HttpsURLConnection urlHttpsConnection = null;
try {
URL url = new URL(this.getServerURL());
//urlConnection = (HttpURLConnection) url.openConnection();
//Si necesito usar HTTPS
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts();
//Creo la Conexion
urlHttpsConnection = (HttpsURLConnection) url.openConnection();
//Seteo la verificacion para que NO verifique nada!!
urlHttpsConnection.setHostnameVerifier(DO_NOT_VERIFY);
//Asigno a la otra variable para usar simpre la mism
urlConnection = urlHttpsConnection;
} else {
urlConnection = (HttpURLConnection) url.openConnection();
}
//Do the same like up
y agrega un método trustAllHosts para confiar en cada servidor (no verifique ningún certificado)
private static void trustAllHosts() {
X509TrustManager easyTrustManager = new X509TrustManager() {
public void checkClientTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public void checkServerTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
Esos cambios funcionaron muy bien, pero no quiero confiar en cada servidor. Quiero usar mi archivo de almacén de claves para validar la conexión y usar SSL de la manera correcta. He leído mucho en Internet y he realizado muchas pruebas, pero no puedo entender lo que tengo que hacer y cómo hacerlo.
¿Puede alguien ayudarme?
Muchas gracias
Disculpa mi pobre ingles
------------------------- ACTUALIZACIÓN 2011/08/24 ------------------- ------------------------------
Bueno, todavía estoy trabajando en esto. Hice un nuevo método para configurar KeyStore, InputStream, etc.
El método se ve así:
private static void trustIFNetServer() {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
String keyPassword = "password";
ks.load(in, keyPassword.toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tms, new java.security.SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
}
Primero tuve muchos problemas con la clave y el certificado, pero ahora está funcionando (creo)
Mi problema ahora mismo es una excepción de TimeOut. No sé por qué se genera. Creo que es algo con la escritura de datos, pero no puedo resolverlo todavía.
¿Alguna idea?
Debe crear un archivo de tienda de confianza para su certificado autofirmado como se describe aquí . Úselo en el lado del cliente para conectarse con su servidor. Realmente no importa si usas JKS u otro formato, por ahora asumiré JKS.
Para lograr lo que tiene en mente, necesita un TrustManager
diferente, obviamente. Puede usar TrustManagerFactory
y alimentar su configuración de confianza con su tienda de confianza recién creada.
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("<path to your key store>");
ks.load(in, "password".toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
Utilice tms
para tms
su SSLContext
en su lugar para la nueva configuración de confianza que se utilizará para su conexión SSL / TLS.
También debe asegurarse de que la parte CN
del certificado TLS del servidor sea igual al FQDN (nombre de dominio completo) de su servidor, por ejemplo, si la URL base de su servidor es '' https://www.example.com '', entonces la CN
del certificado debe ser ''www.example.com''. Esto es necesario para la verificación de nombre de host, una función que evita los ataques de hombre en el medio. Puede desactivar esto, pero solo cuando lo use su conexión será realmente segura.
Crea tu tienda de confianza, almacena como activo y úsala para inicializar este SocketFactory . Luego use la fábrica en lugar de la suya propia.
Si quiere ignorar todo el certificado, ignore el saludo, entonces esto funciona: HttpsURLConnection y conexiones intermitentes
Sería muy feliz si alguien se beneficia con el siguiente código que será útil para:
1) Usando HttpUrlConnection para hacer una solicitud simple.
2) Usando HttpsUrlConnection al confiar en todos los certificados.
3) Usando HttpsUrlConnection con certificado personalizado para llamadas seguras (Usando el archivo .bks)
---------------- Llame a estos métodos según sus requisitos --------------
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
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.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class MainActivity extends AppCompatActivity {
TextView txtResult;
public static final String TAG = MainActivity.class.getName();
public static final String SERVER_AUTH_URL = "https://demo.your.secure.ulr"; //Https i.e. Secure request
String result = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtResult = (TextView)findViewById(R.id.txt_result);
AsyncRequest asyncRequest = new AsyncRequest();
asyncRequest.execute();
}
public class AsyncRequest extends AsyncTask<Void, Void, Void>
{
@Override
protected Void doInBackground(Void... params) {
//result = sendGETRequestHttp(); //HttpUrlConnection request
//result = sendGETRequestHttpsTrustAll(); //HttpsUrlConnection request, trust all built in certificate in device
result = sendGETRequestHttpsWithCertificate(MainActivity.this); //request for secure connection with custom certificate
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
txtResult.setText(result);
}
}
private String sendGETRequestHttp() { // String requestString (If have POST parameters)
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
try {
URL url = new URL("http://www.yoururl.com/"); //Simple HttpURLConnection request
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("GET"); //("POST");
//OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
//out.write(requestString.getBytes());
//out.flush();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
dis = new DataInputStream(in);
int ch;
long len = urlConnection.getContentLength();
if (len != -1) {
for (int i = 0; i < len; i++)
if ((ch = dis.read()) != -1) {
messagebuffer.append((char) ch);
}
} else {
while ((ch = dis.read()) != -1)
messagebuffer.append((char) ch);
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return messagebuffer.toString();
}
private String sendGETRequestHttpsTrustAll() { // String requestString (If have POST parameters)
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
try {
URL url = new URL(SERVER_AUTH_URL); //("https://www.codeproject.com/");
//Connection port HTTPS
HttpsURLConnection urlHttpsConnection = null;
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts(); //Trust all certificate
//Open Connection
urlHttpsConnection = (HttpsURLConnection) url.openConnection();
//Set Verifier
urlHttpsConnection.setHostnameVerifier(DO_NOT_VERIFY);
//Assigning value
urlConnection = urlHttpsConnection;
} else {
urlConnection = (HttpURLConnection) url.openConnection();
}
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("GET"); //("POST");
//OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
//out.write(requestString.getBytes());
//out.flush();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
dis = new DataInputStream(in);
int ch;
long len = urlConnection.getContentLength();
if (len != -1) {
for (int i = 0; i < len; i++)
if ((ch = dis.read()) != -1) {
messagebuffer.append((char) ch);
}
} else {
while ((ch = dis.read()) != -1)
messagebuffer.append((char) ch);
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return messagebuffer.toString();
}
private static void trustAllHosts() {
X509TrustManager easyTrustManager = 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;
}
};
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{easyTrustManager};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
private String sendGETRequestHttpsWithCertificate(Context context){ // String requestString (If have POST parameters)
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpsURLConnection urlConnection = null;
try{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.yourtruststore); //.bks file in raw folder of resources
String keyPassword = "pass"; //Your Password for cetificate
ks.load(in, keyPassword);
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tms, new java.security.SecureRandom());
sc.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL(SERVER_AUTH_URL);
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(sc.getSocketFactory());
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("GET"); //("POST");
//OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
//out.write(requestString.getBytes());
//out.flush();
InputStream inStream = new BufferedInputStream(urlConnection.getInputStream());
dis = new DataInputStream(inStream);
int ch;
long len = urlConnection.getContentLength();
if (len != -1) {
for (int i = 0; i < len; i++)
if ((ch = dis.read()) != -1) {
messagebuffer.append((char) ch);
}
} else {
while ((ch = dis.read()) != -1)
messagebuffer.append((char) ch);
}
dis.close();
} catch (Exception e) {
Log.d(TAG,"Connection error = "+e.getMessage());
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return messagebuffer.toString();
}
}