无效密钥异常:无效的密钥格式 java

11

我正在尝试从Vault中检索公钥,它被存储为密钥。

我正在尝试将检索到的字符串转换为PUBLIC KEY以验证签名。

示例公钥字符串如下所示

-----BEGIN PUBLIC KEY----- MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBWeqVZ8Ub/o4VQ8nnm888B /Ydqv2IN5bObxupZ7njMKuT/WPgwlK8+Wc0Xjhy82E51XW6E4/0um8sIQ1cxvoSO QsrfkRagD+O9OrjQbb2TqrilDDhFx9EGjXuZpR3brDUufCG6SkypqiKSaMuoVoax c82TZ1uAIp5OSroWt1IdUkvam24X/7zDIf1l8XWCmbfCDrBb73hBYA4MgTjsSckC 5nz+GLcWTfz0wze4lwHCi1KYFv+1+WcYHWPLbqLtc8nzVqkuP5Ne/9HAFkaEAIw5 fKLccksaT/TLyIcrALcfuABlgX1yeBulVcbTAp+WiYRvo9+FKK23pbwkh+uy0tq1 AgMBAAE= -----END PUBLIC KEY-----

我已将其添加到我的密钥值中,并且没有格式。

但是,使用以下代码时,我遇到了错误InvalidKeyException:INVALID KEY FORMAT在此行中:

 PublicKey publicKey = fact.generatePublic(pubKeySpec);

这里是代码:
            String publicKeyAsString = secretClient.getSecret("key-name").getValue();
    
            byte[] keyContentAsBytes = publicKeyAsString.getBytes();
    
            KeyFactory fact = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyContentAsBytes);
            PublicKey publicKey = fact.generatePublic(pubKeySpec);

使用堆栈跟踪进行编辑:

Caused by: java.security.InvalidKeyException: invalid key format
    at sun.security.x509.X509Key.decode(X509Key.java:386) ~[?:?]
    at sun.security.x509.X509Key.decode(X509Key.java:401) ~[?:?]
    at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:122) ~[?:?]
    at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:330) ~[?:?]
    at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:235) ~[?:?]

编辑:测试用公钥:

-----BEGIN PUBLIC KEY----- MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBWeqVZ8Ub/o4VQ8nnm888B /Ydqv2IN5bObxupZ7njMKuT/WPgwlK8+Wc0Xjhy82E51XW6E4/0um8sIQ1cxvoSO QsrfkRagD+O9OrjQbb2TqrilDDhFx9EGjXuZpR3brDUufCG6SkypqiKSaMuoVoax c82TZ1uAIp5OSroWt1IdUkvam24X/7zDIf1l8XWCmbfCDrBb73hBYA4MgTjsSckC 5nz+GLcWTfz0wze4lwHCi1KYFv+1+WcYHWPLbqLtc8nzVqkuP5Ne/9HAFkaEAIw5 fKLccksaT/TLyIcrALcfuABlgX1yeBulVcbTAp+WiYRvo9+FKK23pbwkh+uy0tq1 AgMBAAE= -----END PUBLIC KEY-----

PublicKeyAsString 的值如下所示:

-----BEGIN PUBLIC KEY----- MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBWeqVZ8Ub/o4VQ8nnm888B /Ydqv2IN5bObxupZ7njMKuT/WPgwlK8+Wc0Xjhy82E51XW6E4/0um8sIQ1cxvoSO QsrfkRagD+O9OrjQbb2TqrilDDhFx9EGjXuZpR3brDUufCG6SkypqiKSaMuoVoax c82TZ1uAIp5OSroWt1IdUkvam24X/7zDIf1l8XWCmbfCDrBb73hBYA4MgTjsSckC 5nz+GLcWTfz0wze4lwHCi1KYFv+1+WcYHWPLbqLtc8nzVqkuP5Ne/9HAFkaEAIw5 fKLccksaT/TLyIcrALcfuABlgX1yeBulVcbTAp+WiYRvo9+FKK23pbwkh+uy0tq1 AgMBAAE= -----END PUBLIC KEY-----

1个回答

10

起初我以为你的问题与Azure KeyVault Secret API返回的信息类型有关,通常是用base64编码。

在这种情况下,你需要在尝试执行实际密钥材料处理之前进行适当的base64解码:

String publicKeyAsString = secretClient.getSecret("key-name").getValue();
    
byte[] keyContentAsBytes = Base64.getDecoder().decode(publicKeyAsString);

