簡體   English   中英

如何使用Java中來自Azure KeyVault的證書設置SSLContext

[英]How do I set up a SSLContext using certificate from Azure KeyVault in Java

我正在研究在Azure應用服務實例上部署的Java Web應用程序。 而且我需要調用一個REST API,該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());
        }
    }
}

我需要從已經存儲了證書的Azure Keyvault中獲取證書,而不是將PFX文件加載到KeyStore中。 KeyVaultClient(Azure的Java客戶端庫)為我提供了一種獲取X509Certificate對象的機制。 是否可以使用X509Certificate對象而不是PFX文件來啟動KeyStore?

我的目標是為請求處理機制提供一個可重用的SSLContext對象,因此當我的Web應用程序接收到請求時,可以使用它來調用外部安全API。 而且我需要做到這一點,而不依賴於文件系統中的任何文件和外部JVM密鑰/信任存儲。

2018年7月5日:遵循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);

但是,當我在HTTPS連接中使用生成的SSLSocketFactory時,出現以下錯誤:

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

KeyVaultClient(Azure的Java客戶端庫)為我提供了一種獲取X509Certificate對象的機制。 是否可以使用X509Certificate對象而不是PFX文件來啟動KeyStore?

是的。 步驟是

1)在Cert對象(可能是X509Certificate )中加載Azure證書
2)創建一個新的KeyStore實例(無論是JKS還是PKCS12格式)
3)通過使用空輸入流調用load初始化此新KeyStore ,這將創建一個新的空存儲。
4)通過調用setCertificateEntry手動將Azure證書添加為KeyStore中的受信任條目
5)使用此密鑰庫作為TrustManagerFactory的基礎

是的,您可以從證書創建KeyStore不能將其用於客戶端身份驗證或相互身份驗證。

Java使用KeyStore類和相關文件來存儲(通常)三種不同但相關的事物, 如Javadoc中對該類所詳述 要對自己進行身份驗證,您必須具有一個PrivateKeyEntry,如其名稱所暗示的那樣,其中包含一個私鑰,再加上至少一個證書,通常還包含多個證書鏈。 TrustedCertEntry用於驗證其他方,尤其是SSL / TLS連接的其他端點(對等方)。 當您是SSL / TLS客戶端時,如此處所示,TrustedCertEntry用於通過驗證服務器的證書來對服務器進行身份驗證。 (第三個可能性,SecretKeyEntry,不用於SSL / TLS,甚至不被Java實現且常用的PKCS12密鑰庫類型所支持。)

使用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 ,其中包含您需要的私鑰,除非在我看過的地方沒有限制。 我沒有找到直接獲取證書鏈的任何方法,但是看來證書條目具有頒發者鏈接,這對您建立證書鏈是足夠的,盡管我自己無法對此進行測試/驗證。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM