Utilizando dos claves privadas (almacén de claves) y dos claves públicas (almacén de confianza) en una conexión SSL Socket

Necesito usar un par de claves en una conexión SSL de un socket sin cambiar nada en los clientes.

¿Por qué

Porque un cliente usa un atributo CN en el almacén de confianza para el protocolo de enlace de conexión y otros clientes que usan otro valor en el mismo atributo para procesar la misma tarea de la misma manera.

Así que necesito usartwo key store (privado)con distintos atributos CN y también alias ycompartir dos tiendas de confianza diferentes (clave pública) con atributos CN distintos y también alias también.

Describiendo a continuación:

keyStore1

Tipo de almacén de claves: JKS

roveedor de @Keystore: SUN

Sobre nombre: identity1

Propietario: CN = app1 ...

Issuer: CN = app1 ...

trustStore1

Sobre nombre: identity1

Propietario: CN = app1 ...

Issuer: CN = app1 ...

keyStore2

Sobre nombre: identity2

Propietario: CN = app2 ...

Issuer: CN = app2 ...

trustStore2

Sobre nombre: identity2

Propietario: CN = app2 ...

Issuer: CN = app2 ...

Intenté implementar esta función de esta manera:

    KeyStore KeyStore1;

                try {
                    String keyStoreFile1 = "privatekey1";
                    String keyStoreType1 = "jks";
                    char[] keyStorePwd1 = "password".toCharArray();

                    keyStore1 = KeyStore.getInstance(keyStoreType1);
                    keyStore1.load(new FileInputStream(keyStoreFile1), keyStorePwd1);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load keystore (" + thr + ")");
                }

                KeyStore trustStore1;

                try {
                    String trustStoreFile1 = "publickey1";
                    String trustStoreType1 = "jks";
                    char[] trustStorePwd1 = "password".toCharArray();

                    trustStore1 = KeyStore.getInstance(trustStoreType1);
                    trustStore.load(new FileInputStream(trustStoreFile1), trustStorePwd1);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load truststore (" + thr + ")");
                }


                KeyStore keyStore2;

                try {
                    String keyStoreFile2 = "privatekey2";
                    String keyStoreType2 = "jks";
                    char[] keyStorePwd2 = "anotherpass".toCharArray();

                    keyStore2 = KeyStore.getInstance(key2StoreType);
                    keyStore2.load(new FileInputStream(keyStoreFile2), keyStorePwd2);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load keystore (" + thr + ")");
                }

                KeyStore trustStore2;

                try {
                    String trustStoreFile2 = "publickey2";
                    String trustStoreType2 = "jks";
                    char[] trustStorePwd2 = "anotherpass".toCharArray();

                    trustStore2 = KeyStore.getInstance(trustStoreType2);
                    trustStore2.load(new FileInputStream(trustStoreFile2), trustStorePwd2);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load truststore (" + thr + ")");
                }



                KeyManagerFactory kmfkey1 = KeyManagerFactory
.getInstance(KeyManagerFactory.getkey1Algorithm());

                kmfkey1.init(keyStore1, "password".toCharArray());

                TrustManagerFactory tmfkey1 =
                        TrustManagerFactory.getInstance(TrustManagerFactory.getkey1Algorithm());
                tmfkey1.init(trustStore1);


                SSLContext ctx = SSLContext.getInstance("SSL");
                ctx.init(kmfkey1.getKeyManagers(), tmfkey1.getTrustManagers(), null);


                KeyManagerFactory kmfkey2 =  KeyManagerFactory.
getInstance(KeyManagerFactory.getkey1Algorithm());

                kmfkey2.init(keyStore2, "password".toCharArray());

                TrustManagerFactory tmfkey2 =  TrustManagerFactory
.getInstance(TrustManagerFactory.getkey1Algorithm());

                tmfkey2.init(trustStore2);


                SSLContext ctxkey2 = SSLContext.getInstance("SSL");
                ctxkey2.init(kmfkey2.getKeyManagers(), tmfkey2.getTrustManagers(), null);

                SSLServerSocketFactory sslSrvSockFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

                serverSocket = sslSrvSockFact.createServerSocket(port);

... Pero recibí este mensaje de error

...

java.security.KeyStoreException: almacén de claves no inicializado en java.security.KeyStore.aliases (KeyStore.java:941) en com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl. (SunX509KeyManagerImpl.java:106) en com.sun .net.ssl.internal.ssl.KeyManagerFactoryImpl $ SunX509.engineInit (KeyManagerFactoryImpl.java:41) en javax.net.ssl.KeyManagerFactory.init (KeyManagerFactory.java:192)

