Подтвердить что ты не робот

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

Я вызываю веб-сервис, требующий аутентификации сертификата клиента.

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

Ссылки, которые 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());

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

Ответ 3

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

Я использую его для создания 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);
    }
}

Ответ 4

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