Использование двух закрытых ключей (хранилище ключей) и двух открытых ключей (хранилище доверенных сертификатов) в одном сокет-соединении SSL

Мне нужно использовать пару ключей в одном сокете ssl-соединения без изменений в клиентах.

Почему?

Потому что один клиент использует атрибут CN в хранилище доверенных сертификатов для установления соединения, а другие клиенты используют другое значение в том же атрибуте для одинаковой обработки той же задачи.

Так что мне нужно использоватьдва ключевых магазина (частный)с отчетливыми атрибутами CN а также псевдонимы иразделить два разных магазина доверия (открытый ключ) с различными атрибутами CN, а также псевдонимами.

Описание ниже:

keyStore1

Тип хранилища ключей: JKS

Поставщик Keystore: SUN

Псевдоним:identity1

Владелец:CN = app1 ...

Эмитент: CN = app1 ...

trustStore1

Псевдоним:identity1

Владелец:CN = app1 ...

Эмитент: CN = app1 ...

keyStore2

Псевдоним:identity2

Владелец: CN = app2 ...

Эмитент: CN = app2 ...

trustStore2

Псевдоним:identity2

Владелец:CN = app2 ...

Эмитент: CN = app2 ...

Я попытался реализовать эту функцию следующим образом:

    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);

... но я получил это сообщение об ошибке

...

java.security.KeyStoreException: неинициализированное хранилище ключей в java.security.KeyStore.aliases (KeyStore.java:941) в com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl. (SunX509KeyManagerImpl.java:106) в com. net.ssl.internal.ssl.KeyManagerFactoryImpl $ SunX509.engineInit (KeyManagerFactoryImpl.java:41) в javax.net.ssl.KeyManagerFactory.init (KeyManagerFactory.java:192)

...

Поэтому я действительно хочу знать, возможно ли использовать пары ключей в одном сокетном соединении или решить это другим способом, который я не могу увидеть или разобраться.

РЕДАКТИРОВАТЬ 1

Бруно, не могли бы вы привести мне полный или полный пример?

Потому что не понятно для меня ....

Я попробовал две вещи:

Одно из решений - поместить два ключа в хранилище закрытых ключей, следуя предыдущему предложению ... но не работает, и я получил сообщение ниже:

Исключение в потоке "main" java.lang.NoSuchMethodError: javax.net.ssl.SSLContext.setDefault (Ljavax / net / ssl / SSLContext;) в ... initialiseManager (499)

Вы были правы ... Мне все еще нужно выбрать один контекст или

javax.net.ssl.SSLException: ни один из доступных сертификатов или ключей не соответствует включенным комплектам шифров SSL. при попытке отправить сообщение на сервер ...

Во-вторых, я последовал твоему предложению ... но соединение SSL Socket не запускается

Я реализовал это с помощью многих других парней, которые показали свой код здесь на этом сайте ... спасибо за все

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 Активация SSL-соединения

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

2012.02.09 18:47:00 Траст-файл загружен publicKey

2012.02.09 18:47:00 Траст-файл загружен publicKeyTwo

2012.02.09 18:47:00 Loaded keystore privateKey privateKey

2012.02.09 18:47:00 Loaded keystore privateKey2 privateKeyTwo

2012.02.09 18:47:00 Количество зарегистрированных ключевых менеджеров: 1 *

Но ничего не произошло, когда я попытался отправить сообщение на сервер ...

Трудно найти пример, который действительно работает в этом случае.

РЕДАКТИРОВАТЬ 2

Привет бруно

На самом деле вы обладаете глубокими знаниями по этому вопросу, Java и SSH, и я ценю вашу помощь. Вся эта информация поможет мне лучше понять эту проблему, пока показывает мне путь ... Спасибо большое

И снова вы правы ... Я использую код для Java 6 на Java 5 JRE

Но, следуя снова вашему рецепту, я получил этот код:

            // 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);

Это именно то, что мне нужно для достижения моей цели?

РЕДАКТИРОВАТЬ 3

Используя этот подход, мы получили рукопожатие между клиентом и сервером, используя второе хранилище ключей с псевдонимом 2, используя public trust store2 в клиенте.

