Тестирование и модификация Java truststore/keystore хранилищ
Я уже давно не пишу статьи по теме работы. Находки новых знаний и новый опыт присутствуют, но то ли постоянное документирование их в wiki компании то ли быстрое устаревание материала удерживают меня от публикации такой информации в персональном блоге. Текущая статья исключения. Она относится к инструментарию.
Изначально проблемой стало исследование, тестирование и модификация Java truststore и keystore хранилищ на рабочем лептопе. Нужна конкретная версия Java и т.д.. Во время работы с такими хранилищами я накидал скрипт обертку и макрос, что значительно упростило мне процедуру тестов. В купе с этим я использовал подход с непосредственным воспроизведением SSL/TLS подключений в Java приложении.
Такой простой стенд принес очень много пользы. Настолько что я решил о нем написать не только в корпоративной документации, но и записать себе на будущее.
Основу стенда составит docker образ Java-машины
java.sh
#!/bin/sh
exec docker run --rm -ti -v $(pwd):/test -w /test -e HOME=/test -u $(id -u):$(id -g) openjdk:7 $@
Укажите версию jdk, которую вам нужно тестировать
UrlConnectionTest.java
import java.io.IOException;
import javax.net.ssl.HttpsURLConnection;
import java.net.MalformedURLException;
import java.security.cert.Certificate;
import java.net.URL;
public class UrlConnectionTest {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL(System.getProperty("url"));
try {
HttpsURLConnection urlConnect = (HttpsURLConnection) url.openConnection();
urlConnect.setReadTimeout(10000);
urlConnect.setRequestMethod("GET");
System.out.println(urlConnect.getResponseCode());
System.out.println(urlConnect.getCipherSuite());
Certificate[] certs = urlConnect.getServerCertificates();
for(Certificate cert : certs){
System.out.println("----------------------------------------- CERT BEGIN ---------------------------------------------");
System.out.println(cert.toString());
System.out.println("----------------------------------------- CERT END ---------------------------------------------");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Этот кусок кода поможет тестировать SSL/TLS подключения
ну и сам макрос
Makefile
all: test_trueststore
build:
./java.sh javac UrlConnectionTest.java
test_trueststore: build
./java.sh java -Djavax.net.ssl.trustStore=/test/cacerts -Durl=${URL} UrlConnectionTest
test: build
./java.sh java -Djavax.net.ssl.trustStore=/test/cacerts -Djavax.net.ssl.keyStore=/test/.keystore -Djavax.net.ssl.keyStorePassword=changeit -Durl=${URL} UrlConnectionTest
я почистил его от лишнего мусора. "changeit" это стандартный пароль truststore файлов. Файлы cacerts и .keystore должны у вас уже быть. Такая настройка позволяет тестировать подключения по https адресам. Например:
make URL=https://api.cognitive.microsoft.com/sts/v1.0/issueToken
основные операции с хранилищами можно выполнять через сценарий обертку
./java.sh keytool -import -trustcacerts -file Microsoft_Azure_TLS_Issuing_CA_01.pem -alias Microsoft_Azure_TLS_Issuing_CA_01 -keystore cacerts
./java.sh keytool --list -trustcacerts -keystore cacerts
./java.sh keytool -delete -alias Microsoft_Azure_TLS_Issuing_CA_01 -keystore cacerts
P.S. Ну и немного теории:
"javax.net.ssl.keyStore" и "javax.net.ssl.trustStore" параметры используются для создания KeyManagers и TrustManagers (соответственно), для построения SSLContext который содержит настройки SSL/TLS для создания SSL/TLS соединения через SSLSocketFactory или SSLEngine. Система уже имеет при запуске значения по умолчанию для этих параметров, которые могут быть получены через SSLContext.getDefault() или SSLSocketFactory.getDefault(). Различия между trustStore и keyStore хранилищем можно найти в JSSERefGuide
TrustManager: Определяет каким удаленным сертификатам может быть оказано доверие.
KeyManager: Определяет какие сертификаты процесс может отсылать Java-машина для установления соединения со своей стороны.
Хранилище в javax.net.ssl.keyStore содержит ваши ключи и сертификаты, тогда как javax.net.ssl.trustStore должно содержать сертификаты центров сертификации, которым можно доверять при установке соединений с удаленными узлами. Поместив сертификаты центров сертификаций в keyStore вы не заставите Java им доверять. В свою очередь сертификаты из trustStore не могут быть использованы для установки соединения со стороны Java.
Обычно указывать truststore нет необходимости. Им комплектуется любая версия Java дистрибутива ($JAVA_HOME/lib/security/cacerts). Основная проблема в его устаревании. Так как новые сертификаты центров сертификации туда не попадают. Обновлять дистрибутив Java для смены этого файла нет необходимости. Можно обновить его (про то как это сделать частично затронуто в этой статье) либо просто скопировать из более свежей версии Java (например из их docker образов).
По умолчанию Tomcat ищет Keystore в файле .keystore в домашней дериктории пользователя от которого запущен процесс (тут тоже часто используют стандартный пароль "changeit". Вот тут есть смысл его менять).