Пример 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/