但是看起来Azure客户端提供的信息是纯文本。

在这种情况下,密钥是一个PEM编码的公钥。

标准的KeyFactory将不能直接处理返回的信息,但稍加修改即可。例如,请尝试以下操作:

// Actually
// String publicKeyAsString = secretClient.getSecret("key-name").getValue();

String publicKeyAsString =
    "-----BEGIN PUBLIC KEY-----\n" +
    "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBWeqVZ8Ub/o4VQ8nnm888B\n" +
    "/Ydqv2IN5bObxupZ7njMKuT/WPgwlK8+Wc0Xjhy82E51XW6E4/0um8sIQ1cxvoSO\n" +
    "QsrfkRagD+O9OrjQbb2TqrilDDhFx9EGjXuZpR3brDUufCG6SkypqiKSaMuoVoax\n" +
    "c82TZ1uAIp5OSroWt1IdUkvam24X/7zDIf1l8XWCmbfCDrBb73hBYA4MgTjsSckC\n" +
    "5nz+GLcWTfz0wze4lwHCi1KYFv+1+WcYHWPLbqLtc8nzVqkuP5Ne/9HAFkaEAIw5\n" +
    "fKLccksaT/TLyIcrALcfuABlgX1yeBulVcbTAp+WiYRvo9+FKK23pbwkh+uy0tq1\n" +
    "AgMBAAE=\n" +
    "-----END PUBLIC KEY-----";

String publicKeyPem = publicKeyAsString
    .replace("-----BEGIN PUBLIC KEY-----", "")
    .replaceAll("\\n", "")
    .replace("-----END PUBLIC KEY-----", "");

byte[] keyContentAsBytes =  Base64.getDecoder().decode(publicKeyPem);

try {
  KeyFactory fact = KeyFactory.getInstance("RSA");
  X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyContentAsBytes);
  PublicKey publicKey = fact.generatePublic(pubKeySpec);
  System.out.println(publicKey);
}catch (Throwable t) {
  t.printStackTrace();
}

更好的方法是使用BouncyCastle的PemReader来完成此任务:

try (
    Reader reader = new StringReader(publicKeyAsString);
    PemReader pemReader = new PemReader(reader)
) {
  KeyFactory fact = KeyFactory.getInstance("RSA");
  PemObject pemObject = pemReader.readPemObject();
  byte[] keyContentAsBytesFromBC = pemObject.getContent();
  X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyContentAsBytesFromBC);
  PublicKey publicKey = fact.generatePublic(pubKeySpec);
  System.out.println(publicKey);
} catch (Throwable t) {
  t.printStackTrace();
}

请注意,我在变量publicKeyAsString的定义中包含了多个换行符,这是必要的,以便程序能够处理信息。

请验证Azure是否以类似的方式返回pem编码的密钥:如果不是,则很可能是问题的原因。

此外,请注意Azure KeyVault返回您上传的密钥,也许问题就出在那里。请尝试以下操作:

PublicKey publicKey = ...
StringWriter writer = new StringWriter();
PemWriter pemWriter = new PemWriter(writer);
pemWriter.writeObject(
  new PemObject("PUBLIC KEY", publicKey.getEncoded())
);
pemWriter.flush();
pemWriter.close();
String publicKeyAsString = writer.toString();
// Upload to Azure KeyVault

1
非常感谢。请查看我的更新。希望它有所帮助。 - jccampanero
1
@Rohi,请查看答案的更新内容,我忘记了你当然也需要解码----BEGIN PUBLIC KEY---------END PUBLIC KEY-----边界内的文本:byte[] keyContentAsBytes = Base64.getDecoder().decode(publicKeyPem); - jccampanero
1
我明白了。请问,能否在注释中包含引起问题的文本片段以及从secretClient.getSecret("key-name").getValue()获取的值? - jccampanero
1
@Rohi,请问您能否尝试使用BouncyCastle方法,但不进行解码吗?我的意思是,将行byte[] pemContentAsBytes = Base64.getDecoder().decode(publicKeyAsString);替换为byte[] pemContentAsBytes = publicKeyAsString.getBytes();。实际上,您可以在不解码的情况下获得正确的密钥值。 - jccampanero
1
非常感谢,我很欣赏。是的,请不要在答案中发布实际有价值的内容。我现在必须离开,但稍后会查看示例。 - jccampanero
显示剩余35条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接