简体   繁体   English

如何使用Java中来自Azure KeyVault的证书设置SSLContext

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

I am working on a Java web application deployed on an Azure App Service instance. 我正在研究在Azure应用服务实例上部署的Java Web应用程序。 And I need to make a call to a REST API that is secured by requiring mutual authentication over SSL. 而且我需要调用一个REST API,该API需要通过SSL进行相互身份验证来确保安全。 Since this is an app service, I don't have the luxury of adding the certificate and public key to the keystore and truststore respectively, and it has to all be done via code. 由于这是一项应用程序服务,因此我无法将证书和公钥分别添加到密钥库和信任库中,而必须全部通过代码来完成。 Although with JCE and SSL, I managed to write the following console application that accesses the secure API successfully (with the help of other StackOverflow Q&A): 尽管使用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());
        }
    }
}

Instead of loading the PFX file into the KeyStore, I need to get the certificate from Azure Keyvault which already stores the certificate. 我需要从已经存储了证书的Azure Keyvault中获取证书,而不是将PFX文件加载到KeyStore中。 The KeyVaultClient (Java client library from Azure) provides me with a mechanism to obtain an X509Certificate object. KeyVaultClient(Azure的Java客户端库)为我提供了一种获取X509Certificate对象的机制。 Is it possible to initiate a KeyStore with a X509Certificate object, instead of from a PFX file? 是否可以使用X509Certificate对象而不是PFX文件来启动KeyStore?

My goal is to have a reusable SSLContext object available to the request processing mechanism, so I can use it to call the external, secure API when my web application receives a request. 我的目标是为请求处理机制提供一个可重用的SSLContext对象,因此当我的Web应用程序接收到请求时,可以使用它来调用外部安全API。 And I need to do this without relying on any files and external JVM key/trust stores in the filesystem. 而且我需要做到这一点,而不依赖于文件系统中的任何文件和外部JVM密钥/信任存储。

07/05/2018: Follow up to insightful suggestion from GPI I manually built the SSLContext : 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);

But when I use the resulting SSLSocketFactory in the HTTPS connection, I get the following error: 但是,当我在HTTPS连接中使用生成的SSLSocketFactory时,出现以下错误:

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

The KeyVaultClient (Java client library from Azure) provides me with a mechanism to obtain an X509Certificate object. KeyVaultClient(Azure的Java客户端库)为我提供了一种获取X509Certificate对象的机制。 Is it possible to initiate a KeyStore with a X509Certificate object, instead of from a PFX file? 是否可以使用X509Certificate对象而不是PFX文件来启动KeyStore?

Yes it is. 是的。 The steps are 步骤是

1) Load the Azure certificate in a Cert object (probably a X509Certificate ) 1)在Cert对象(可能是X509Certificate )中加载Azure证书
2) Create a new KeyStore instance (whatever the format, JKS or PKCS12 ) 2)创建一个新的KeyStore实例(无论是JKS还是PKCS12格式)
3) Init this new KeyStore by calling load with a null input stream , this will make a new, empty store. 3)通过使用空输入流调用load初始化此新KeyStore ,这将创建一个新的空存储。
4) Manually add the Azure certificate as a trusted entry in the KeyStore with a call to setCertificateEntry 4)通过调用setCertificateEntry手动将Azure证书添加为KeyStore中的受信任条目
5) Use this keystore as the base of your TrustManagerFactory 5)使用此密钥库作为TrustManagerFactory的基础

YES you can create a KeyStore from a cert BUT NO you cannot use it for client auth aka mutual auth. 是的,您可以从证书创建KeyStore不能将其用于客户端身份验证或相互身份验证。

Java uses the KeyStore class and related files to store (in general) three different though related kinds of things as detailed in Javadoc for the class . Java使用KeyStore类和相关文件来存储(通常)三种不同但相关的事物, 如Javadoc中对该类所详述 To authenticate yourself, you must have a PrivateKeyEntry which contains, as its name might suggest, a private key, PLUS at least one certificate and usually a chain of multiple certificates. 要对自己进行身份验证,您必须具有一个PrivateKeyEntry,如其名称所暗示的那样,其中包含一个私钥,再加上至少一个证书,通常还包含多个证书链。 A TrustedCertEntry is used to authenticate other parties, and in particular the other endpoint (peer) of an SSL/TLS connection; TrustedCertEntry用于验证其他方,尤其是SSL / TLS连接的其他端点(对等方)。 when you are the SSL/TLS client, as here, a TrustedCertEntry is used to authenticate the server by validating the server's cert. 当您是SSL / TLS客户端时,如此处所示,TrustedCertEntry用于通过验证服务器的证书来对服务器进行身份验证。 (The third possibility, SecretKeyEntry, is not used for SSL/TLS, and not even supported by PKCS12 keystore type as implemented by Java and commonly used.) (第三个可能性,SecretKeyEntry,不用于SSL / TLS,甚至不被Java实现且常用的PKCS12密钥库类型所支持。)

With an X509Certificate object, you can create a TrustedCertEntry, and the code you got from GPI does so. 使用X509Certificate对象,您可以创建TrustedCertEntry,然后从GPI获取的代码即可。 A TrustedCertEntry (in a KeyStore ) is only usable to authenticate the other party, in this situation the server. TrustedCertEntry(位于KeyStore )仅可用于验证另一方,在这种情况下为服务器。 To authenticate yourself, the client, to the server, you need not simply a certificate but a private key and certificate chain, packaged as a PrivateKeyEntry, from which you create a KeyManager put in the SSLContext and used by JSSE, as per your first code. 为了验证自己的身份,客户端, 服务器 ,您需要不仅仅是一个证书,但私钥和证书链,包装成以PrivateKeyEntry,从中你创建KeyManager投放SSLContext和使用JSSE,按你的第一个代码。

AFAICS Azure vault represents keys as com.microsoft.azure.keyvault.webkey.JSONWebKey , which appears to be limited to RSA or AES. AFAICS Azure保管库将密钥表示为com.microsoft.azure.keyvault.webkey.JSONWebKey ,它似乎仅限于RSA或AES。 If RSA, there are two toRSA methods (overloads) that from the description should produce a KeyPair, which I presume means java.security.KeyPair , containing the private key you need, unless there are limitations not stated where I looked. 如果是RSA,则从描述中有两种toRSA方法(重载)应产生一个KeyPair,我认为这意味着java.security.KeyPair ,其中包含您需要的私钥,除非在我看过的地方没有限制。 I don't see any way to get a certificate chain directly, but it appears certificate entries have issuer links, which should be sufficient for you to build the chain, although I'm not in a position to test/verify this myself. 我没有找到直接获取证书链的任何方法,但是看来证书条目具有颁发者链接,这对您建立证书链是足够的,尽管我自己无法对此进行测试/验证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM