PAYATHON 2026

Azure Key Vault 签署 JAR 为何报 InvalidKeyException?

数据小陈

先在原命令中显式加入 -sigalg SHA256withRSA,检查默认签名算法是否与 Azure JCA Provider 的支持范围不匹配。现有日志可以将排查重点放在签名算法和 Provider 上,但由于缺少实际 JDK 版本及完整异常栈,还无法确认唯一根因。

为什么读取了密钥,签名仍然失败?

原命令使用 azure-security-keyvault-jca-2.8.1.jar,密钥类型为 RSA-HSM,长度为 4096 位,没有指定 -sigalg。日志最后报错:

java.security.InvalidKeyException: No installed provider supports this key: com.azure.security.keyvault.jca.implementation.KeyVaultPrivateKey

KeyVaultPrivateKey 是 Azure Provider 对远程私钥的表示。读取这个对象和完成签名是不同的操作,HSM 私钥不会因此被下载到本地。Azure 文档也明确说明,HSM 密钥不可导出。Azure Key Vault 证书与私钥说明

这条异常表示,在当前请求的签名算法下,JCA 没有找到能够使用该私钥对象完成初始化的实现。本地存在某个算法的实现,也不代表它能处理 Azure 的远程密钥。

Getting key、Getting certificate 等 INFO 日志表明程序正在执行相应操作,无法单凭这些文字确认所有读取请求和签名权限均已通过。日志中的 TSA 地址也不能证明已取得时间戳。

为什么优先检查 SHA384withRSA?

jarsigner 未指定 -sigalg 时,会根据密钥类型、长度和 JDK 的默认规则选择算法。例如,JDK 24 对 4096 位 RSA 密钥默认选择 SHA384withRSA,也允许通过 -sigalg 覆盖。jarsigner 算法说明

查到的 Azure Provider 主分支源码注册了 RSA SHA-256、RSA SHA-512 和 RSASSA-PSS 等签名实现,但没有注册对应的 RSA SHA-384 实现。RSA SHA-256 实现使用 SHA256withRSA。Provider 注册源码、RSA SHA-256 实现

一种与现象吻合的排查假设是:默认选择了 SHA384withRSA,但 Azure Provider 没有提供对应实现。这里引用的是主分支源码,实际使用的 2.8.1 JAR 仍需单独检查,不能直接将两者视为相同。

如何修改签名命令?

先加入下面两个选项,其余配置保持一致:

-sigalg SHA256withRSA -digestalg SHA-256

其中,-sigalg 决定私钥签名算法,-digestalg 决定 JAR 条目的摘要算法。解决上述算法匹配问题需要指定 -sigalg,只修改 -digestalg 不够。jarsigner 参数说明

Windows CMD 示例:

jarsigner -keystore NONE -storetype AzureKeyVault ^
  -verbose -storepass "" ^
  -sigalg SHA256withRSA -digestalg SHA-256 ^
  -tsa http://timestamp.globalsign.com/tsa/r6advanced1 ^
  -providerName AzureKeyVault ^
  -providerClass com.azure.security.keyvault.jca.KeyVaultJcaProvider ^
  -J--module-path="C:\Java_Jar_Signing_26\azure-security-keyvault-jca-2.8.1.jar" ^
  -J--add-modules="com.azure.security.keyvault.jca" ^
  -J"-Dazure.keyvault.uri=https://my.vault.azure.net/" ^
  -J"-Dazure.keyvault.tenant-id=<TENANTID>" ^
  -J"-Dazure.keyvault.client-id=<CLIENTID>" ^
  -J"-Dazure.keyvault.client-secret=<SECRET>" ^
  -signedjar "signed.jar" "unsigned.jar" "mycertalias"

替换所有占位符。示例将原命令中的 <<CLIENTID>> 统一写为 <CLIENTID>;实际参数必须是应用的 client ID。^ 是 CMD 的续行符,后面不要留空格。

这条命令用于验证算法匹配的假设,尚未在你的环境中验证。

指定算法后仍然失败,如何继续定位?

确认实际运行的 JDK

在 CMD 中检查:

where java
where jarsigner
java -version
jarsigner -J-version

重点确认 jarsigner 实际使用哪个 JDK。目录名 Java_Jar_Signing_26 无法证明 JDK 版本。

查看实际 JAR 注册的签名算法

如果该 JDK 提供 JShell,可以加载同一份 JAR:

jshell --module-path "C:\Java_Jar_Signing_26\azure-security-keyvault-jca-2.8.1.jar" --add-modules com.azure.security.keyvault.jca

然后执行:

import java.security.*;
import com.azure.security.keyvault.jca.KeyVaultJcaProvider;

Provider p = new KeyVaultJcaProvider();
p.getServices().stream()
    .filter(s -> "Signature".equals(s.getType()))
    .map(Provider.Service::getAlgorithm)
    .sorted()
    .forEach(System.out::println);

System.out.println(p.getService("Signature", "SHA256withRSA"));
System.out.println(p.getService("Signature", "SHA384withRSA"));

getService 返回 null,表示这份 Provider 没有注册对应名称的服务。如果存在 SHA256withRSA 而缺少 SHA384withRSA,就支持前面的算法匹配判断。这个检查只查看服务注册情况,不验证 Azure 身份认证、远程签名或权限。

获取完整异常栈

在签名命令中追加:

-debug -J-Djava.security.debug=provider

检查实际选中的算法、Provider 和嵌套异常。如果已经进入 Azure 签名请求并出现 HTTP 403,再检查该身份对目标 key 的签名权限、密钥状态和版本对应关系。

原命令已经通过 -providerClass 加载 Provider。当前错误不足以说明必须调整全局 Provider 顺序;缺少某种算法实现时,提高优先级也无法补出该实现。

证书格式和中间证书是否需要调整?

问题中的 Content Type: PKCS11 需要核实。PKCS#11 是密码设备接口规范,Azure Key Vault 证书内容类型通常讨论的是 PKCS#12/PFX 或 PEM;它与 RSA-HSM 密钥类型属于不同配置概念。Azure JCA 支持说明

仅凭这一处记录无法断言证书必须重签。把证书改为非 PEM,也不会自动补齐 Provider 的签名算法实现。

intermediate1.cer 和 intermediate2.cer 主要用于构建证书链。若签名完成后出现 PKIX path building failed,再检查链是否完整,以及验证环境是否信任根证书。-certchain 用于补充签名所携带的证书链,不能解决 Provider 无法使用 KeyVaultPrivateKey 的问题。jarsigner 的 -certchain 说明

签名成功后运行:

jarsigner -verify -verbose -certs -strict signed.jar

检查签名算法、签名条目、证书链和时间戳的验证结果。若显式指定 SHA256withRSA 后成功,而默认命令失败,再结合实际 JDK 默认算法和 Provider 服务列表,就能进一步确认是否由算法不匹配引起。

备注:内容仅供参考。