...

Así que realmente quiero saber si es posible usar pares de claves en una conexión de socket o resolver esto de una manera diferente que no puedo ver o manejar.

EDIT 1

Bruno, ¿podría darme un ejemplo completo o completo, por favor?

Porque no está claro para mí ...

Intenté dos cosas:

One solutions es poner las dos claves dentro de un almacén de claves privado siguiendo una sugerencia previa ... pero no funciona y recibí el siguiente mensaje:

Exception en el hilo "main" java.lang.NoSuchMethodError: javax.net.ssl.SSLContext.setDefault (Ljavax / net / ssl / SSLContext;) en ... initialiseManager (499)

Tenías razón ... todavía necesito elegir un contexto o

javax.net.ssl.SSLException: Ningún certificado o clave disponible corresponde a los conjuntos de cifrado SSL que están habilitados. mientras intentaba enviar un mensaje al servidor ...

Segundo, seguí tu sugerencia ... pero la conexión SSL Socket no se inicia

Implementé esto con la ayuda de muchos otros tipos que mostraron su código aquí en este sitio ... gracias por todo

private KeyManager[] getKeyManagers() throws IOException, GeneralSecurityException {

        // First, get the default KeyManagerFactory.
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg);

        // Next, set up the KeyStore to use. We need to load the file into
        // a KeyStore instance.

        File keyFile = new File("privatekey1");

        FileInputStream fis = new FileInputStream(keyFile);
        LogManager.log("Loaded keystore privatekey1 " + keyFile.getAbsolutePath(),
LogManager.LOG_LOWEST_LEVEL);
        KeyStore ks = KeyStore.getInstance("jks");
        String keyStorePassword = "password";
        ks.load(fis, keyStorePassword.toCharArray());
        fis.close();
        // Now we initialise the KeyManagerFactory with this KeyStore
        kmFact.init(ks, keyStorePassword.toCharArray());


        KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg);

        File keyFileTwo = new File("privatekey2");

        FileInputStream fisTwo = new FileInputStream(keyFileTwo);
        LogManager.log("Loaded keystore privatekey2 " + keyFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);
        KeyStore ksTwo = KeyStore.getInstance("jks");
        String keyStorePasswordTwo = "password";
        ksTwo.load(fisTwo, keyStorePasswordTwo.toCharArray());
        fisTwo.close();
        // Now we initialise the KeyManagerFactory with this KeyStore
        dkmFact.init(ksTwo, keyStorePasswordTwo.toCharArray());


        // default
        //KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg);
        //dkmFact.init(null, null);


        // Get the first X509KeyManager in the list
        X509KeyManager customX509KeyManager = getX509KeyManager(alg, kmFact);
        X509KeyManager jvmX509KeyManager = getX509KeyManager(alg, dkmFact);

        KeyManager[] km = {new MultiKeyStoreManager(jvmX509KeyManager, customX509KeyManager)};
        LogManager.log("Number of key managers registered:" + km.length, LogManager.LOG_LOWEST_LEVEL);
        return km;
    }

    /**
     * Find a X509 Key Manager compatible with a particular algorithm
     * @param algorithm
     * @param kmFact
     * @return
     * @throws NoSuchAlgorithmException
     */
    private X509KeyManager getX509KeyManager(String algorithm, KeyManagerFactory kmFact)
            throws NoSuchAlgorithmException {
        KeyManager[] keyManagers = kmFact.getKeyManagers();

        if (keyManagers == null || keyManagers.length == 0) {
            throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " produced no key managers");
        }

        X509KeyManager x509KeyManager = null;

        for (int i = 0; i < keyManagers.length; i++) {
            if (keyManagers[i] instanceof X509KeyManager) {
                x509KeyManager = (X509KeyManager) keyManagers[i];
                break;
            }
        }

        if (x509KeyManager == null) {
            throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " did not produce a X509 Key manager");
        }
        return x509KeyManager;
    }

    private void initialiseManager(int iPort) throws IOException, GeneralSecurityException {
        // Next construct and initialise a SSLContext with the KeyStore and
        // the TrustStore. We use the default SecureRandom.

        // load your key store as a stream and initialize a KeyStore
        File trustFile = new File("publicKey");
        LogManager.log("Trust File Loaded " + trustFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);

        InputStream trustStream = new FileInputStream(trustFile);
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());

        // if your store is password protected then declare it (it can be null however)
        char[] trustPassword = "password".toCharArray();

        // load the stream to your store
        trustStore.load(trustStream, trustPassword);

        File trustFileTwo = new File("publicKeyTwo");
        LogManager.log("Trust File Loaded " + trustFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);

        InputStream trustStreamTwo = new FileInputStream(trustFileTwo);
        KeyStore trustStoreTwo = KeyStore.getInstance(KeyStore.getDefaultType());

        // if your store is password protected then declare it (it can be null however)
        char[] trustPasswordTwo = "password".toCharArray();

        // load the stream to your store
        trustStoreTwo.load(trustStreamTwo, trustPasswordTwo);


        // initialize a trust manager factory with the trusted store
        TrustManagerFactory trustFactory =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustFactory.init(trustStore);
        trustFactory.init(trustStoreTwo);

        // get the trust managers from the factory
        TrustManager[] managers = trustFactory.getTrustManagers();


        SSLContext context = SSLContext.getInstance("SSL");
        context.init(getKeyManagers(), managers, new SecureRandom());
        SSLContext.setDefault(context);

        SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

        serverSocket = sslSrvFact.createServerSocket(iPort);
        // this method didn't create a Socket Connection correctly 

    }

    class MultiKeyStoreManager implements X509KeyManager {

        private final X509KeyManager jvmKeyManager;
        private final X509KeyManager customKeyManager;

        public MultiKeyStoreManager(X509KeyManager jvmKeyManager, X509KeyManager customKeyManager) {
            this.jvmKeyManager = jvmKeyManager;
            this.customKeyManager = customKeyManager;
        }

        @Override
        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
            // try the first key manager
            String alias = customKeyManager.chooseClientAlias(keyType, issuers, socket);
            if (alias == null) {
                alias = jvmKeyManager.chooseClientAlias(keyType, issuers, socket);
                LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
            }

            return alias;

        }

        @Override
        public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
            // try the first key manager
            String alias = customKeyManager.chooseServerAlias(keyType, issuers, socket);
            if (alias == null) {
                alias = jvmKeyManager.chooseServerAlias(keyType, issuers, socket);
                LogManager.log("Reverting to JVM Server alias : " + alias ,LogManager.LOG_LOWEST_LEVEL);
            }
            return alias;
        }

        @Override
        public String[] getClientAliases(String keyType, Principal[] issuers) {
            String[] cAliases = customKeyManager.getClientAliases(keyType, issuers);
            String[] jAliases = jvmKeyManager.getClientAliases(keyType, issuers);
            LogManager.log("Supported Client Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length,
                    LogManager.LOG_LOWEST_LEVEL);
            return (String[]) ArrayUtils.addAll(cAliases, jAliases);
        }

        @Override
        public PrivateKey getPrivateKey(String alias) {
            PrivateKey key = customKeyManager.getPrivateKey(alias);
            if (key == null) {
                System.out.println("Reverting to JVM Key : " + alias);
                return jvmKeyManager.getPrivateKey(alias);
            } else {
                return key;
            }
        }

        @Override
        public String[] getServerAliases(String keyType, Principal[] issuers) {
            String[] cAliases = customKeyManager.getServerAliases(keyType, issuers);
            String[] jAliases = jvmKeyManager.getServerAliases(keyType, issuers);
            LogManager.log("Supported Server Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length,
                    LogManager.LOG_LOWEST_LEVEL);
            return (String[]) ArrayUtils.addAll(cAliases, jAliases);
        }

        @Override
        public java.security.cert.X509Certificate[] getCertificateChain(String string) {
            java.security.cert.X509Certificate[] chain = customKeyManager.getCertificateChain("alias_key1");
            if (chain == null || chain.length == 0) {
                LogManager.log("Reverting to JVM Chain : " + string, LogManager.LOG_LOWEST_LEVEL);
                return jvmKeyManager.getCertificateChain("alias_key2");
            } else {
                return chain;
            }
        }
    }

