Как оказалось, это была тонкая проблема, и здесь стоит дать ответ, если у кого-то есть что-то подобное.
Ответ TLDR заключается в том, что я не проверял, чтобы мой ключ и сертификат не были нулевыми, и в результате попытался добавить нулевой ключ и сертификат в хранилище ключей. Более длинный ответ следует.
То, как наш веб-сервер настроен на использование SSL, в частности, для поддержки типичной конфигурации нашего пользователя, где IP-адрес используется для настройки адреса прослушивания веб-сайта, а не DNS-имени, заключается в том, что он находит сертификат в мастер-хранилище ключей, используя псевдоним, и создает временное хранилище ключей, содержащее только сертификат для этого веб-сайта, используя это хранилище ключей для настройки контекста SSL и фабрики сокетов SSL, например:
// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE
try {
final char[] BLANK_PWD=new char[0];
SSLContext ctx=SSLContext.getInstance("TLS");
KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Key ctfkey=mstkst.getKey(svrctfals,BLANK_PWD);
Certificate[] ctfchn=mstkst.getCertificateChain(svrctfals);
KeyStore sktkst;
sktkst=KeyStore.getInstance("jks");
sktkst.load(null,BLANK_PWD);
sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
kmf.init(sktkst,BLANK_PWD);
ctx.init(kmf.getKeyManagers(),null,null);
ssf=ctx.getServerSocketFactory();
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr);
}
Обратите внимание, что для извлечения секретного ключа и сертификатов из хранилища мастер-ключей используется пустой пароль. Это была моя проблема - я по привычке использовал keytool, создал пару закрытых ключей с паролем (тот же пароль, что и в хранилище ключей).
Поскольку у меня был пароль к сертификату, ключ и сертификат не были извлечены, и ноль был передан sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
Однако setKeyEntry
проверяет переданный Key
, используя instanceof
, и приходит к выводу (правильно), что null
не instanceof PrivateKey
, что приводит к вводящей в заблуждение ошибке, которую я видел.
Исправленный код проверяет, найдены ли ключ и сертификат, и отправляет соответствующие ошибки:
// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE
try {
final char[] BLANK_PWD=new char[0];
SSLContext ctx=SSLContext.getInstance("TLS");
KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Key ctfkey=mstkst.getKey(svrctfals,BLANK_PWD);
Certificate[] ctfchn=mstkst.getCertificateChain(svrctfals);
KeyStore sktkst;
if(ctfkey==null) {
throw new IOException("Cannot create server socket factory: No key found for alias '"+svrctfals+"'");
}
if(ctfchn==null || ctfchn.length==0) {
throw new IOException("Cannot create server socket factory: No certificate found for alias '"+svrctfals+"'");
}
sktkst=KeyStore.getInstance("jks");
sktkst.load(null,BLANK_PWD);
sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
kmf.init(sktkst,BLANK_PWD);
ctx.init(kmf.getKeyManagers(),null,null);
ssf=ctx.getServerSocketFactory();
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr);
}