HTTPS - это по сути обычный HTTP, но данные передаются в зашифрованном виде с помощью SSL/TLS.
Установка соединения происходит в два этапа, соединение всегда инициирует клиент:
- Handshake
- Data
После этого осуществляется собственно обмен данными, зашифрованными с помощью сессионного ключа.
С практической точки зрения клиенту необходимо только решить, доверяет ли он серверу или нет, и предоставить свой сертификат, если нужно.
В java существует специальное хранилище cacerts, в котором уже находятся многие удостоверяющие центры. Это так называемые root CA, которым безусловно доверяют.
При получении серверного сертификата (цепочки сертификатов) клиент проверяет, приводит ли цепочка сертификатов к известному ему CA, которому он доверяет, и если он не находит такую цепочку, выбрасывается эксепшн вида
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target.
Так что пути решения проблемы два:
- Добавить серверный сертификат в cacerts
$openssl s_client -connect ${REMHOST}:${REMPORT} и скопировать в файл все что между строк -----BEGIN CERTIFICATE----- и -----END CERTIFICATE----- (включая их)
Полученный файл сертификата нужно добавить к cacerts командой
$keytool -import -keystore cacerts -alias [myalias] -file server_cert.pem
- Установить trustManager при создании sslContext.
SSLSocketFactory socketFactory = createSSLContext().getSocketFactory();
HttpsURLConnection connection = (HttpsURLConnection) (url).openConnection();
connection.setSSLSocketFactory(socketFactory);
private final SSLContext createSSLContext()
throws NoSuchAlgorithmException, KeyStoreException,
CertificateException, IOException,
UnrecoverableKeyException, KeyManagementException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream in = new FileInputStream("server.pem");
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null);
try {
X509Certificate cacert = (X509Certificate) cf.generateCertificate(in);
trustStore.setCertificateEntry("server_alias", cacert);
} finally {
IOUtils.closeQuietly(in);
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}
О том, как предоставить клиентский сертификат, в следующий раз.
Полезные ссылки:
http://www.zytrax.com/tech/survival/ssl.html