and this gave me this status

* 2012.02.09 18:47:00 Activación de una conexión SSL

2012.02.09 18:47:00 [... :: ejecutar]

2012.02.09 18:47:00 Archivo de confianza Cargado publicKey

2012.02.09 18:47:00 Archivo de confianza cargado publicKeyTwo

2012.02.09 18:47:00 Almacén de claves cargado privateKey privateKey

2012.02.09 18:47:00 Almacén de claves cargado privateKey2 privateKeyTwo

2012.02.09 18:47:00 Número de gerentes clave registrados: 1 *

Pero no pasó nada cuando intenté enviar un mensaje para el servidor ...

Es difícil encontrar un ejemplo que realmente funcione en este caso.

EDIT 2

Hola Bruno

Realmente tienes un conocimiento avanzado sobre este tema, Java y SSH, y agradezco tu ayuda. Toda esta información me ayuda a comprender mejor este problema mientras me muestra el camino ... Muchas gracias

Y nuevamente tienes razón ... Estoy usando código para Java 6 en un Java 5 JRE

Pero siguiendo de nuevo tu receta obtuve este código:

            // Load the key store: change store type if needed
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream fis = new FileInputStream("keyStore1");
        char[] keyPass = "passw".toCharArray();
        try {
            ks.load(fis, keyPass);
        } finally {
            if (fis != null) {
                fis.close();
            }
        }

        // Get the default Key Manager
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(
                KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, keyPass);

        final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0];
        X509KeyManager km = new X509KeyManager() {

            public String chooseServerAlias(String[] keyType, Principal[] issuers, Socket socket) {
                String alias;

                InetAddress remoteAddress = socket.getInetAddress();

                if (remoteAddress.getHostAddress().equalsIgnoreCase("11.111.111.11")) {
                    alias = "alias1";
                    LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                } else {
                    alias = "alias2";
                    LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                }
                return alias;
            }

            public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
                // try this key manager
                String alias = origKm.chooseClientAlias(keyType, issuers, socket);
                LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                return alias;
            }

            public String[] getClientAliases(String keyType, Principal[] issues) {

                String[] cAliases = origKm.getClientAliases(keyType, issues);
                LogManager.log("Supported Client Aliases : " + cAliases.length, LogManager.LOG_LOWEST_LEVEL);
                return cAliases;

            }

            public String[] getServerAliases(String keyType, Principal[] issues) {

                String[] sAliases = origKm.getServerAliases(keyType, issues);
                LogManager.log("Supported Server Aliases: " + sAliases.length, LogManager.LOG_LOWEST_LEVEL);
                return sAliases;

            }

            public String chooseServerAlias(String keyType, Principal[] issues, Socket socket) {

                // try this key manager
                String alias = origKm.chooseServerAlias(keyType, issues, socket);
                LogManager.log("Reverting to JVM Server alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                return alias;
            }

            public X509Certificate[] getCertificateChain(String keyType) {

                // here I could specify my other keystore, keystore2 how I could do this?

                // I'm thinking in the righ way when I implemented this code to get the correct private key? 

                X509Certificate[] chain = origKm.getCertificateChain("alias1");
                if (chain == null || chain.length == 0) {
                    LogManager.log("Reverting to JVM Chain : " + keyType, LogManager.LOG_LOWEST_LEVEL);

                    return origKm.getCertificateChain("alias2");
                } else {
                    return chain;
                }
            }

            public PrivateKey getPrivateKey(String alias) {

                PrivateKey key = origKm.getPrivateKey(alias);

                // here I could get my other keystore according the alias, for example 
                // keystore2 how I could do this?

                LogManager.log("Reverting to JVM Key : " + alias, LogManager.LOG_LOWEST_LEVEL);
                return key;
            }
        };

        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(new KeyManager[]{km}, null, null);

        SSLServerSocketFactory sslSrvFact = sslContext.getServerSocketFactory();
        objServerSocket = sslSrvFact.createServerSocket(iPort);

