Пример простого HTTPS соединения без проверки сертификата сервера
Далее коннекчусь к своему серверу программно. Для начала делаю такой код:
@Test
public void testSimpleHttpsClient()
throws CertificateException, InterruptedException, UnrecoverableKeyException, NoSuchAlgorithmException,
IOException, KeyManagementException, KeyStoreException {
startServer(8080);
URL url = new URL("https://localhost:8080/");
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
con.setRequestMethod( "GET" );
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
con.setSSLSocketFactory(sslContext.getSocketFactory());
int responseCode = con.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = con.getInputStream();
} else {
inputStream = con.getErrorStream();
}
// Process the response
BufferedReader reader;
String line = null;
reader = new BufferedReader( new InputStreamReader( inputStream ) );
while( ( line = reader.readLine() ) != null )
{
System.out.println( line );
}
inputStream.close();
}
Стартую сервер на 8080 порту, в метод startServer(int port) перенесен весь код, из предыдущего теста.
startServer(8080);
Указываю, что нужно создать HttpsUrlConnection на URL https://localhost:8080 методом GET.
URL url = new URL("https://localhost:8080/");
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
con.setRequestMethod( "GET" );
Создаю «пустой» SSLContext и на его основе создаю SSLSocketFactory, который передаю в параметры соединению.
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
con.setSSLSocketFactory(sslContext.getSocketFactory());
Запускаю на тестирование, получаю исключение:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1764)
....
Ошибка говорит о том, что не найден сертификат, подтверждающий подлинность сервера. Правильно, я не указал TrustManager-ов. Соответственно сертификаты проверить нельзя. Добавляю пустой TrustManager, пока без сертификатов, разрешающий любые сертификаты.
TrustManager[] trustManagers = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
};
Тестируюсь. Теперь «валидация» сертификата проходит, но получаю другое исключение:
java.io.IOException: HTTPS hostname wrong: should be
at sun.net.www.protocol.https.HttpsClient.checkURLSpoofing(HttpsClient.java:524)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:448)
Теперь сертификат сервера проверяется, но не проходит проверку имя хоста. Добавляю свой собственный HostnameVerifier:
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return s.equals(sslSession.getPeerHost());
}
};
con.setHostnameVerifier(hostnameVerifier);
Тестируюсь, соединение успешно. Результирующий код клиента и сервера, пока, такой:
import com.sun.net.httpserver.*;
import org.junit.Test;
import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SimpleServerTest {
static class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
String response = "This is the response";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
}
}
void startServer(int port) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException,
UnrecoverableKeyException, InterruptedException, KeyManagementException{
HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5);
server.createContext("/", new MyHandler());
char[] storepass = "storepass".toCharArray();
char[] keypass = "serverpass".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(SimpleServerTest.class.getResourceAsStream("server.jks"), storepass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, keypass);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{}, null);
server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure (HttpsParameters params) {
// get the remote address if needed
InetSocketAddress remote = params.getClientAddress();
SSLContext c = getSSLContext();
// get the default parameters
SSLParameters sslparams = c.getDefaultSSLParameters();
params.setSSLParameters(sslparams);
// statement above could throw IAE if any params invalid.
// eg. if app has a UI and parameters supplied by a user.
}
});
server.setExecutor(null); // creates a default executor
server.start();
}
@Test
public void testSimpleHttpsClientWithTrustManagerAndVerifier()
throws CertificateException, InterruptedException, UnrecoverableKeyException, NoSuchAlgorithmException,
IOException, KeyManagementException, KeyStoreException {
startServer(8080);
URL url = new URL("https://localhost:8080/");
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
con.setRequestMethod( "GET" );
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
};
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
public boolean verify(String s, SSLSession sslSession) {
return s.equals(sslSession.getPeerHost());
}
};
con.setHostnameVerifier(hostnameVerifier);
sslContext.init(null, trustManagers, null);
con.setSSLSocketFactory(sslContext.getSocketFactory());
int responseCode = con.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = con.getInputStream();
} else {
inputStream = con.getErrorStream();
}
// Process the response
BufferedReader reader;
String line = null;
reader = new BufferedReader( new InputStreamReader( inputStream ) );
while( ( line = reader.readLine() ) != null )
{
System.out.println( line );
}
inputStream.close();
}
}
Взято:
https://dev64.wordpress.com/2013/06/18/configure-embedded-jdk-http-server-for-https/