【问题标题】:How to read RSA public key from file/string in java/kotlin using BouncyCastle如何使用 BouncyCastle 从 java/kotlin 中的文件/字符串中读取 RSA 公钥
【发布时间】:2020-01-28 22:20:42
【问题描述】:

我正在尝试从 kotlin 解析 RSA 格式的公钥。我有一个字符串中的键,其内容如下:

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwAzOKC8d0o0dcv1KqILLehASGgOWyjlAc+adazix6ThhX7QeD3Qw
HzxPpbwsJrVPIEMEIN383awIqnCfIL+AbCQPL13XaUCCS74wC5a84X1r6hcI5XO1
9CPAn+jBKmTr4hPaHWKxuhfO3PcXxGfQdXyqNT96bCYnAYaeSECohFjqDbe+RFcL
1lIns2GtQPMh1/uDyhPA+8HSguREWn+Ac3I2c0wtrzZa6R4nruPgIi6XbWRqAskr
tzbO2Xy6O1DcERH9tg+es/pbrWHRHrwEmLXorj3iGqkJJBUmLeW6B5EjmIgiukdJ
dw7bLTNcwf2n0BLJy/bgnhcw4TMOzUadSQIDAQAB
-----END RSA PUBLIC KEY-----

我发现了很多代码示例来执行此操作,其中涉及使用 String.replace() 修剪 BEGIN 和 END,但这对我来说似乎很老套。 BouncyCastle code seems to handle this already 包括为它遇到的不同类型的“文件”创建解析器的能力。我正在尝试这个:

try {
    val parser = PEMParser(StringReader(publicKeyString))
    val pemObject = parser.readPemObject()
    val pemContent = pemObject.content
    val key = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(pemContent)
    serviceLogger.info("Key object: {}", key)
} catch (e: Exception) {
    serviceLogger.error("Could not generate key from keyspec", e)
}

我得到一个 pemContent(一个字节数组)没有问题,但是当我尝试将它实际解析为 RSAPublicKey 时,我得到了这个:

java.lang.IllegalArgumentException: failed to construct sequence from byte[]: DEF length 3 object truncated by 2

我无法弄清楚我是否正确调用 RSAPublicKey.getInstance() - 使用整个 PemObject 的内容 - 还是这个异常告诉我我的密钥有问题。

我在这方面能找到的例子已经很老了,而且 API 似乎已经发展到我不必手动分割字符串的地步了。

我怀疑这真的有助于解决问题,但我正在从 rsa 密钥对生成此文件:

func PublicKeyToPemBytes(prvkey *rsa.PrivateKey) ([]byte, error) {
    var pubkey *rsa.PublicKey
    pubkey = &prvkey.PublicKey

    pubkey_bytes := x509.MarshalPKCS1PublicKey(pubkey)
    if pubkey_bytes == nil {
        return nil, errors.New("Public key could not be serialized")
    }

    pubkey_pem := pem.EncodeToMemory(
        &pem.Block{
            Type:  "RSA PUBLIC KEY",
            Bytes: pubkey_bytes,
        },
    )

    return pubkey_pem, nil
}

go rsa.PublicKey 对象包含一个 N 和一个 E。它为我提供了上面列出的文件,base64 解码结果完全相同,长度为 270 字节。

【问题讨论】:

  • 您是否更改了数据并截断了它?它的一部分可能在一个有效的 PKCS1-RSAPublicKey 中,而不是偶然发生的,但其他部分是完全错误的。这种格式现在很少单独使用,我很惊讶 go 会这样做; Bouncy 支持它只是因为 OpenSSL 支持 - 而 OpenSSL 只是部分支持它,作为上个世纪的遗物。也就是说,如果您有有效数据,Bouncy 应该可以工作。 (也是 OpenSSL。)你能发布一个完整的密钥,甚至是一个测试密钥吗?记住公钥加密的要点是公钥不是秘密的,甚至可以被敌人知道。
  • 正确,公平点。我修改了原始帖子以包含完整的密钥。我的密钥生成肯定有问题 - 它正在生成 ASN.1 类型为 0x62 而不是 0x02 的整数。我解决了这个问题,所以上面的例子对于 dumpasn1 和 'openssl asn1parse' 都是可以接受的
  • 使用您发布的密钥,并对您的代码进行 Java 化处理(这意味着它几乎是逐字记录的),我没有遇到任何异常并获得预期的输出。
  • 戴夫,哪种格式更好?我不想将它存储为实际证书(至少我不这么认为)......我只是想以某种合理的格式存储和传递 RSA 密钥对的公共部分。
  • 看起来很笨重,使用 X.509/PKIX 证书——如果你没有一个真正的 CA 颁发的证书,那么一个虚拟的自签名证书——实际上是一种常见且处理公钥的传统方式(重要的是,PGP 和 SSH 除外)。如果您不想这样做,请使用 from X.509 的“SubjectPublicKeyInfo”结构 - 这是 OpenSSL 自 2000 年以来默认使用的结构,以及 Java Key 和 KeyFactory(非对称) 直接支持它——在 DER(又名二进制)而不是 PEM 中,但是即使没有 Bouncy,在不需要更改数据的情况下进行转换也很容易。

标签: kotlin bouncycastle


【解决方案1】:

dave_thompson_085 是对的。这段代码确实有效。他问我是否更改了数据,这让我更仔细地观察它,我意识到我正在对 base64 执行 .toUpperCase()。结果仍然是完全有效的 base64,有些位在这里和那里翻转。我根本不应该这样做 .toUpperCase() ,直到他这么说我才看到它。有弹性确实有效。

【讨论】:

    猜你喜欢
    • 2019-01-13
    • 2018-09-24
    • 2019-09-22
    • 2012-01-18
    • 2014-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-28
    相关资源
    最近更新 更多