Пример HTTPS клиента и HTTPS сервера с двусторонней аутентификацией
Остался вариант двусторонней аутентификации. Для этого мне нужно на клиентской стороне создать keystore с клиентским ключом и заимпортить клиентский сертификат в серверный truststore. Делаю это следующими командами:
keytool -genkey -dname "cn=client-test-ssl,ou=dev64,o=dev64-wordpress,L=unspec, st=unspec, c=RU" -alias client -keypass serverpass -keystore client.jks -storepass storepass
В итоге получаю клиентский keystore c приватным ключом и публичным ключом подписанным self-signed сертификатом. Затем экспортирую клиентский сертификат:
keytool -export -keystore client.jks -alias client -storepass storepass -file client.cer
Получаю файл client.cer. Остается создать на основе полученного клиентского сертификата серверный truststore:
keytool -import -keystore servertrust.jks -file client.cer -storepass storepass
Получаю серверный truststore. Теперь у меня в ресурсах 4 keystore и два сертификата. Сертификаты можно выкинуть, они нужны были только на этапе создания truststore. Остается добавить в сервер чтение truststore, а в клиент keystore с клиентским ключом. Все фрагменты кода уже есть. Остается только слегка изменить серверную сторону и клиентскую сторону.
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 startServerWithTruststore(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);
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(this.getClass().getResourceAsStream("servertrust.jks"), storepass);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure (HttpsParameters params) {
params.setWantClientAuth(true);
params.setNeedClientAuth(true);
}
});
server.setExecutor(null); // creates a default executor
server.start();
}
@Test
public void testTwoWayAuthentication()
throws CertificateException, InterruptedException, UnrecoverableKeyException, NoSuchAlgorithmException,
IOException, KeyManagementException, KeyStoreException {
startServerWithTruststore(8080);
URL url = new URL("https://localhost:8080/");
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
con.setRequestMethod( "GET" );
SSLContext sslContext = SSLContext.getInstance("TLS");
char[] passphrase = "storepass".toCharArray();
char[] keypass = "serverpass".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(SimpleServerTest.class.getResourceAsStream("client.jks"), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, keypass);
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(this.getClass().getResourceAsStream("clienttrust.jks"), passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
public boolean verify(String s, SSLSession sslSession) {
return s.equals(sslSession.getPeerHost());
}
};
con.setHostnameVerifier(hostnameVerifier);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), 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();
}
}
Тестируюсь. Соединение проходит…. Последний штрих. Убираю в клиенте keystore, чтобы проверить, что соединение не пройдет. Однако соединение проходит без ошибок. Копаю где ошибка, не нахожу. Т.о. делаю вывод что встроенный в JDK сервер по-видимому обладает ограниченной функциональностью. Проверка клиентского сертификата в нем не конфигурится (или я не знаю, как это сделать)…. Тем не менее, я думаю, приведенный выше код все равно полезный и для тестирования встроенный в JDK сервер применять можно. На этом завершаю затянувшийся пост.
Взято:
https://dev64.wordpress.com/2013/06/18/configure-embedded-jdk-http-server-for-https/