¿Es exactamente esto lo que necesito para lograr mi objetivo?

EDIT 3

Utilizando este enfoque, obtuve un apretón de manos entre el cliente y el servidor utilizando el segundo almacén de claves con alias2 utilizando public store2 de confianza en el cliente

... pero aún recibí un error cuando intenté usar trust store1 en el cliente

... recibió un mensaje de [addr = / 11.111.111.11] Volviendo al alias del servidor JVM: alias2 Volviendo a la clave JVM: alias2 Error al recuperar: javax.net.ssl.SSLHandshakeException: Alerta fatal recibida: certificate_unknown

Mi código no cambia el servidor para usar la clave privada1 con alias1 ... cuando el cliente está usando el almacén de confianza público1 con cert alias1 ...

Qué más hay que hacer para resolver esto ... Creo que está cerca de la solución final ...

thx otra vez!

EDIT 4

Bruno, cambié el método getCertificateChain siguiendo su sugerencia que se muestra a continuación

public X509Certificate[] getCertificateChain(String alias) {
      X509Certificate[] chain = origKm.getCertificateChain(alias);
      return chain;
}

y el método

public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
                    String alias;

también eliminé el método duplicado ...

Y en la adicción, los clientes que usan la antigua tienda de confianza no verifican el nombre de host

