如何将原始模数和指数转换为RSA公钥(.pem格式)

16

我已经在二进制文件中嵌入了RSA公钥的模数和指数,现在我想要提取整个blob并创建一个可用的.pem格式的公钥。

目前,我正在提取完整的260字节(4个字节用于指数,256个字节用于模数),并使用base64进行编码。我是通过以下shell命令实现的:

tail -c $((filesize - start_of_key_data)) filename | head -c $size_of_key_data | base64 > outkey

这给我以下字符串:

<<<<<< modulus & exponent extracted from binary file, base64-encoded >>>>>>

tZyrQA6cZFJfVm6FyXwtZaLQYg8EecuO+ObrHTwc8JO+XrgnpNAdmlhbAEPxSNnjwhNnbYGYGL4F
vzmnZXzZU71Key42HQPh1k2Zx1UDbrH5ciODKx1ZbuEx8K24SHnL1nY/H75hwhT/ZRRVGQDvYDT+
sgzw2vmV66+dflw1Zs8BLhqjLjczdHvjeVXsDRJ9Mvvd/dhFH8UlTf4JpLGya9nsNIfNBBIf1Lll
RWwCTiEIbaOMgWcLjLV/2tk/j5Dra/oQnVf/2hVsEF/hXEx41YjeEW/warweoDVG7zaxrHEc/k/r
ZCUCZKxf8nBKdqax/gRICvkG6e5xg2GQw0W/ZwABAAE=

现在,当我拿出之前提取模数和指数的key.pem密钥对,并像这样显示公共部分:

Now, when I take the key.pem keypair that the modulus & exponent were originally extracted from, and display the public portion like so

openssl rsa -in key.pem -pubout -out pubkey.pem

我得到了这个字符串(我省略了页眉和页脚行):

<<<<<<<<< valid public key data extracted from keypair >>>>>>>>>

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtZyrQA6cZFJfVm6FyXwt
ZaLQYg8EecuO+ObrHTwc8JO+XrgnpNAdmlhbAEPxSNnjwhNnbYGYGL4FvzmnZXzZ
U71Key42HQPh1k2Zx1UDbrH5ciODKx1ZbuEx8K24SHnL1nY/H75hwhT/ZRRVGQDv
YDT+sgzw2vmV66+dflw1Zs8BLhqjLjczdHvjeVXsDRJ9Mvvd/dhFH8UlTf4JpLGy
a9nsNIfNBBIf1LllRWwCTiEIbaOMgWcLjLV/2tk/j5Dra/oQnVf/2hVsEF/hXEx4
1YjeEW/warweoDVG7zaxrHEc/k/rZCUCZKxf8nBKdqax/gRICvkG6e5xg2GQw0W/
ZwIDAQAB

您可以看到,我自己提取并使用base64编码的关键数据实际上存在于使用openssl从key.pem中提取的有效公钥数据的数据中。但是,在开头有45个字符是我的提取数据所没有的。

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA

并且最后的8个字符也不同。

ZwIDAQAB

请问有谁能提供一些关于如何将模数和指数转换成可用的公钥的建议吗?

(目标是在bash脚本中完成此操作,而不是像许多人建议的使用Python或C语言。)

2个回答

19

您所使用的命令 openssl rsa -in key.pem -pubout -out pubkey.pem 会生成如下的ASN.1结构:

SEQUENCE(2 elem)
  SEQUENCE(2 elem)
    OBJECT IDENTIFIER 1.2.840.113549.1.1.1
    NULL
  BIT STRING(1 elem)
    SEQUENCE(2 elem)
      INTEGER(2048 bit) 229263895356027367204242482830890190076375310244080661230946245232688INTEGER 65537

您可以使用openssl asn1parse -in pubkey.pem命令查看结构,或使用在线ASN.1解码器

公钥由以下内容组成:

  1. 固定的头部(包含所有字节,指定了整个序列以及模数编码)
  2. 模数
  3. 指定指数编码的头部
  4. 指数

如果您已正确收集了模数和指数字节,可以通过将这四个部分连接起来构建出可被 OpenSSL 理解的公钥形式。您已经有了第一个较长的头部。 "中间头部" 是 '02 03':

  1. '02'表示整数
  2. 整数本身长度为3个字节(65537 = 01 00 01)

如果您的模数为2048位(256字节),指数为3个字节(使长度字段保持有效),则可以通过将这四个部分连接起来生成 PEM 文件:

<header> <modulus> 0x02 0x03 <exponent>

这就是为什么二进制转储文件中的最后几个字节与 OpenSSL 输出不同的原因:提取的 260 个字节不包含 02 03,而是将 65537 记录为 00 01 00 01(不像 ASN.1 编码中的 01 00 01)。
总结一下,您可以按照以下步骤生成 PEM 文件:
将提取的模数和指数从 base64 转换回来并提取它们(请注意要跳过 65537 的前导零字节,即257个字节偏移量)。
echo 'tZyrQA6cZFJfVm6FyXwtZaLQYg8EecuO+ObrHTwc8JO+XrgnpNAdmlhbAEPxSNnjwhNnbYGYGL4FvzmnZXzZU71Key42HQPh1k2Zx1UDbrH5ciODKx1ZbuEx8K24SHnL1nY/H75hwhT/ZRRVGQDvYDT+sgzw2vmV66+dflw1Zs8BLhqjLjczdHvjeVXsDRJ9Mvvd/dhFH8UlTf4JpLGya9nsNIfNBBIf1LllRWwCTiEIbaOMgWcLjLV/2tk/j5Dra/oQnVf/2hVsEF/hXEx41YjeEW/warweoDVG7zaxrHEc/k/rZCUCZKxf8nBKdqax/gRICvkG6e5xg2GQw0W/ZwABAAE=' | base64 -d > modulus-exp.bin
dd if=modulus-exp.bin of=modulus.bin bs=1 count=256
dd if=modulus-exp.bin of=exponent.bin bs=1 skip=257 count=3

创建标题:

创建标题:

echo 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA' | base64 -d > header.bin
echo '02 03' | xxd -r -p > mid-header.bin

把它们拼接在一起:
cat header.bin modulus.bin mid-header.bin exponent.bin > key.der

转换为PEM格式:

openssl pkey -inform der -outform pem -pubin -in key.der -out key.pem

测试获取工作密钥 - 通过使用ASN.1解码器进行检查,或者

openssl asn1parse -in key.pem
openssl asn1parse -in key.pem -strparse 19

0

1
"PyCrypto 2.x未经维护,已过时且存在安全漏洞。" https://github.com/pycrypto/pycrypto 你知道另一个可以做到这一点的库吗? - bieboebap
@bieboebap 这个答案确实是正确的。它并没有使用PyCryto,而是使用了PyCryptodome,它是PyCryto的一个分支,目前仍然非常活跃并被广泛使用。 - undefined

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