您无需导出现有参数,然后在其之上重新导入。这会迫使您的机器生成 RSA 密钥,然后将其丢弃。因此,为构造函数指定密钥大小并不重要(如果您不使用该密钥,它通常不会生成密钥)。
公钥文件是 DER 编码的 blob。
-----BEGIN PUBLIC KEY-----
MIGgMA0GCSqGSIb3DQEBAQUAA4GOADCBigKBggC8rLGlNJ17NaWArDs5mOsV6/kA
7LMpvx91cXoAshmcihjXkbWSt+xSvVry2w07Y18FlXU9/3unyYctv34yJt70SgfK
Vo0QF5ksK0G/5ew1cIJM8fSxWRn+1RP9pWIEryA0otCP8EwsyknRaPoD+i+jL8zT
SEwV8KLlRnx2/HYLVQkCAwEAAQ==
-----END PUBLIC KEY-----
如果你获取 PEM 盔甲内的内容,它是一个 Base64 编码的字节数组。
30 81 A0 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01
05 00 03 81 8E 00 30 81 8A 02 81 82 00 BC AC B1
A5 34 9D 7B 35 A5 80 AC 3B 39 98 EB 15 EB F9 00
EC B3 29 BF 1F 75 71 7A 00 B2 19 9C 8A 18 D7 91
B5 92 B7 EC 52 BD 5A F2 DB 0D 3B 63 5F 05 95 75
3D FF 7B A7 C9 87 2D BF 7E 32 26 DE F4 4A 07 CA
56 8D 10 17 99 2C 2B 41 BF E5 EC 35 70 82 4C F1
F4 B1 59 19 FE D5 13 FD A5 62 04 AF 20 34 A2 D0
8F F0 4C 2C CA 49 D1 68 FA 03 FA 2F A3 2F CC D3
48 4C 15 F0 A2 E5 46 7C 76 FC 76 0B 55 09 02 03
01 00 01
ITU-T X.690定义了如何读取根据基本编码规则(BER)、规范编码规则(CER,我从未见过明确使用的)和杰出编码规则(DER)编码的内容。在大多数情况下,CER 限制 BER,而 DER 限制 CER,使得 DER 最容易阅读。 (ITU-T X.680描述了抽象语法符号一 (ASN.1),这是 DER 的二进制编码的语法)
我们现在可以做一些解析:
30
这标识了带有 CONSTRUCTED 位设置 (0x20) 的 SEQUENCE (0x10),这意味着它包含其他 DER/标记值。 (SEQUENCE 始终在 DER 中构建)
81 A0
下一部分是长度。由于它具有高位设置(> 0x7F),因此第一个字节是“长度长度”值。它表示真实长度编码在接下来的 1 个字节中(lengthLength & 0x7F
)。因此该SEQUENCE 的内容总共为160 字节。 (在这种情况下,“其余数据”,但序列可能包含在其他内容中)。那么我们来读一下内容:
30 0D
我们再次看到我们构建的序列(0x30
),长度值为0x0D
,所以我们有一个 13 字节的有效负载。
06 09 2A 86 48 86 F7 0D 01 01 01 05 00
The 06
是对象标识符,带有0x09
字节有效负载。 OID 有一种稍微不直观的编码,但这相当于文本表示1.2.840.113549.1.1.1
,即id-rsaEncryption
(http://www.oid-info.com/get/1.2.840.113549.1.1.1).
这仍然给我们留下了两个字节(05 00
)我们看到的是 NULL(具有 0 字节有效负载,因为它是 NULL)。
到目前为止我们已经
SEQUENCE
SEQUENCE
OID 1.2.840.113549.1.1.1
NULL
143 more bytes.
继续:
03 81 8E 00
The 03
表示位串。 BIT STRING 被编码为 [tag] [length] [未使用的位数]。未使用的位基本上始终为零。所以这是一个位序列,0x8E
字节长,并且全部被使用。
从技术上讲,我们应该到此为止,因为尚未设置 CONSTRUCTED。但由于我们碰巧知道该结构的格式,因此我们将该值视为已设置 CONSTRUCTED 位:
30 81 8A
这是我们的朋友的构造序列,0x8A
有效负载字节,它方便地对应于“剩下的一切”。
02 81 82
02
标识一个 INTEGER,并且这个有0x82
有效负载字节:
00 BC AC B1 A5 34 9D 7B 35 A5 80 AC 3B 39 98 EB
15 EB F9 00 EC B3 29 BF 1F 75 71 7A 00 B2 19 9C
8A 18 D7 91 B5 92 B7 EC 52 BD 5A F2 DB 0D 3B 63
5F 05 95 75 3D FF 7B A7 C9 87 2D BF 7E 32 26 DE
F4 4A 07 CA 56 8D 10 17 99 2C 2B 41 BF E5 EC 35
70 82 4C F1 F4 B1 59 19 FE D5 13 FD A5 62 04 AF
20 34 A2 D0 8F F0 4C 2C CA 49 D1 68 FA 03 FA 2F
A3 2F CC D3 48 4C 15 F0 A2 E5 46 7C 76 FC 76 0B
55 09
前导 0x00 将违反 DER,除非下一个字节设置了高位。这意味着 0x00 是为了防止符号位被设置,从而使其成为正数。
02 03 01 00 01
另一个 INTEGER,3 个字节,值01 00 01
。我们就完成了。
SEQUENCE
SEQUENCE
OID 1.2.840.113549.1.1.1
NULL
BIT STRING
SEQUENCE
INTEGER 00 BC AC ... 0B 55 09
INTEGER 01 00 01
收获https://www.rfc-editor.org/rfc/rfc5280我们看到这看起来很像SubjectPublicKeyInfo
结构:
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
-- contains a value of the type
-- registered for use with the
-- algorithm object identifier value
当然,它不知道RSA公钥格式是什么。但 oid-info 网站告诉我们去看看RFC 2313,我们看到的地方
An RSA public key shall have ASN.1 type RSAPublicKey:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e }
也就是说,我们读取的第一个 INTEGER 是模值,第二个是(公共)指数。
DER编码是big-endian,这也是RSAParameters编码,但是对于RSAParameters你需要删除前导0x00
来自模数的值。
虽然这并不像给您提供代码那么容易,但根据这些信息为 RSA 密钥编写解析器应该相当简单。我建议你把它写成internal static RSAParameters ReadRsaPublicKey(...)
,然后你只需要做
RSAParameters rsaParameters = ReadRsaPublicKey(...);
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
// things you want to do with the key go here
}