Как указать псевдоним исходящего сертификата для HTTPS-звонков? - PullRequest
17 голосов
/ 13 марта 2011

Я звоню в веб-службу, которая требует аутентификации сертификата клиента.

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

Я пробовал следующее свойство, но без ожидаемого результата:

System.setProperty("com.sun.enterprise.security.httpsOutboundKeyAlias", "my-client-certificate alias");

Как указать псевдоним сертификата клиента, который следует использовать?

Ответы [ 4 ]

14 голосов
/ 29 декабря 2011

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

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

Сначала мы создали менеджер ключей:

public class FilteredKeyManager implements X509KeyManager {

private final X509KeyManager originatingKeyManager;
private final X509Certificate[] x509Certificates;

public FilteredKeyManager(X509KeyManager originatingKeyManager, X509Certificate[] x509Certificates) {
    this.originatingKeyManager = originatingKeyManager;
    this.x509Certificates = x509Certificates;
}

public X509Certificate[] getCertificateChain(String alias) {
    return x509Certificates;
}

public String[] getClientAliases(String keyType, Principal[] issuers) {
    return new String[] {"DesiredClientCertAlias"};
}

Все остальные методы, необходимые для реализации, являются переходами к originatingKeyManager.

Тогда, когда мы фактически настроим контекст:

SSLContext context = SSLContext.getInstance("TLSv1");
context.init(new KeyManager[] { new FilteredKeyManager((X509KeyManager)originalKeyManagers[0], desiredCertsForConnection) },
    trustManagerFactory.getTrustManagers(), new SecureRandom());

Надеюсь, это прояснит ситуацию и сработает для всех, кто пытается решить эту проблему.

10 голосов
/ 07 июня 2011

Краткий ответ: это не может быть сделано с реализацией Java ssl по умолчанию.

Длинный ответ: я смотрел на то, как рукопожатие SSL реализовано в sun.security.ssl.ClientHandshaker.В его методе serverHelloDone называется X509ExtendedKeyManager.chooseClientAlias.Его реализации действительно сделаны таким образом, что они возвращают первый псевдоним, чья запись соответствует заданному алгоритму ключа и немногим другим.Нет способа настроить псевдоним.

Для тех, кто может изменить код, это выглядит как многообещающее решение: http://www.44342.com/java-f392-t785-p1.htm

5 голосов
/ 19 сентября 2014

Вот полный отсканированный код, который работает.

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

Кредиты отправляются в zarniwoop.

/**
 * filters the SSLCertificate we want to use for SSL
 * <code>
 * KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
 * kmf.init(keyStore, null);
 * String SSLCertificateKeyStoreAlias = keyStore.getCertificateAlias(sslCertificate);
 * KeyManager[] keyManagers = new KeyManager[] { new FilteredKeyManager((X509KeyManager)kmf.getKeyManagers()[0], sslCertificate, SSLCertificateKeyStoreAlias) };
 * </code>
 */
private class FilteredKeyManager implements X509KeyManager {

    private final X509KeyManager originatingKeyManager;
    private final X509Certificate sslCertificate;
    private final String SSLCertificateKeyStoreAlias;

    /**
     * @param originatingKeyManager,       original X509KeyManager
     * @param sslCertificate,              X509Certificate to use
     * @param SSLCertificateKeyStoreAlias, Alias of the certificate in the provided keystore
     */
    public FilteredKeyManager(X509KeyManager originatingKeyManager, X509Certificate sslCertificate, String SSLCertificateKeyStoreAlias) {
        this.originatingKeyManager = originatingKeyManager;
        this.sslCertificate = sslCertificate;
        this.SSLCertificateKeyStoreAlias = SSLCertificateKeyStoreAlias;
    }

    @Override
    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
        return SSLCertificateKeyStoreAlias;
    }

    @Override
    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
        return originatingKeyManager.chooseServerAlias(keyType, issuers, socket);
    }

    @Override
    public X509Certificate[] getCertificateChain(String alias) {
        return new X509Certificate[]{ sslCertificate };
    }

    @Override
    public String[] getClientAliases(String keyType, Principal[] issuers) {
        return originatingKeyManager.getClientAliases(keyType, issuers);
    }

    @Override
    public String[] getServerAliases(String keyType, Principal[] issuers) {
        return originatingKeyManager.getServerAliases(keyType, issuers);
    }

    @Override
    public PrivateKey getPrivateKey(String alias) {
        return originatingKeyManager.getPrivateKey(alias);
    }
}
1 голос
/ 14 марта 2011

У меня сложилось впечатление, что KeyManager инициализируется с помощью хранилища ключей и использует псевдоним записи личного ключа для поиска соответствующего сертификата и цепочки сертификатов.
В противном случае, я думаю, что он выбирает цепочку на основетипы ключей и центры сертификации, распознаваемые хостом.
Так что в вашем случае ваше описание не упоминает частную запись в хранилище ключей, поэтому я предполагаю, что менеджер ключей выбирает наиболее подходящий сертификат.
Я не в курсена все системные свойства, которые вы упоминаете.
-Попробуйте изменить хранилище ключей, чтобы иметь закрытый ключ и связанную цепочку
-Или (не уверен, будет ли это работать) изменить псевдоним сертификата, который вы хотите отправитьна сервер для соответствия имени субъекта сертификата

...