Java Spring Boot下如何从Azure Key Vault提取RSA私钥?
解决方案:从Azure Key Vault获取RSA私钥并签名JWT
你的问题核心在于:当Azure Key Vault中的RSA密钥未设置为可导出(exportable=true),且应用不在可信执行环境(TEE)中时,无法直接获取私钥的明文材料——这是Azure KV的安全设计,避免私钥泄露。下面分两种场景给出可行方案:
场景1:密钥允许导出(架构允许私钥导出)
如果你的架构允许私钥导出,需要先确保密钥创建时开启了可导出选项:
- 若通过Azure Portal创建密钥:在“创建密钥”页面,“可导出”选择“是”。
- 若通过CLI/SDK创建:指定
--exportable true(CLI)或在CreateKeyOptions中设置setExportable(true)。
修正后的代码如下,确保能正确获取可导出的私钥:
@Bean public com.auth0.jwt.algorithms.Algorithm rsaAlgorithm(KeyClient azureKeyClient) { KeyVaultKey keyVaultKey = azureKeyClient.getKey(privateKey); JsonWebKey jsonWebKey = keyVaultKey.getKey(); // 传入true强制获取私钥,前提是密钥已设置exportable=true KeyPair rsaPair = jsonWebKey.toRsa(true); RSAPublicKey publicKey = (RSAPublicKey) rsaPair.getPublic(); RSAPrivateKey privateKeyRsa = (RSAPrivateKey) rsaPair.getPrivate(); if (privateKeyRsa == null) { throw new IllegalStateException("无法获取私钥,请检查密钥是否设置为可导出(exportable=true)"); } return Algorithm.RSA256(publicKey, privateKeyRsa); }
场景2:密钥不可导出(符合架构要求,私钥不离开KV)
这是更安全的方案,不需要本地持有私钥,直接调用Azure Key Vault的签名服务完成JWT签名:
实现步骤
- 确保服务主体拥有
Key Vault Crypto User角色权限,允许执行签名操作。 - 使用
KeyClient调用KV的签名接口,传入JWT的待签名字符串。 - 用KV返回的签名结果组装完整JWT。
代码示例
@Service public class JwtSignerService { private final KeyClient keyClient; private final String keyName; public JwtSignerService(KeyClient keyClient, @Value("${azure.keyvault.key.name}") String keyName) { this.keyClient = keyClient; this.keyName = keyName; } public String generateSignedJwt(Map<String, Object> customClaims, String issuer, String audience) throws JsonProcessingException { // 1. 构建JWT Header Map<String, Object> header = new HashMap<>(); header.put("alg", "RS256"); header.put("kid", keyName); // 使用KV密钥名称作为密钥标识 // 2. 构建JWT Payload long currentTime = System.currentTimeMillis() / 1000; Map<String, Object> payload = new HashMap<>(customClaims); payload.put("iss", issuer); payload.put("aud", audience); payload.put("iat", currentTime); payload.put("exp", currentTime + 3600); // 1小时过期 // 3. 序列化Header和Payload为Base64URL格式 ObjectMapper objectMapper = new ObjectMapper(); String base64Header = Base64.getUrlEncoder().withoutPadding().encodeToString(objectMapper.writeValueAsBytes(header)); String base64Payload = Base64.getUrlEncoder().withoutPadding().encodeToString(objectMapper.writeValueAsBytes(payload)); String signingInput = base64Header + "." + base64Payload; // 4. 调用Azure Key Vault执行签名 SignResult signResult = keyClient.sign( keyName, SignatureAlgorithm.RS256, signingInput.getBytes(StandardCharsets.UTF_8) ); // 5. 组装完整JWT String base64Signature = Base64.getUrlEncoder().withoutPadding().encodeToString(signResult.getSignature()); return signingInput + "." + base64Signature; } }
为什么你之前的代码会失败?
- 当密钥未设置
exportable=true且不在TEE环境时,Azure KV返回的JsonWebKey不会包含私钥的核心参数(如d、p、q等),因此toRsa()返回的私钥为null,toRsa(true)会因缺少必要参数抛出NPE。 - TEE环境(如Azure机密计算虚拟机)是特殊场景,需要密钥在TEE中创建且应用运行在TEE内,才能获取私钥的内存实例(不会导出明文),但绝大多数业务场景不需要这种配置。
内容的提问来源于stack exchange,提问作者Wrapper
相关产品推荐
相关产品推荐