... но я все еще получаю ошибку, когда я пытался использовать доверие store1 в клиенте

... получил сообщение от [addr = / 11.111.111.11] Возврат к псевдониму сервера JVM: псевдоним2 Возврат к ключу JVM: псевдоним2 Ошибка при получении: javax.net.ssl.SSLHandshakeException: Получено фатальное оповещение: certificate_unknown

Мой код не меняет сервер для использования закрытого ключа1 с псевдонимом1 ... когда клиент использует публичное хранилище доверенных сертификатов1 с сертификатом псевдоним1 ...

Что еще нужно сделать, чтобы решить это ... Я считаю, что близок к окончательному решению ...

спасибо снова!

РЕДАКТИРОВАТЬ 4

Бруно, я изменил метод getCertificateChain, следуя твоему предложению, показанному ниже

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

и метод

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

также я удалил дублированный метод ...

И в зависимости клиенты, которые используют старое хранилище доверия, не проверяют имя хоста

private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {

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

Но для клиента, которому нужно использовать второе хранилище доверия, выполните эту проверку, и это причина, потому что мне нужно иметь дело с сертификатами пары ключей ...

EDIT5

Я хотел бы знать, как я могу реализовать сокет сервера, чтобы он мог идентифицировать и использовать правильный сертификат в соответствии с сертификатом, используемым клиентом для установления связи рукопожатия с сервером.

Объясняя лучше, на стороне сервера это:

AppServerSideSocket.jar

хранилище ключей:privateKeyApp (введите JKS, сгенерированный с помощью keytool)хранилище открытых ключей:publicKeyApp (введите JKS, общий для всех клиентов)

И на стороне клиента ...

AppClientSideSocket.jar - хранилище открытых ключей:publicKeyApp

AppServerSideSocket.jar прослушивание запросов клиентов и однажды полученная информация о процессе, отправленная клиентами

AppClientSideSocket.jar соединиться с сервером, используя SSL с помощьюpublicKeyApp без проверки имени хоста сервера и после рукопожатия отправьте информацию для приложения AppServerSideSocket.

Теперь у меня есть другое клиентское приложение,AppClientSideSocketNEW.jar, и это проверяет имя хоста сервера, чтобы установить связь с сервером. В этом случае CN, используемый в общедоступном сертификате на стороне клиента, должен совпадать с именем хоста, гдеAppServerSideSocket.jar находятся.

Первоначально соединение было настроено таким образом на стороне сервера:

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);
}

Все клиенты получили один и тот же publicKeyApp и подключаются к серверу без проверки имени хоста, поэтому не имеет значения, является ли сервер серверным сокетом приложения (AppServerSideSocket.jar) устанавливается на сервер с именем хоста badServer1.com, а CN ключа в privateKeyApp и publicKeyApp устанавливается с goodServer1.com, потому что все клиенты не проверяют имя хоста или атрибут CN ключа.

Сильфон показал кусок такого рода связи

 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);

Но новый клиент (AppClientSideSocketNEW.jar) сделайте эту проверку обязательно, теперь необходимо предоставить новый сертификат для этого клиента с новым значением для атрибута CN, отражающего правильное имя хоста CN, где находится сокет сервера.

У меня нет доступа ко второму клиенту, и я уверен, что он выполняет проверку имени хоста.

Итак, я создал два новых сертификата пары ключей (privateKeyAppNew и publicKeyAppNew) и, по-видимому, обмен данными между сервером с использованием этой новой пары ключей и новым клиентом с использованием этого нового открытого ключа publicKeyAppNew произошел успешно, после того как сервер, конечно, настроил использование этой новой пары ключей.

Но мне нужно продолжать использовать старую пару ключей для старых клиентов. Я хотел бы знать, как я могу справиться с этим.

Использование менеджера ключей позволило мне проверить клиентский сертификат в приложении сервера, когда клиент попытается подключиться и выбрать подходящий и выполнить рукопожатие с использованием правильного сертификата?

Или мне нужно отдельное соединение сокета ssl в разных портах для какого типа клиентов?

Ответы на вопрос(2)

Ваш ответ на вопрос