Установка ssl keystore во время выполнения в Jetty

Можно ли изменить хранилище ключей во время выполнения? В настоящее время я настраиваю SSL до того, как сделаю server.start() -

sslContextFactory.setTrustStore(ks);
sslContextFactory.setTrustStorePassword(TRUSTSTORE_PASS);
sslContextFactory.setKeyStorePassword(KEYSTORE_PASS); 
ServerConnector https = new ServerConnector(server, sslContextFactory);

server.start()

Мне бы хотелось создать сертификат во время выполнения и использовать его. В основном я создаю такой инструмент, как Fiddler, который создает сертификаты на лету.

Ответ 1

После публикации этого вопроса в списке рассылки Jetty я получил ответ, что это не реально выполнимо

Ответ 2

Это исправлено с Jetty 9.4.0, см. https://github.com/eclipse/jetty.project/issues/918. Теперь вы можете просто переопределить Key/TrustStore и т.д. И вызвать SslContextFactory.reload.

Заметьте, однако есть предостережение с возобновлением сеанса TLS: https://github.com/eclipse/jetty.project/issues/918#issuecomment-250791417. Согласно комментариям, это не должно быть проблемой для обычных браузеров, но кто знает об IE, мобильных, не-браузерах и т.д.

Ответ 3

Я не эксперт в пакетах Java-безопасности, но, насколько мне известно, нет прямого способа создания ключевой пары из открытого API.

Однако, я могу, если вы позволите своему коду импортировать пакеты с ограниченным солнцем, например:

import sun.security.x509.*;

Вот схема кода, который вы ищете:

PrivateKey privkey = pair.getPrivate();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
//Validity for next one year
Date to = new Date(from.getTime() + (365) * 86400000l);

CertificateValidity interval = new CertificateValidity(from, to);

BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(dn);

info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));

// Sign the cert
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);

//cert object is ready to use

Надеюсь, что это поможет

Ответ 4

Создайте собственную реализацию KeyStore.

Вы можете создать класс, который переопределяет KeyStore и помещает его в качестве доверия к Jetty. Затем вы можете вернуть любой Certificate, который вы хотите.

Возможно, вам нужно использовать стороннюю библиотеку для создания сертификатов "на лету", поскольку Java не может создавать сертификаты (с официальным API). Вы можете использовать BouncyCastle для этого.

Ответ 5

Кажется, здесь есть две проблемы: генерация сертификата на динамическом уровне ( "Что бы я хотел сделать, это создать сертификат во время выполнения и использовать его" ) и настроить его без перезапуска ( "Возможно ли изменить keystore во время выполнения?" ).

  • Чтобы генерировать сертификат динамически, вы можете использовать BouncyCastle и его класс X509V3CertificateGenerator.

    • Сначала создайте самозаверяющий CA (с базовым набором ограничений CA), используя keytool например (посмотрите в параметре -ext). Это будет ваш пользовательский CA.

    • Экспортируйте сертификат из этого хранилища ключей (только сертификат CA, а не его закрытый ключ) и импортируйте его в клиенты, которые вы собираетесь использовать.

    • В вашем приложении, используя этот закрытый ключ для подписания с X509V3CertificateGenerator, и убедитесь, что DN вашего эмитента совпадает с DN субъекта сертификата CA, который вы создали выше.

    • Затем вам нужно настроить генерировать сертификат с помощью DN темы (или альтернативного имени субъекта), который соответствует имени хоста, с которым должен связаться ваш клиент. Это может быть сложным битом, если вы намереваетесь сделать это автоматически как своего рода прозрачный прокси. (Насколько я знаю, текущие версии Java не могут читать имя, поступающее из расширения SNI, по крайней мере, не заранее или без дополнительной ручной обработки.) Более простым способом было бы иметь это имя хоста в качестве настраиваемого параметра в вашем инструменте.

  • Чтобы настроить его без перезапуска сервера, вы можете реализовать свой собственный X509KeyManager, который остается на месте в SSLContext, который вы используете, но для которого вы сохраняете ссылку и пользовательские аксессоры, позже настройте сертификат. Это, конечно, не обязательно что-то "чистое", и я его не пробовал, но он должен работать в принципе. (Вы должны обязательно убедиться, что аспекты concurrency обрабатываются должным образом.)

    Это может позволить вам не отключать прослушивающий сокет, перенастроить SSLContext и перезапустить сокет. Учитывая, что в любом случае вам может потребоваться взаимодействие с вашим приложением (для перенастройки имени хоста), это может быть чрезмерным.