Como resultado, este fue un problema sutil, y vale la pena dar la respuesta aquí en caso de que alguien más tenga algo similar.
La respuesta TLDR es que no verifiqué que mi clave y certificado no eran nulos y, como resultado, intenté agregar una clave nula y un certificado a un almacén de claves. La respuesta más larga sigue. La forma en que configuramos nuestro servidor web para usar SSL, específicamente para soportar la configuración típica de nuestro usuario donde la dirección IP se usa para configurar la dirección de escucha del sitio web en lugar de un nombre DNS, es que ubica el certificado en la clave de la tienda maestro usando el alias, y crea una clave-tienda efímera que contiene sólo el certificado para ese sitio web, utilizando esa clave-tienda para configurar un contexto SSL y una fábrica de sockets SSL, así:
// 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);
}
Observe que utiliza una contraseña en blanco para extraer la clave privada y los certificados del almacén de claves maestro. Ese era mi problema: por costumbre, al usar keytool, creé el par de claves privadas con una contraseña (la misma contraseña que el almacén de claves).
porque tenía una contraseña en el certificado, la clave y el certificado no se extrajeron, y nulo se pasó a sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
Sin embargo, setKeyEntry
comprueba el pasado Key
utilizando instanceof
y llega a la conclusión (correctamente) que null
no es una instanceof PrivateKey
, resultando en el error engañoso que estaba viendo.
El código corregido comprueba que la clave y el certificado se encontraron errores y envía apropiadas:
// 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);
}
Gracias; Voy a ver esto. –