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

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

Почему?

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

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

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

keyStore1

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

Поставщик хранилища ключей: 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. (SunX509KeyManagerImp6):в com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl $ SunX509.engineInit (KeyManagerFactoryImpl.java:41) в javax.net.ssl.KeyManagerFactory.init (KeyManagerFactory.java:192)

1087* ...

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

РЕДАКТИРОВАТЬ 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 Загруженное хранилище ключей privateKey privateKey

2012.02.09 18:47:00 Загруженное хранилище ключей 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 с использованием общего хранилища доверия 2 в клиенте

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

... получил сообщение от [addr = / 11.111.111.11] Возврат к псевдониму сервера JVM: alias2 Возврат к ключу JVM: alias2 Ошибка при получении: 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 ]

5 голосов
/ 10 февраля 2012

«Очевидная» проблема заключается в том, что вы фактически не используете SSLContext s, которые вы создаете:

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

Это должно быть как минимум:

SSLServerSocketFactory sslSrvSockFact =
    (SSLServerSocketFactory) ctx.getServerSocketFactory();

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

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

Неважно, хотите ли вы использовать одно хранилище ключей или загрузить свой ключ / сертификат из двух хранилищ ключей: если вы действительно этого хотите, вы можете реализовать getPrivateKey и getCertificateChain, чтобы они загружали ключи / Сертификаты из двух разных хранилищ ключей в зависимости от псевдонима. Это было бы излишне сложно, хотя. В любом случае вам все равно придется что-то делать на основе выбора псевдонима, поэтому вы можете также загрузить оба ключа / сертификата из одного хранилища ключей, используя разные псевдонимы.

С точки зрения сервера, единственный способ выбрать один псевдоним (и, следовательно, пару ключ / сертификат) - это использовать то, что доступно в сокете (или движке, если вы используете X509ExtendedKeyManager). Поскольку Java 7 не поддерживает индикацию имени сервера (которая позволяет клиенту сообщать, какое имя хоста запрашивается перед этим процессом выбора), вам может потребоваться сделать это на основе IP-адреса клиента или IP-адреса вашего сервера. используется (если у вас их больше одного).

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

Вы, похоже, не понимаете, что такое хранилище ключей и хранилище доверенных сертификатов Если вы не планируете использовать аутентификацию по сертификату клиента, вы можете игнорировать настройки хранилища доверенных сертификатов на сервере. Вы можете использовать значение по умолчанию (null) как второй параметр вашего SSLContext.init(...). Ваше «хранилище ключей (keystore)» - это информация, используемая локальной стороной (в данном случае вашим сервером), «truststore (хранилище ключей)» используется для определения, какой удаленной стороне следует доверять.

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

EDIT:

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

NoSuchMethodError

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

Обычно эта ошибка отлавливается компилятором; может только эта ошибка происходят во время выполнения, если определение класса несовместимо Измененное.

Это не имеет ничего общего с вашими настройками SSL. Не уверен, что вы сделали здесь, но похоже, что вы можете использовать код для Java 6 на Java 5 JRE (Java 6 не имела setDefault на SSLContext). Что еще более важно, есть что-то неправильное в том, как вы, похоже, используете здесь Java.

javax.net.ssl.SSLException:

Нет доступных сертификатов или ключей, соответствующих комплектам шифров SSL которые включены.

Это вполне может быть объяснено тем фактом, что вы, похоже, не использовали SSLContext, которые вы вообще инициализировали ...

  • Если в вашем хранилище ключей есть две пары личных ключей / сертификатов.

Мой ответ здесь все еще стоит. Я постараюсь сделать это немного более явным. Я предполагаю, что один из ваших сертификатов / секретных ключей использует alias1, а другой alias2. Узнайте, используя keyool -list, если вы не уверены. Вам решать выбрать и настроить их.

// Load the key store: change store type if needed
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream("/path/to/keystore");
try {
    ks.load(fis, keystorePassword);
} finally {
    if (fis != null) { fis.close(); }
}

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

final X509KeyManager origKm = (X509KeyManager)kmf.getKeyManagers()[0];
X509KeyManager km = new X509KeyManager() {
    public String chooseServerAlias(String keyType, 
                                    Principal[] issuers, Socket socket) {
        InetAddress remoteAddress = socket.getInetAddress();
        if (/* remoteAddress has some conditions you need to define yourself */ {
            return "alias1";
        } else {
            return "alias2";
        }
    }

    public String chooseClientAlias(String[] keyType, 
                                    Principal[] issuers, Socket socket) {
       // Delegate this other methods to origKm.
        origKm.chooseClientAlias(keyType, issuers, socket);
    }

    // Delegate this other methods to origKm, in the same way as 
    // it was done for chooseClientAlias.
}

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

SSLSocketFactory sslSocketFactory = sslContext.getSSLSocketFactory();
  • Если вам действительно нужны два разных хранилища ключей.

Сделайте то же самое и, кроме того, в getCertificateChain(String alias) выберите, какое из двух хранилищ ключей использовать в зависимости от псевдонима, и используйте его для получения цепочки сертификатов. То же самое для getPrivateKey(...).

1 голос
/ 09 февраля 2012

Самый простой способ - поместить все в одно хранилище ключей - все ключи и все доверенные сертификаты. Это устранило бы вашу проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...