private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {

    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

Pero para el cliente que necesita usar el segundo almacén de confianza, haga esta verificación y es una razón porque necesito lidiar con los certificados de pares de claves ...

EDIT5

Me gustaría saber cómo puedo implementar un socket de servidor para permitirle identificar y usar el certificado correcto de acuerdo con el certificado que utiliza el cliente para continuar la comunicación de comunicación con el servidor.

Explicando mejor, en el lado del servidor está:

AppServerSideSocket.jar

almacén de claves privado: privateKeyApp (escriba JKS, generado con keytool)public keystore: publicKeyApp (escriba JKS, compartido con todos los clientes)

Y en el lado del cliente ...

AppClientSideSocket.jar - almacén de claves público: publicKeyApp

Los AppServerSideSocket.jar escuchando las solicitudes de los clientes y una vez recibido la información del proceso enviada por los clientes

Los AppClientSideSocket.jar conectarse con el servidor usando SSL usando publicKeyApp sin verificar el nombre de host del servidor y después del apretón de manos, envíe información para la aplicación AppServerSideSocket.

Ahora tengo otra aplicación cliente, AppClientSideSocketNEW.jar, y esto verifica el nombre de host del servidor para comunicarse con el servidor. En este caso, el CN utilizado en el certificado público en el lado del cliente debe coincidir con el nombre de host donde AppServerSideSocket.jar son

Originalmente, la conexión se configuró de esta manera en el lado del servidor:

if (usingSSLConn) {
    System.setProperty("javax.net.ssl.keyStore", "privateKeyApp");
    System.setProperty("javax.net.ssl.keyStorePassword", "privateKeyAppPassword");

    SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
    ServerSocket serverSocketApp = sslServerSocketFactory.createServerSocket(Port);

} else
    serverSocketApp = new ServerSocket(Port);
}

Todos los clientes recibieron la misma publicKeyApp y se conectaron con el servidor sin verificar el nombre de host, por lo que no importa si el servidor es la aplicación de socket del servidor AppServerSideSocket.jar) se instala en el servidor con el nombre de host es badServer1.com y el CN de la clave en privateKeyApp y publicKeyApp se configura con goodServer1.com, porque todos los clientes no verifican el nombre de host o el atributo CN de la clave.

Bellow muestra una pieza de este tipo de conexión

 private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {

        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
 };

System.setProperty("javax.net.ssl.trustStore", publicKey1);
        System.getProperties().setProperty("java.protocol.handler.pkgs", "javax.net.ssl.internal.www.protocol"); 
     HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY);
     ...
     SOAPConnectionFactory soapConn = SOAPConnectionFactory.newInstance();
     SOAPConnection connection = soapConn.createConnection();
     ...     
     URL endpoint = new URL(hostname + ":" + port);

Pero el nuevo cliente AppClientSideSocketNEW.jar) realice esta verificación obligatoriamente, ahora es necesario proporcionar un nuevo certificado para este cliente con un nuevo valor para el atributo CN que refleje el nombre de host correcto CN donde está el socket del servidor.

No tengo acceso al segundo cliente y estoy seguro de que sí verifica el nombre de host.

Así que creé dos nuevos certificados de pares de claves privateKeyAppNew y publicKeyAppNew) y aparentemente las comunicaciones ocurrieron con éxito entre el servidor que usa este nuevo par de claves y el nuevo cliente que usa esta nueva clave pública publicKeyAppNew, después de configurar el servidor para usar este nuevo par de claves, por supuesto.

Pero necesito continuar usando el viejo par de claves para clientes antiguos. Me gustaría saber cómo puedo lidiar con esto.

El uso de un administrador de claves me permite verificar el certificado del cliente en la aplicación del servidor cuando el cliente intenta conectarse y elegir el apropiado y hacer el apretón de manos con el certificado correcto.

¿O necesito una conexión de socket SSL distinta en diferentes puertos para qué tipo de clientes?

Respuestas a la pregunta(4)

Su respuesta a la pregunta