Как настроить SSLContext с использованием сертификата от Azure KeyVault в Java - PullRequest
0 голосов
/ 03 июля 2018

Я работаю над веб-приложением Java, развернутым в экземпляре службы приложений Azure. И мне нужно позвонить в REST API, который защищен с помощью взаимной аутентификации по SSL. Поскольку это служба приложения, я не могу позволить себе роскошь добавлять сертификат и открытый ключ в хранилище ключей и хранилище доверенных сертификатов соответственно, и все это должно быть сделано с помощью кода. Хотя с JCE и SSL мне удалось написать следующее консольное приложение, которое успешно обращается к защищенному API (с помощью других вопросов и ответов StackOverflow):

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

public class TestPFOM {

    public static void main(String[] args) throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
            IOException, UnrecoverableKeyException, KeyManagementException {

        System.out.println("Start test for mutual authentication");
        KeyStore ks = KeyStore.getInstance("PKCS12");
        File file = new File(System.getProperty("user.dir") + "/client.company.com.pfx");
        System.out.println("Loaded PKCS12 from file");
        try (FileInputStream fis = new FileInputStream(file)) {

            ks.load(fis, "password".toCharArray());
            System.out.println("Loaded keys into keystore");
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, "password".toCharArray());
            System.out.println("Initialized KeyStoreManager");
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(kmf.getKeyManagers(), null, new SecureRandom());
            System.out.println("initialized SSLContext");
            SSLSocketFactory factory = sc.getSocketFactory();
            System.out.println("Obtained SSLSocketFactory");

            URL url = new URL("https://services.company.com/api/company_data");
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            System.out.println("Opened secure HTTPS connection");
            connection.setSSLSocketFactory(factory);
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Accept", "application/json");
            StringBuilder stringBuilder = new StringBuilder();
            int responseCode = connection.getResponseCode();
            System.out.println("HTTP response code = " + responseCode);
            try (BufferedReader reader = responseCode == 200
                    ? new BufferedReader(new InputStreamReader(connection.getInputStream()))
                    : new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
                String line = null;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                    stringBuilder.append(line);
                }
            } catch (Exception e) {
                System.err.println("Error: " + e.getMessage());
            }
            System.out.println(stringBuilder.toString());
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
        }
    }
}

Вместо загрузки файла PFX в хранилище ключей мне нужно получить сертификат от Azure Keyvault, который уже хранит сертификат. KeyVaultClient (клиентская библиотека Java от Azure) предоставляет мне механизм для получения объекта X509Certificate. Можно ли инициировать хранилище ключей с помощью объекта X509Certificate вместо файла PFX?

Моя цель - сделать многократно используемый объект SSLContext доступным для механизма обработки запросов, чтобы я мог использовать его для вызова внешнего безопасного API, когда мое веб-приложение получает запрос. И мне нужно сделать это, не полагаясь ни на какие файлы и внешние ключи / хранилища JVM-ключей в файловой системе.

07/05/2018: следуйте проницательному предложению от GPI Я вручную создал SSLContext:

KeyStore keyStore = KeyStore.getInstance("PKCS12");
// Initiate and load empty key store
keyStore.load(null, null);
// clientCert is an X509Certificate object
keyStore.setCertificateEntry("clientCert", clientCert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // PKIX
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

Но когда я использую полученный SSLSocketFactory в соединении HTTPS, я получаю следующую ошибку:

sun.security.validator.ValidatorException:
  PKIX path building failed: 
    sun.security.provider.certpath.SunCertPathBuilderException:
      unable to find valid certification path to requested target

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

ДА вы можете создать KeyStore из сертификата НО вы не можете использовать его для аутентификации клиента или взаимной аутентификации.

Java использует класс KeyStore и связанные файлы для хранения (в общем) трех разных, хотя и связанных между собой видов вещей , как подробно описано в Javadoc для класса . Для аутентификации у вас должен быть PrivateKeyEntry, который содержит, как можно предположить из его имени, закрытый ключ, ПЛЮС, по крайней мере, один сертификат и обычно цепочку из нескольких сертификатов. TrustedCertEntry используется для аутентификации других сторон, и, в частности, другой конечной точки (однорангового) соединения SSL / TLS; когда вы являетесь клиентом SSL / TLS, как здесь, TrustedCertEntry используется для аутентификации сервера путем проверки его сертификата. (Третья возможность, SecretKeyEntry, не используется для SSL / TLS и даже не поддерживается типом хранилища ключей PKCS12, как это реализовано в Java и обычно используется.)

С помощью объекта X509Certificate вы можете создать TrustedCertEntry, и код, полученный из GPI, делает это. TrustedCertEntry (в KeyStore) может использоваться только для аутентификации другой стороны, в этой ситуации сервера. Чтобы аутентифицировать себя, клиента, до сервера, вам нужен не просто сертификат, а личный ключ и цепочка сертификатов, упакованные как PrivateKeyEntry, из которых вы создаете KeyManager, вставляемый в SSLContext и используется JSSE в соответствии с вашим первым кодом.

AFAICS Azure хранилище представляет ключи как com.microsoft.azure.keyvault.webkey.JSONWebKey , которое, по-видимому, ограничено RSA или AES. Если RSA, есть два toRSA метода (перегрузки), которые из описания должны создать KeyPair, который, я полагаю, означает java.security.KeyPair, содержащий нужный вам закрытый ключ, если только нет ограничений, не указанных в том месте, где я смотрел. Я не вижу способа получить цепочку сертификатов напрямую, но, похоже, записи сертификатов имеют ссылки на издателей, которых должно быть достаточно для построения цепочки, хотя я не в состоянии сам это проверить / проверить.

0 голосов
/ 04 июля 2018

KeyVaultClient (клиентская библиотека Java от Azure) предоставляет мне механизм для получения объекта X509Certificate. Можно ли инициировать хранилище ключей с помощью объекта X509Certificate вместо файла PFX?

Да, это так. Шаги

1) Загрузить сертификат Azure в объекте Cert (возможно, X509Certificate)
2) Создайте новый экземпляр KeyStore (независимо от формата, JKS или PKCS12)
3) Инициируйте этот новый KeyStore, вызвав load с нулевым входным потоком , это создаст новое, пустое хранилище.
4) Вручную добавьте сертификат Azure в качестве доверенной записи в хранилище ключей с помощью вызова setCertificateEntry
5) Используйте этот склад ключей в качестве базы вашего TrustManagerFactory

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