Как загрузить пароль закрытого ключа в новый (не устаревший) SSLSocketFactory?

Мой текущий клиент TCP создает сокеты, используя org.apache.http.conn.ssl.SSLSocketFactory
Я перехожу на import javax.net.ssl.SSLSocketFactory; Однако я не уверен, как загрузить закрытый ключ для сертификата клиента, который хранится в файле .p12, который у меня уже есть, в качестве контекста для новый SSLSocketFactory?

Вот мой существующий код, использующий устаревший SSLSocketFactory:

import org.apache.http.ssl.SSLContexts;
import org.apache.http.conn.ssl.SSLSocketFactory;

String ksPassphrase = "myKSpass";
String pkPassphrase = "myPKpass";
// Get a keystore instance of type PKCS12:
KeyStore keystore = KeyStore.getInstance("PKCS12");
// Load keystore file and password:
keystore.load(new FileInputStream("myFile.p12"), ksPassphrase.toCharArray());
// Create HTTPS connection socket factory context:
SSLContext context = SSLContexts.custom()
    .loadKeyMaterial(keystore, pkPassphrase.toCharArray())
    .build();

SSLSocketFactory sslScktFactory = new SSLSocketFactory(context);

Вот лучший код, которым я мог управлять из документов:

import javax.net.ssl.*;

// not sure if this is the pass to KS or private key or, somehow both?
String passphrase = "myPassphrase" 

SSLContext context = SSLContext.getInstance("TLS");
KeyManagerFactory keyMngFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("JKS");

keyStore.load(new FileInputStream("myFile.jks"), passphrase.toCharArray());
keyMngFactory.init(keyStore, passphrase);
context.init(keyMngFactory.getKeyManagers(), null, null);

SSLSocketFactory sslScktFactory = context.getSocketFactory();
  • Я думаю, проблема заключается в том, что у меня нет метода .custom() для контекста в новой библиотеке.
    Что использовать для новой SSLSocketFactory?
  • Дело в том, что в хранилищах ключей JKS нет внутренних паролей для закрытых ключей, хранящихся внутри (до сих пор я использовал только PKCS, не знакомый с форматом JKS), и поэтому я не вижу, куда ссылается пароль закрытого ключа к?

person DraxDomax    schedule 14.11.2019    source источник


Ответы (1)


Пароль, который вы передаете KeyStore.load(InputStream,char[]), является паролем магазина; пароль, который вы передаете KeyManagerFactory.init(KeyStore,char[]), является паролем закрытого ключа («the» в единственном числе; должен быть одинаковым для всех ключей, если их несколько), и обратите внимание, что это char[], а не String - ваш компилятор (или IDE) должен был сказать вам об этом . Вам нужно изменить новый код, чтобы использовать KeyStore.getInstance("PKCS12"), если вы хотите продолжать использовать существующие (или любые другие/новые) файлы PKCS12.

Обратите внимание, что API позволяет вам использовать keypass, отличный от storepass, но другое программное обеспечение, с которым вы можете обмениваться файлами PKCS12 или делиться ими (например, Windows, MacOSX, OpenSSL, я думаю, NSS), не поддерживает это, и чтобы попытаться избежать таких проблем, командная строка Java keytool не будет устанавливать keypass, отличный от storepass для PKCS12.

Что использовать для нового SSLSocketFactory?

SSLContext.getSocketFactory() -- именно так, как вы написали. На самом деле это то, что ваш предыдущий, устаревший org.apache.http.conn.ssl.SSLSocketFactory.SSLSocketFactory(SSLContext) делает внутри, за исключением того, что он также по умолчанию использует HostNameVerifier.

Дело в том, что в хранилищах ключей JKS нет внутренних паролей для закрытых ключей, хранящихся внутри (до сих пор я использовал только PKCS, не знакомый с форматом JKS), и поэтому я не вижу, куда ссылается пароль закрытого ключа к?

Нет; В магазинах JKS есть отдельные storepass и keypass, которые будут использоваться в опубликованном вами коде, если он будет исправлен, как я описал выше, хотя шифрование с закрытым ключом, которое JKS использует, keypass for слабее, чем обычно используется в PKCS12. И поскольку файлы JKS в любом случае не совместимы с другим программным обеспечением, keytool поддерживает передачу ключей, отличных от storepass. На самом деле keytool (и JCA в целом) поддерживает разные значения паролей для разных закрытых ключей в одном хранилище JKS, но KeyManagerFactory (для SSL/TLS, т.е. JSSE) не поддерживает это, равно как и Apache (какой другой чем добавление/обертывание ключевой стратегии choice просто использует JSSE внизу).

(OP) Изменить: вот некоторый проверенный рабочий код, полученный в результате вашего предложения:

// Consider having both the same, for compatibility with Windows/MacOSX/OpenSSL/etc:
String ksPassphrase = "myKSpass";
String pkPassphrase = "myPKpass";

SSLContext context = SSLContext.getInstance("TLS");
KeyManagerFactory keyMngFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");

keyStore.load(new FileInputStream("myFile.p12"), 
ksPassphrase.toCharArray());
keyMngFactory.init(keyStore, pkPassphrase.toCharArray());
context.init(keyMngFactory.getKeyManagers(), null, null);

SSLSocketFactory sslScktFactory = context.getSocketFactory();
person dave_thompson_085    schedule 14.11.2019
comment
очень познавательно, спасибо! Я надеюсь, что это нормально, что я добавил фиксированный фрагмент кода, чтобы лучше визуализировать ваш совет. - person DraxDomax; 15.11.2019
comment
Я всегда задавался вопросом: не является ли механизм выбора ключей внутри хранилища ключей довольно грубым? Я имею в виду: когда я ищу зашифрованные файлы в своей программе, я называю файл, который хочу найти, и предоставляю пароль. Здесь мы предоставляем только пароль. кажется, что init() будет проходить через хранилище ключей, пока ему не удастся разблокировать ключ с помощью пароля? Кроме того, для меня это имеет еще меньше смысла, поскольку разные закрытые ключи даже не поддерживаются во всех системах! Итак, как мы узнаем, какой закрытый ключ был получен тогда? Или это как трастовый магазин, где все ключи представлены вместе? Разве это не неэффективно? - person DraxDomax; 15.11.2019
comment
еще одно примечание: на самом деле мне дали JKS, я поленился и попробовал, не конвертируя в PKCS12, и он все еще работает! Объект 'keystore', экземпляр которого был создан с помощью .getInstance(PKCS12), позже выполнил init() для файла JKS, что привело к хорошему сокету. Я даже не думаю, что буду конвертировать свой файл JKS: D - person DraxDomax; 15.11.2019