将证书和私钥加载到Java KeyStore中

时间:2020-07-02 16:55:32

标签: java certificate keystore x509 azure-keyvault

我正在尝试从Azure Key Vault中获取证书及其私钥,然后调用远程服务器并进行客户端证书身份验证。

第一部分效果很好(从Key Vault获取),但是我完全坚持将公共和私人资料导入KeyStore。

我尝试过

keyStore.load(publicKey, null);
keyStore.load(new ByteArrayInputStream(privateKey.getBytes()),
    "thePassphrase".toCharArray());

但这导致

java.io.IOException: DER input, Integer tag error
        at java.base/sun.security.util.DerInputStream.getInteger(DerInputStream.java:192)
        at java.base/sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1995)
        at java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:222)
        at java.base/java.security.KeyStore.load(KeyStore.java:1479)

这是减去我不知道如何实现的全部内容-

DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();

SecretClient secretClient = new SecretClientBuilder()
    .vaultUrl("https://<myvault>.vault.azure.net")
    .credential(credential)
    .buildClient();

CertificateClient certClient = new CertificateClientBuilder()
    .vaultUrl("https://<myvault>.vault.azure.net")
    .credential(credential)
    .buildClient();

// Get the public part of the cert
KeyVaultCertificateWithPolicy certificate = certClient.getCertificate("Joes-Crab-Shack");
byte[] publicKey = certificate.getCer();

// Get the private key
KeyVaultSecret secret = secretClient.getSecret(
    certificate.getName(),
    certificate.getProperties().getVersion());
String privateKey = secret.getValue();

// ***************
// How do i get the cert and its private key into KeyStore?
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// I've also tried "JKS" but that leads to
//    java.io.IOException: Invalid keystore format
keyStore.load(...)
// ***************

// Do client certificate authentication
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, null).build();

CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
response = httpClient.execute(new HttpGet("https://remote.that.asks.for.client.cert/"));

InputStream inputStream = response.getEntity().getContent();

body = IOUtils.readInputStreamToString(inputStream, Charsets.UTF_8);

如何将证书及其私钥放入KeyStore,以便可以在HTTP客户端中使用它?

3 个答案:

答案 0 :(得分:1)

有点晚了,但我想提出一个我在从 Azure Keyvault 检索证书然后将其放入 java Keystore 时练习过的解决方案。

我使用的依赖项如下。

com.azure:azure-security-keyvault-certificates:jar:4.1.3
com.azure:azure-identity:jar:1.0.4
com.azure:azure-security-keyvault-secrets:jar:4.1.1

然后,代码块在下面。

        char[] emptyPass = {};
        
        // Azure KeyVault Credentials
        ClientSecretCredential credential = new ClientSecretCredentialBuilder()
            .tenantId(tenantId)
            .clientId(clientId)
            .clientSecret(clientSecret)
            .build();

        CertificateClient certificateClient = new CertificateClientBuilder()
            .vaultUrl(vaultUri)
            .credential(credential)
            .buildClient();

        SecretClient secretClient = new SecretClientBuilder()
            .vaultUrl(vaultUri)
            .credential(credential)
            .buildClient();
        // Azure KeyVault Credentials

        // Retrieving certificate
        KeyVaultCertificateWithPolicy certificateWithPolicy = certificateClient.getCertificate(alias);
        KeyVaultSecret secret = secretClient.getSecret(alias, certificateWithPolicy.getProperties().getVersion());

        byte[] rawCertificate = certificateWithPolicy.getCer();
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        ByteArrayInputStream certificateStream = new ByteArrayInputStream(rawCertificate);
        Certificate certificate = cf.generateCertificate(certificateStream);

        // "certificateStream" should be closed
        // Retrieving certificate

        // Retrieving private key
        String base64PrivateKey = secret.getValue();
        byte[] rawPrivateKey = Base64.getDecoder().decode(base64PrivateKey);

        KeyStore rsaKeyGenerator = KeyStore.getInstance(KeyStore.getDefaultType());
        ByteArrayInputStream keyStream = new ByteArrayInputStream(rawPrivateKey);
        rsaKeyGenerator.load(keyStream, null);

        // "keyStream" should be closed as well.

        Key rsaPrivateKey = rsaKeyGenerator.getKey(rsaKeyGenerator.aliases().nextElement(), emptyPass);
        // Retrieving private key

        // Importing certificate and private key into the KeyStore
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);
        keyStore.setKeyEntry(alias, rsaPrivateKey, emptyPass, new Certificate[] {certificate});
        // Importing certificate and private key into the KeyStore

        SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
            new SSLContextBuilder()
                .loadTrustMaterial(null, new TrustAllStrategy())
                .loadKeyMaterial(keyStore, pass)
                .build(),
            NoopHostnameVerifier.INSTANCE);

        CloseableHttpClient httpClient = HttpClients.custom()
                                                    .setSSLSocketFactory(socketFactory)
                                                    .build();

        ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        RestTemplate restTemplate = new RestTemplate(requestFactory);

