使用 Java SDK 从 Azure KeyVault 下载 .PFX 证书时遇到问题。
下载后,在将证书上传到 Azure KeyVault 中的证书边栏选项卡之前,该证书无法像原始的十六进制编码 .PFX 文件一样打开。注意:我尝试了带密码的 PFX 文件和不带密码的 PFX 文件。
下载后,生成的 HEX 文件有 373 行和 14,919 个字符,且无法打开。上传之前,.pfx 十六进制文件有 374 行和 14,947 个字符。
有人知道我做错了什么吗?下载后,使用原始密码或空白密码,我收到此错误:
test.pfx is not a KeyStore of any of the following recognized types: JCE, JCEKS, PKCS#12, UBER or BCFKS
(我使用 Keystore Explorer 进行测试)
public static void main(String[] args) throws Exception {
System.setProperty("AZURE_CLIENT_ID", "...");
System.setProperty("AZURE_CLIENT_SECRET", "...");
System.setProperty("AZURE_TENANT_ID", "..."");
final DefaultAzureCredential azureCredential = new DefaultAzureCredentialBuilder()
.build();
CertificateClient certificateClient = new CertificateClientBuilder()
.vaultUrl("https://djangofan-kv-test.vault.azure.net")
.credential(azureCredential)
.buildClient();
String certificateName = "test";
KeyVaultCertificateWithPolicy certificate = certificateClient.getCertificate(certificateName);
CertificateKeyType keyType = certificate.getPolicy().getKeyType();
System.out.println("\nKey type: " + keyType);
String secretIdAndUrl = certificate.getKeyId();
System.out.println("\nSecret ID: " + secretIdAndUrl);
String secretValue = secretClient.getSecret(certificateName).getValue();
System.out.println("\nSecret value:\n" + secretValue);
Base64.Decoder decoder = Base64.getDecoder();
byte[] decodedBytes = decoder.decode(secretValue);
System.out.println("\nSecret value decoded:\n" + Arrays.toString(decodedBytes));
String hexData = bytesToHex(decodedBytes);
System.out.println("\nSecret value HEX dump:\n" + hexData);
try (BufferedWriter writer = new BufferedWriter(new FileWriter("test.pfx", StandardCharsets.UTF_8))) {
writer.write(hexData);
}
System.exit(0);
}
注意:从 KeyVault GUI 手动下载 .pfx 证书效果很好。
我也用SecretsClient尝试过这种方式:
SecretClient secretClient = new SecretClientBuilder()
.vaultUrl("https://djangofan-kv-test.vault.azure.net")
.credential(azureCredential)
.buildClient();
String secretName = "secretnopass";
KeyVaultSecret secret = secretClient.getSecret(secretName);
String secretValue = secret.getValue();
System.out.println("\nSecret value:\n" + secretValue);
String hexData = bytesToHex(secretValue.getBytes(StandardCharsets.UTF_8));
System.out.println("\nSecret value HEX dump:\n" + hexData);
Maven 依赖项:
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-security-keyvault-certificates</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-security-keyvault-secrets</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.3.5</version>
</dependency>
注意:我将尝试这种替代方法,但我不确定为什么直接下载到 keyVault 不起作用:https://youtu.be/26WGUCQ_LAI?t=618
PFX - 也称为 PKCS #12 - 是一种二进制编码。您不应该将
byte[]
编码为十六进制字符串以写入文件。即使原始证书导入是 PEM,您也不会进行任何解码 - 只需保存文件,该文件已标记为 Base64 编码数据。
请参阅我编写的 .NET SDK 实现。你在正确的道路上找到了秘密;不过,您应该使用附加到证书的秘密 ID:它是相同的 ID,这只是一个可能更改的实现细节。仅当内容类型为“application/x-pkcs12”时才进行 base64 解码。
这是解决方案,尽管它并不理想,因为我的意思是我必须下载密钥库部分,然后在代码中重新组装它们以生成有效的密钥库。不幸的是,我找不到方法只将密钥库下载到我的函数中。也许这是因为该函数必须“在内存中执行操作”并且它没有文件存储。
为此,您需要将 3 个公共证书 + 1 个私钥上传到 KeyVault 实例的 Secrets 边栏选项卡中。
您可以通过引用传递方式将 KeyStore 对象传递到此方法中以重新构建密钥库:
private void addNonprodKeysToKeystore(KeyStore keyStore, String nonprodCertificateAlias, SecretClient secretClient) throws Exception {
// get cert chain root cert
KeyVaultSecret rootCertSecret = secretClient.getSecret(DIGICERT_ROOT_AZURE_PEM);
String rootCertPEM = rootCertSecret.getValue();
// get cert chain intermediate cert
KeyVaultSecret intermediateCertSecret = secretClient.getSecret(DIGICERT_INTERMEDIATE_AZURE_PEM);
String intermediateCertPEM = intermediateCertSecret.getValue();
// get cert chain head cert
KeyVaultSecret headCertSecret = secretClient.getSecret(HEAD_CERT_AZURE_PEM);
String headCertPEM = headCertSecret.getValue();
// build cert chain for keystore
Certificate[] certificateChain = buildCertificateChain(headCertPEM, intermediateCertPEM, rootCertPEM);
KeyVaultSecret privateKeySecret = secretClient.getSecret(PRIVATE_KEY_AZURE_PEM);
String encodedPrivateKey = privateKeySecret.getValue();
byte[] decodedPrivateKey = Base64.getDecoder().decode(encodedPrivateKey);
String decodedPkString = new String(decodedPrivateKey, StandardCharsets.UTF_8);
String strippedPkPEM = stripPEMHeadersFromPrivateKey(decodedPkString);
byte[] decodedPkBody = Base64.getDecoder().decode(strippedPkPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decodedPkBody);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
keyStore.setKeyEntry(nonprodCertificateAlias, privateKey, this.resolveKeystorePassword(), certificateChain);
}