java - respuesta - keytool opcion no permitida
Elegir certificado de cliente SSL en Java (3)
Nuestro sistema se comunica con varios proveedores de servicios web. Todos se invocan desde una única aplicación de cliente Java. Todos los servicios web hasta ahora han estado en SSL, pero ninguno usa certificados de cliente. Bueno, un nuevo compañero está cambiando eso.
Hacer que la aplicación use un certificado para la invocación es fácil; establecer javax.net.ssl.keyStore
y javax.net.ssl.keyStorePassword
lo hará. Sin embargo, ahora el problema es cómo hacerlo para que solo use el certificado al invocar ese servicio web en particular. Supongo que, en términos más generales, nos gustaría poder elegir el certificado del cliente que se utilizará, si corresponde.
Una solución rápida podría ser establecer las propiedades del sistema, invocando los métodos y luego desarmarlos. El único problema con eso es que estamos tratando con una aplicación de subprocesos múltiples, por lo que ahora tendríamos que ocuparnos de la sincronización o bloqueos, o lo que sea.
Se supone que cada cliente de servicio es completamente independiente el uno del otro, y están empaquetados individualmente en JAR separados. Por lo tanto, una opción que se me ha ocurrido (aunque no la hemos analizado adecuadamente) es aislar de alguna manera cada JAR, tal vez cargar cada uno en una máquina virtual diferente con diferentes parámetros. Esa es simplemente una idea que no sé cómo implementar (o si es posible, para el caso).
Esta publicación sugiere que es posible seleccionar un certificado individual de un almacén de claves, pero la forma de adjuntarlo a la solicitud parece ser un problema diferente.
Estamos utilizando Java 1.5, Axis2 y las clases de clientes generadas con wsimport
o wsdl2java
.
Inicialicé las instancias EasySSLProtocolSocketFactory y Protocol para diferentes puntos finales y EasySSLProtocolSocketFactory el protocolo con una clave única como esta:
/**
* This method does the following:
* 1. Creates a new and unique protocol for each SSL URL that is secured by client certificate
* 2. Bind keyStore related information to this protocol
* 3. Registers it with HTTP Protocol object
* 4. Stores the local reference for this custom protocol for use during furture collect calls
*
* @throws Exception
*/
public void registerProtocolCertificate() throws Exception {
EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory();
easySSLPSFactory.setKeyMaterial(createKeyMaterial());
myProtocolPrefix = (HTTPS_PROTOCOL + uniqueCounter.incrementAndGet());
Protocol httpsProtocol = new Protocol(myProtocolPrefix,(ProtocolSocketFactory) easySSLPSFactory, port);
Protocol.registerProtocol(myProtocolPrefix, httpsProtocol);
log.trace("Protocol [ "+myProtocolPrefix+" ] registered for the first time");
}
/**
* Load keystore for CLIENT-CERT protected endpoints
*/
private KeyMaterial createKeyMaterial() throws GeneralSecurityException, Exception {
KeyMaterial km = null;
char[] password = keyStorePassphrase.toCharArray();
File f = new File(keyStoreLocation);
if (f.exists()) {
try {
km = new KeyMaterial(keyStoreLocation, password);
log.trace("Keystore location is: " + keyStoreLocation + "");
} catch (GeneralSecurityException gse) {
if (logErrors){
log.error("Exception occured while loading keystore from the following location: "+keyStoreLocation, gse);
throw gse;
}
}
} else {
log.error("Unable to load Keystore from the following location: " + keyStoreLocation );
throw new CollectorInitException("Unable to load Keystore from the following location: " + keyStoreLocation);
}
return km;
}
Cuando tengo que invocar el servicio web, hago esto (que básicamente reemplaza "https" en la URL con https1, https2 o algo más, dependiendo del protocolo que haya inicializado para ese extremo en particular):
httpClient.getHostConfiguration().setHost(host, port,Protocol.getProtocol(myProtocolPrefix));
initializeHttpMethod(this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix));
¡Funciona a las mil maravillas!
La configuración se realiza a través de SSLContext
, que es efectivamente una fábrica para SSLSocketFactory
(o SSLEngine
). Por defecto, esto se configurará desde las propiedades javax.net.ssl.*
. Además, cuando un servidor solicita un certificado, envía un mensaje CertificateRequest
TLS / SSL que contiene una lista de nombres completos de CA que está dispuesto a aceptar. Aunque esta lista es estrictamente solo indicativa (es decir, los servidores pueden aceptar certificados de emisores que no figuran en la lista o pueden rechazar certificaciones válidas de CA en la lista), generalmente funciona de esta manera.
De forma predeterminada, el selector de certificados en el X509KeyManager
configurado dentro de SSLContext
(de nuevo, normalmente no tiene que preocuparse por ello), elegirá uno de los certificados emitidos por uno en la lista (o puede estar encadenado a un emisor). ahí). Esa lista es el parámetro de los issuers
en X509KeyManager.chooseClientAlias
(el alias
es el nombre del alias para el certificado que desea seleccionar, como se menciona en el almacén de claves). Si tiene varios candidatos, también puede usar el parámetro de socket
, que le proporcionará la dirección IP del par si eso ayuda a hacer una elección.
Si esto ayuda, puede encontrar usando jSSLutils (y su envoltorio) para la configuración de su SSLContext
(estas son principalmente clases de ayuda para construir SSLContext
s). (Tenga en cuenta que este ejemplo es para elegir el alias del lado del servidor, pero se puede adaptar, el código fuente está disponible ).
Una vez hecho esto, debe buscar la documentación relacionada con la propiedad del sistema axis.socketSecureFactory
en Axis (y SecureSocketFactory
).Si observa el código fuente de Axis, no debería ser demasiado difícil crear una org.apache.axis.components.net.SunJSSESocketFactory
que se haya inicializado a partir del SSLContext
de su elección (consulte esta pregunta ).
SecureSocketFactory
di cuenta de que SecureSocketFactory
Axis2, donde parece que SecureSocketFactory
ha desaparecido. Es posible que pueda encontrar una solución mediante el SSLContext
predeterminado, pero esto afectará a toda la aplicación (lo cual no es genial). Si usa un X509KeyManagerWrapper de jSSLutils, es posible que pueda usar el X509KeyManager
predeterminado y tratar solo ciertos hosts como una excepción. (Esta no es una situación ideal, no estoy seguro de cómo usar un SSLContext
/ SSLSocketFactory
en Axis 2)
Alternativamente, de acuerdo con este documento de Axis 2 , parece que Axis 2 utiliza Apache HTTP Client 3.x:
Si desea realizar autenticación de cliente SSL (SSL bidireccional), puede usar la función Protocol.registerProtocol de HttpClient. Puede sobrescribir el protocolo "https" o utilizar un protocolo diferente para las comunicaciones de autenticación del cliente SSL si no quiere meterse con el https normal. Encuentre más información en http://jakarta.apache.org/commons/httpclient/sslguide.html
En este caso, SslContextedSecureProtocolSocketFactory
debería ayudarlo a configurar un SSLContext
.
Los clientes SSL de Java solo enviarán un certificado si así lo solicita el servidor. Un servidor puede enviar una sugerencia opcional sobre qué certificados aceptará; esto ayudará a un cliente a elegir un solo certificado si tiene múltiples.
Normalmente, se crea un nuevo SSLContext
con un certificado de cliente específico, y las instancias de Socket
se crean a partir de una fábrica obtenida de ese contexto. Desafortunadamente, Axis2 no parece ser compatible con el uso de un SSLContext
o un SocketFactory
personalizado. Sus configuraciones de certificado de cliente son globales.