【问题标题】:How to load RSA public key from String for signature verification in Java?如何从 String 加载 RSA 公钥以在 Java 中进行签名验证?
【发布时间】:2019-07-24 05:58:07
【问题描述】:

我有以下公钥,它以文本形式存储在数据库 (PostgresSQL) 中。它是一个字符串,在 java 中:

-----BEGIN RSA PUBLIC KEY-----     
MIICCgKCAgEA1ht0OqZpP7d/05373OE7pB7yCVGNGzkUEuCneyfOzps6iA03NbvI
1ZL0Jpp/N3AW73lGdhaoa3X3JE4GsI/bsToVLQwTKmIOC4yjTvBctmFEoyhhTfxW
s1UHZKl4XZ/7THbRlKHhRaTKyfDAbikkMAxNT/qutLAPjnN1qOwjb1oRq52NP6FJ
KWTTikz4UeOHroX+Xthn2fJSJDlQ4YMdBbgrZVx5JcHKNuPTKRf5gI8QQKMSA9Q9
QJRE5OGp7b6dG14ZmOUnUxb00Mp20LgcaGPcuWU+oFsbQaF6W4G4bdkSZRJJXhSg
d4Q7mahpar94/gnztJmth0GzqTWUYyZIWNqIFoMwuOgeaiDV43zb3uLsRVpRKYYy
esmzcOy/jTScVLRCD8QRyu9B2wgCkNAVztQOXPCOOa4O1LlVQWaecIs4WPhOqDhi
KTBhyVkpC1TrrBkp+QMqMqWll1OyVb6k/7uV0qE/i6rHJtjo5v9bcIgYzswyx9CD
9PKl2Q0L0Jg7TMG+yLDIrLfGeuSeEc4XYJzN7bJcCeiizzu5iU9dQUkrncOrq9jn
Ub2pM/+A+JqIsoPK3IY/pJKqH4JYpGKhO1iPQF6iXIZT1r3ZgJUSQtzSeyYqhkla
2uR2BsbPbDqebCuXm3lAsY5w+dujijcn96PKwYha1LsK5sACHuJ79AMCAwEAAQ==
-----END RSA PUBLIC KEY-----

我不知道这个密钥是如何生成的,对不起。我被告知要使用这个密钥并验证另一个我称之为“对象”的字符串的签名。有人告诉我,我必须用来验证“对象”的算法是 SHA256withRSA。

所以,我写了下面的java方法来读取密钥

private PublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
    publicKey = publicKey.replaceAll("\\n", "");
    publicKey = publicKey.replace("-----BEGIN RSA PUBLIC KEY-----", "");
    publicKey = publicKey.replace("-----END RSA PUBLIC KEY-----", "");
    publicKey = publicKey.trim();
    byte[] keyDecoded = Base64.getDecoder().decode(publicKey.getBytes());
    X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(keyDecoded);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PublicKey pubKey = kf.generatePublic(publicSpec);
    return pubKey;
}

关键是我得到了以下异常:

java.security.InvalidKeyException: IOException: algid parse error, not 一个序列

我已经在 stackoverflow 中阅读了大量的问题。其他用户编写的代码与我的非常相似(有时相同)。所以我绝对不明白为什么它对我不起作用。其他开发人员(同事)在 php 中做同样的事情并且效果很好,所以我会放弃错误公钥的假设。也许我没有清楚地理解这个过程?请问你有什么线索吗?

按照here 的建议,我也尝试使用 BouncyCastle 库来解决这个问题,但我遇到了同样的异常。以下是我写的代码:

private static PublicKey getPublicKey(String publicKey)
        throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
    Security.addProvider(new BouncyCastleProvider());
    PemReader pp = new PemReader(new StringReader(publicKey));
    PemObject pem = pp.readPemObject();
    byte[] content = pem.getContent();
    pp.close();

    X509EncodedKeySpec spec = new X509EncodedKeySpec(content);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePublic(spec);
}

【问题讨论】:

  • 他为什么要使用私钥而不是公钥来验证签名?
  • 我正在阅读您引用的问题。我会修复和整合。请给我几分钟。
  • 公钥似乎是一个 RSA4096 位密钥(一个包含模数和指数的 ASN.1 序列)。您使用最新版本的 Java 吗?否则,您可能会受到旧的加密限制策略的限制,该策略禁止 RSA 密钥大于 2048 位。
  • 我使用的是 Java 1.8

标签: java rsa public-key pem


【解决方案1】:

您无法使用 X509EncodedKeySpec 加载该密钥。根据JavaDoc documentation,它需要以下格式:

SubjectPublicKeyInfo ::= SEQUENCE {
   algorithm AlgorithmIdentifier,
   subjectPublicKey BIT STRING }

相反,您的密钥看起来不同。我使用了您帖子中的数据,将其转换为十六进制数据并发布到在线ASN.1 decoder

输出是这样的:

SEQUENCE (2 elem)
  INTEGER (4096 bit) 873481340827968071893572683200799871431146795599597693981565010037737…
  INTEGER 65537

您可能认识到您的密钥不包含AlgorithmIdentifier,因此无法使用X509EncodedKeySpec 加载它。

我的建议是使用 BouncyCastle 库,它的 PEMParser 类用于加载此密钥:

File pemFile = new File("test.pem");
try (PEMParser pp = new PEMParser(new InputStreamReader(new FileInputStream(pemFile)))) {
    SubjectPublicKeyInfo subjPubKeyInfo = (SubjectPublicKeyInfo) pp.readObject();
    RSAKeyParameters rsa = (RSAKeyParameters) PublicKeyFactory.createKey(subjPubKeyInfo);

    RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsa.getModulus(), rsa.getExponent());
    KeyFactory kf = KeyFactory.getInstance("RSA");
    java.security.PublicKey publicKey = kf.generatePublic(rsaSpec);
    System.out.println(publicKey);
}

或者您通过 openssl 手动将密钥转换为 PKCS#8 格式。

【讨论】:

  • 非常感谢,@罗伯特。如果我尝试使用该密钥签名,为什么现在我得到“签名长度不正确:得到 9575 但预期为 5122”?
  • 签名签名 = Signature.getInstance("SHA256withRSA");签名.initVerify(getPublicKey(publicKey));签名.verify(object.getBytes());
  • @Peter 您忘记将要验证的数据传递给您的Signature 实例:stackoverflow.com/q/46225417/150978
  • @Peter 我解决了你原来的问题,现在已经加载了密钥。请停止修改问题 - 我不喜欢“移动目标”。在 Stackoverflow 中搜索类似问题,如果您没有找到匹配的问题,请为新问题打开一个新问题。
  • 这个Q是一个公钥; PKCS8 用于私钥。 OpenSSL 1.0.0 以上(但直到 1.1.0 才记录)可以将此格式转换为 SPKI(Java 调用 X509Encoded 和 OpenSSL 调用 PUBKEY)openssl rsa -RSAPublicKey_in <in >out
猜你喜欢
  • 2023-04-06
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-28
  • 2010-11-06
  • 2016-04-17
  • 1970-01-01
  • 2013-08-17
相关资源
最近更新 更多