您无需导出现有参数,然后在它们之上重新导入。这会迫使您的机器生成一个 RSA 密钥,然后将其丢弃。所以为构造函数指定一个keysize并不重要(如果你不使用key它不会生成一个......通常)。
公钥文件是一个 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) 的序列 (0x10),这意味着它包含其他 DER/标记值。 (序列总是在 DER 中构造)
81 A0
下一部分是一个长度。由于它设置了高位(> 0x7F),因此第一个字节是“长度长度”值。它表示真实长度在接下来的 1 个字节中编码 (lengthLength & 0x7F)。因此,这个 SEQUENCE 的内容总共是 160 个字节。 (在这种情况下,“其余数据”,但 SEQUENCE 可能包含在其他内容中)。那么让我们阅读内容:
30 0D
我们再次看到我们的 CONSTRUCTED SEQUENCE (0x30),长度值为0x0D,所以我们有一个 13 字节的有效负载。
06 09 2A 86 48 86 F7 0D 01 01 01 05 00
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
03 表示位串。 BIT STRING 被编码为 [tag] [length] [number of used bits]。未使用的位基本上总是零。所以这是一个位序列,0x8E 字节长,并且都被使用了。
从技术上讲,我们应该停在那里,因为没有设置 CONSTRUCTED。但是因为我们碰巧知道这个结构的格式,所以我们把这个值当作 CONSTRUCTED 位被设置了:
30 81 8A
这又是我们的朋友 CONSTRUCTED SEQUENCE,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
另一个整数,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
Harvesing 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 是 Modulus 值,第二个是 (public)Exponent。
DER 编码是 big-endian,这也是 RSAParameters 编码,但对于 RSAParameters,您需要从 Modulus 中删除前导 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
}