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 服务列表,就能进一步确认是否由算法不匹配引起。
备注:内容仅供参考。
