Android: соединение HTTPS (SSL) с использованием HttpsURLConnection
У меня есть 2 приложения, один - сервер Servlet/Tomcat, а другой - приложение для Android.
Я хочу использовать HttpURLConnection для отправки и получения XML между ними.
код:
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();
}
Теперь мне нужно использовать SSL для отправки XML для безопасности.
Во-первых, я использую Java Keytool для генерации файла .keystore.
Keytool -keygen -alias tomcat -keyalg RSA
Затем я поместил XML-код в файл server.xml Tomcat для использования 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"
/>
Затем я изменяю его HttpURLConnection для 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
и добавьте метод trustAllHosts для проверки каждого сервера (не проверяйте какой-либо сертификат)
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();
}
}
Эти изменения работали очень хорошо, но я не хочу доверять каждому серверу. Я хочу использовать файл хранилища ключей для проверки соединения и правильного использования SSL.
Я много читал в Интернете и проводил много тестов, но я не могу понять, что мне нужно делать и как это сделать.
Может кто-нибудь мне помочь?
Большое спасибо
Извините за мой плохой английский
------------------------- UPDATE 2011/08/24 ---------------- ---------------------------------
Хорошо, я все еще работаю над этим. Я сделал новый метод для установки KeyStore, InputStream и т.д.
Метод выглядит следующим образом:
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();
}
}
Сначала у меня было много проблем с ключом и сертификатом, но теперь он работает (я так думаю)
Моя проблема прямо сейчас - это исключение TimeOut. Я не знаю, почему это происходит. Я думаю, что что-то с данными пишут, но я пока не могу решить.
Любая идея?
Ответы
Ответ 1
Вам нужно создать файл хранилища доверия для вашего самозаверяющего сертификата, как описано здесь.
Используйте его на стороне клиента для подключения к вашему серверу. На самом деле не имеет значения, используете ли вы JKS или другой формат, я буду считать JKS на данный момент.
Чтобы выполнить то, что вы имеете в виду, вам нужно другое TrustManager
, очевидно. Вы можете использовать TrustManagerFactory
и перенести свои настройки доверия с помощью только что созданного хранилища доверия.
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();
Используйте tms
для инициализации SSLContext
вместо новых параметров доверия, которые будут использоваться для вашего соединения SSL/TLS.
Также вы должны убедиться, что CN
часть сертификата сервера TLS равна FQDN (полное доменное имя) вашего сервера, например. если ваш базовый URL-адрес сервера " https://www.example.com ', тогда CN
сертификата должен быть" www.example.com ". Это необходимо для проверки имени узла, функции, которая предотвращает атаки" человек-в-середине". Вы можете отключить это, но только при использовании этого соединение будет действительно безопасным.
Ответ 2
Создайте свой магазин доверия, сохраните его как актив и используйте его для инициализации SocketFactory. Затем используйте factory вместо своего собственного "доверять всем".
Ответ 3
Если вы хотите игнорировать весь сертификат, игнорируйте рукопожатие, тогда это работает:
HttpsURLConnection и прерывистые подключения