我希望有人在 Java 密钥库中寻找使用 Azure Keyvault 证书的解决方案,可以从中受益。

答案 1 :(得分:0)

在我看来,导入是一项“一次性工作”,不需要以编程方式解决。 我建议您(与@pedrofb一样)使用Keystore Explorer来完成这项工作-它在我的测试用例中非常有效:

这是我执行导入的步骤,所有文件都可以在我的GitHub-Repo(https://github.com/java-crypto/Stackoverflow/tree/master/Load_certificate_and_private_key_into_Java_KeyStore)中找到:

  1. 使用打开的ssl创建私钥+证书文件:

openssl req -x509 -days 365 -newkey rsa:2048 -keyout key.pem -out cert.pem

这将创建2个文件key.pem(加密的私钥)和cert.pem(带有公钥的证书)。我使用了一些示例数据,key.pem的密码是123456。

  1. https://keystore-explorer.org/downloads.html下载密钥库资源管理器

  2. 运行资源管理器并创建一个新的KeyStore

create new keystore

  1. 选择KeyStore类型= PKCS12

select Key Store type

  1. 使用“工具”-“导入密钥对”导入KeyPair

import Key Pair

  1. 选择密钥对类型作为PKCS#8

select Key Pair type

  1. 设置密码(123456)并选择密钥和证书文件,然后按“导入”

set the password

  1. 选择密钥的别名(默认为证书中给定的电子邮件

choose an alias

  1. 设置密钥对输入密码:kpe123456

set Key Pair Entry password

  1. 消息:密钥对导入成功

Message Key Pair Import successful

save your new keystore

  1. 使用“文件”-“另存为”保存新的密钥库

save your new Key Store

  1. 设置密钥存储区的密码:ks123456

Set a password for the Key Store

  1. 选择路径+文件名:keystore.p12

  2. 就绪-您的私人和证书将导入到新创建的密钥库keystore.p12

答案 2 :(得分:0)

当通过Google搜索并“ azure key Vault获取私钥”时,我发现了一个GitHub问题,该问题详细描述了 如何从Azure Key Vault检索私钥:

https://github.com/Azure/azure-sdk-for-js/issues/7647

向下滚动以回答https://github.com/Azure/azure-sdk-for-js/issues/7647#issuecomment-594935307,有此声明 来自开发人员之一:

如何获取私钥

知道私钥已存储在KeyVault机密中(包括公共证书),我们可以使用KeyVault-Secrets客户端检索它,如下所示:

// Using the same credential object we used before,
// and the same keyVaultUrl,
// let's create a SecretClient
const secretClient = new SecretClient(keyVaultUrl, credential);

// Assuming you've already created a KeyVault certificate,
// and that certificateName contains the name of your certificate
const certificateSecret = await secretClient.getSecret(certificateName);

// Here we can find both the private key and the public certificate, in PKCS 12 format:
const PKCS12Certificate = certificateSecret.value!;

// You can write this into a file:
fs.writeFileSync("myCertificate.p12", PKCS12Certificate);

请注意,默认情况下,证书的contentType为PKCS 12 。尽管指定证书的内容类型将使其以PEM格式获取私钥很简单,但让我们探索一下如何首先从PKCS 12证书中检索PEM私钥。

Using openssl, you can retrieve the public certificate in PEM format by using the following command:
openssl pkcs12 -in myCertificate.p12 -out myCertificate.crt.pem -clcerts -nokeys
You can also use openssl to retrieve the private key, as follows:
openssl pkcs12 -in myCertificate.p12 -out myCertificate.key.pem -nocerts -nodes

简而言之:您无需从单独的“文件”(私钥-PEM和证书-PEM)中“重建” PKCS12-密钥库,如示例所示,

相关问题