Пример клиентского HTTPS соединения с проверкой серверного сертификатаs
Т.о. проведен эксперимент с простейшей односторонней аутентификацией. Недостаток вышеприведенного кода очевиден. В нем применен TrustManager не проверяющий подлинность сервера. Такой код в некоторых случаях достаточен. Но в большинстве — нет. Следующим экспериментом используем нормальный TrustManager с проверкой серверного сертификата. Для этого надо создать truststore с серверным сертификатом. Ищу документацию и нахожу Generating a Keystore and Truststore. Из документа следует, что для создания truststore, нужно с помощью keytool импортировать в него сертификат. Для этого сертификат нужно сначала экспортировать. Делается это следующей командой:
keytool -export -keystore server.jks -alias server-test -storepass storepass -file server.cer
В итоге получается файл server.cer с сертификатом сервера. Теперь создаю на его основе truststore:
keytool -import -keystore clienttrust.jks -file server.cer -storepass storepass
В итоге получаю clienttrust.jks. Ниже на картинке дерево maven-овского проекта. Keystore и truststore лежат в директории resources.
Добавляю в код клиента код читающий и задающий truststore для соединения. Ниже читается keystore с именем clienttrust.jks из ресурсов. Создается TrustManagerFactory на основе keystore.
char[] passphrase = "storepass".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(this.getClass().getResourceAsStream("clienttrust.jks"), passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
Тестирую измененный клиент. Он работает. Серверный сертификат проходит проверку. Т.о. ниже клиентский код для односторонней аутентификации, осуществляющий проверку серверного сертификата по truststore:
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 testClientWithTrustStore()
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");
char[] passphrase = "storepass".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(this.getClass().getResourceAsStream("clienttrust.jks"), passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
public boolean verify(String s, SSLSession sslSession) {
return s.equals(sslSession.getPeerHost());
}
};
con.setHostnameVerifier(hostnameVerifier);
sslContext.init(null, 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();
}
}
Т.о. протестировано соединение с односторонней аутентификацией в двух вариантах: без аутентификации сервера и c аутентификацией сервера.
Взято:
https://dev64.wordpress.com/2013/06/18/configure-embedded-jdk-http-server-for-https/