我想使用RSA公钥加密。如何最好地存储或检索私钥和公钥?在这里使用XML是否是一个好主意?
如何获取这些密钥?
RSAParameters privateKey = RSA.ExportParameters(true);
RSAParameters publicKey = RSA.ExportParameters(false);
RSAParameters有以下成员:D、DP、DQ、Exponent、InverseQ、Modulus、P和Q
哪一个是密钥?
我想使用RSA公钥加密。如何最好地存储或检索私钥和公钥?在这里使用XML是否是一个好主意?
如何获取这些密钥?
RSAParameters privateKey = RSA.ExportParameters(true);
RSAParameters publicKey = RSA.ExportParameters(false);
RSAParameters有以下成员:D、DP、DQ、Exponent、InverseQ、Modulus、P和Q
哪一个是密钥?
我想回应ala的评论,他问:
公钥 = 模数 + 指数
这是完全正确的。有几种存储这个 指数
+ 模数
的方法。第一次尝试制定标准是在RFC 3447(公钥密码学标准(PKCS) #1:RSA密码规范版本2.1)中,它定义了一个名为RSAPublicKey
的公钥结构:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
ASN.1
编码来存储公钥。我有一个样本公钥:
0xDC 67 FA F4 9E F2 72 1D 45 2C B4 80 79 06 A0 94 27 50 82 09 DD 67 CE 57 B8 6C 4A 4F 40 9F D2 D1 69 FB 99 5D 85 0C 07 A1 F9 47 1B 56 16 6E F6 7F B9 CF 2A 58 36 37 99 29 AA 4F A8 12 E8 4F C7 82 2B 9D 72 2A 9C DE 6F C2 EE 12 6D CF F0 F2 B8 C4 DD 7C 5C 1A C8 17 51 A9 AC DF 08 22 04 9D 2B D7 F9 4B 09 DE 9A EB 5C 51 1A D8 F8 F9 56 9E F8 FB 37 9B 3F D3 74 65 24 0D FF 34 75 57 A4 F5 BF 55
该公钥的DER ASN.1编码为:
30 81 89 ;SEQUENCE (0x89 bytes = 137 bytes)
| 02 81 81 ;INTEGER (0x81 bytes = 129 bytes)
| | 00 ;leading zero of INTEGER
| | DC 67 FA
| | F4 9E F2 72 1D 45 2C B4 80 79 06 A0 94 27 50 82
| | 09 DD 67 CE 57 B8 6C 4A 4F 40 9F D2 D1 69 FB 99
| | 5D 85 0C 07 A1 F9 47 1B 56 16 6E F6 7F B9 CF 2A
| | 58 36 37 99 29 AA 4F A8 12 E8 4F C7 82 2B 9D 72
| | 2A 9C DE 6F C2 EE 12 6D CF F0 F2 B8 C4 DD 7C 5C
| | 1A C8 17 51 A9 AC DF 08 22 04 9D 2B D7 F9 4B 09
| | DE 9A EB 5C 51 1A D8 F8 F9 56 9E F8 FB 37 9B 3F
| | D3 74 65 24 0D FF 34 75 57 A4 F5 BF 55
| 02 03 ;INTEGER (0x03 = 3 bytes)
| | 01 00 01 ;hex for 65537. see it?
如果您获取了上述 DER ASN.1 编码的 modulus
+exponent
:
然后您可以使用 PEM 编码它(即base64):
MIGJAoGBANxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hsSk9An9LRafuZXY
UMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4Sbc/w8rjE
3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80dV
ek9b9VAgMBAAE=
将Base64编码的数据包装起来是一种惯例:
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBANxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hsSk9An9LRafuZXY
UMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4Sbc/w8rjE
3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80dV
ek9b9VAgMBAAE=
-----END RSA PUBLIC KEY-----
这就是如何获得一个PEM DER ASN.1 PKCS#1 RSA公钥。
ssh-rsa
):string "ssh-rsa"
mpint e
mpint n
他们不想使用DER ASN.1编码(因为它非常复杂),取而代之的是四字节长度前缀:
00000007 ;7 byte algorithm identifier
73 73 68 2d 72 73 61 ;"ssh-rsa"
00000003 ;3 byte exponent
01 00 01 ;hex for 65,537
00000080 ;128 byte modulus
DC 67 FA F4 9E F2 72 1D 45 2C B4 80 79 06 A0 94
27 50 82 09 DD 67 CE 57 B8 6C 4A 4F 40 9F D2 D1
69 FB 99 5D 85 0C 07 A1 F9 47 1B 56 16 6E F6 7F
B9 CF 2A 58 36 37 99 29 AA 4F A8 12 E8 4F C7 82
2B 9D 72 2A 9C DE 6F C2 EE 12 6D CF F0 F2 B8 C4
DD 7C 5C 1A C8 17 51 A9 AC DF 08 22 04 9D 2B D7
F9 4B 09 DE 9A EB 5C 51 1A D8 F8 F9 56 9E F8 FB
37 9B 3F D3 74 65 24 0D FF 34 75 57 A4 F5 BF 55
将上述整个字节序列进行Base64编码:
AAAAB3NzaC1yc2EAAAADAQABAAAAgNxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hs
Sk9An9LRafuZXYUMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4S
bc/w8rjE3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80
dVek9b9V
并将其包装在OpenSSH头和尾中:
---- BEGIN SSH2 PUBLIC KEY ----
AAAAB3NzaC1yc2EAAAADAQABAAAAgNxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hs
Sk9An9LRafuZXYUMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4S
bc/w8rjE3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80
dVek9b9V
---- END SSH2 PUBLIC KEY ----
注意:OpenSSH使用四个带空格的破折号(----
),而不是五个没有空格的破折号(-----
)。
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
并将其扩展以包括算法标识符前缀(以防您想使用其他公钥加密算法而不是RSA):
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey RSAPublicKey }
RSA的“算法标识符”是1.2.840.113549.1.1.1
,它来自于:
1
- ISO分配的OIDs
1.2
- ISO成员机构
1.2.840
- 美国
1.2.840.113549
- RSADSI
1.2.840.113549.1
- PKCS
1.2.840.113549.1.1
- PKCS-1X.509是一个糟糕的标准,定义了一种可怕的将OID
编码为十六进制的复杂方式,但最终X.509 SubjectPublicKeyInfo
RSA公钥的DER ASN.1编码如下:
30 81 9F ;SEQUENCE (0x9f bytes = 159 bytes)
| 30 0D ;SEQUENCE (0x0d bytes = 13 bytes)
| | 06 09 ;OBJECT_IDENTIFIER (0x09 = 9 bytes)
| | 2A 86 48 86 ;Hex encoding of 1.2.840.113549.1.1
| | F7 0D 01 01 01
| | 05 00 ;NULL (0 bytes)
| 03 81 8D 00 ;BIT STRING (0x8d bytes = 141 bytes)
| | 30 81 89 ;SEQUENCE (0x89 bytes = 137 bytes)
| | | 02 81 81 ;INTEGER (0x81 bytes = 129 bytes)
| | | 00 ;leading zero of INTEGER
| | | DC 67 FA
| | | F4 9E F2 72 1D 45 2C B4 80 79 06 A0 94 27 50 82
| | | 09 DD 67 CE 57 B8 6C 4A 4F 40 9F D2 D1 69 FB 99
| | | 5D 85 0C 07 A1 F9 47 1B 56 16 6E F6 7F B9 CF 2A
| | | 58 36 37 99 29 AA 4F A8 12 E8 4F C7 82 2B 9D 72
| | | 2A 9C DE 6F C2 EE 12 6D CF F0 F2 B8 C4 DD 7C 5C
| | | 1A C8 17 51 A9 AC DF 08 22 04 9D 2B D7 F9 4B 09
| | | DE 9A EB 5C 51 1A D8 F8 F9 56 9E F8 FB 37 9B 3F
| | | D3 74 65 24 0D FF 34 75 57 A4 F5 BF 55
| | 02 03 ;INTEGER (0x03 = 3 bytes)
| | | 01 00 01 ;hex for 65537. see it?
您可以在解码的 ASN.1 中看到,它们只是在旧的 RSAPublicKey
前面添加了一个 OBJECT_IDENTIFIER
。
将上述字节和 PEM(即 base-64)编码:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcZ/r0nvJyHUUstIB5BqCUJ1CC
Cd1nzle4bEpPQJ/S0Wn7mV2FDAeh+UcbVhZu9n+5zypYNjeZKapPqBLoT8eCK51y
Kpzeb8LuEm3P8PK4xN18XBrIF1GprN8IIgSdK9f5SwnemutcURrY+PlWnvj7N5s/
03RlJA3/NHVXpPW/VQIDAQAB
标准是将其与类似于RSA PKCS#1的头部包装在一起,但不包含"RSA"(因为它可能是除RSA之外的其他内容):
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcZ/r0nvJyHUUstIB5BqCUJ1CC
Cd1nzle4bEpPQJ/S0Wn7mV2FDAeh+UcbVhZu9n+5zypYNjeZKapPqBLoT8eCK51y
Kpzeb8LuEm3P8PK4xN18XBrIF1GprN8IIgSdK9f5SwnemutcURrY+PlWnvj7N5s/
03RlJA3/NHVXpPW/VQIDAQAB
-----END PUBLIC KEY-----
这就是你如何发明一个X.509 SubjectPublicKeyInfo/OpenSSL PEM公钥格式。
这并不能阻止 RSA 公钥标准格式的列表。接下来是 OpenSSH 使用的专有公钥格式:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgNxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hs Sk9An9LRafuZXYUMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4Sbc/w8rjE3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80dVek9b9V
实际上,这就是上面提到的SSH公钥格式,但是加上了ssh-rsa
前缀,而不是用---- BEGIN SSH2 PUBLIC KEY ----
/---- END SSH2 PUBLIC KEY ----
包装。
这就是XML RSAKeyValue
公钥的易用性所在:
0x 010001
的base64编码为AQAB
0x 00 dc 67 fa f4 9e f2 72 1d 45 2c b4 80 79 06 a0 94 27 50 82 09 dd 67 ce 57 b8 6c 4a 4f 40 9f d2 d1 69 fb 99 5d 85 0c 07 a1 f9 47 1b 56 16 6e f6 7f b9 cf 2a 58 36 37 99 29 aa 4f a8 12 e8 4f c7 82 2b 9d 72 2a 9c de 6f c2 ee 12 6d cf f0 f2 b8 c4 dd 7c 5c 1a c8 17 51 a9 ac df 08 22 04 9d 2b d7 f9 4b 09 de 9a eb 5c 51 1a d8 f8 f9 56 9e f8 fb 37 9b 3f d3 74 65 24 0d ff 34 75 57 a4 f5 bf 55
的base64编码为ANxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hsSk9An9LRafuZXYUMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4Sbc/w8rjE3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80dVek9b9V
。这意味着该XML文件是:
<RSAKeyValue>
<Modulus>ANxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hsSk9An9LRafuZXYUMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4Sbc/w8rjE3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80dVek9b9V</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
简单得多。缺点是它不像(即Xml不像)那样包装、复制、粘贴得那么好用。
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBANxn+vSe8nIdRSy0gHkGoJQnUIIJ3WfOV7hsSk9An9LRafuZXY
UMB6H5RxtWFm72f7nPKlg2N5kpqk+oEuhPx4IrnXIqnN5vwu4Sbc/w8rjE
3XxcGsgXUams3wgiBJ0r1/lLCd6a61xRGtj4+Vae+Ps3mz/TdGUkDf80dV
ek9b9VAgMBAAE=
-----END RSA PUBLIC KEY-----
但它是一个很好的中性存储格式。
翻译器,二进制
获取)我成功地实现了将密钥存储为XML。RSACryptoServiceProvider中有两种方法:ToXmlString和FromXmlString。ToXmlString将返回一个XML字符串,其中包含公钥数据或同时包含公钥和私钥数据,具体取决于您设置其参数的方式。当提供一个包含公钥数据或同时包含公钥和私钥数据的XML字符串时,FromXmlString方法将使用适当的密钥数据填充RSACryptoServiceProvider。
modulus
、exponent
)和私钥(D
、P
、Q
、DP
、DQ
、InverseQ
)的值简单地表示为Base64编码的字节数据。从那里,您可以将其转换回PKCS#1、PKCS#7、PKCS#8、PFX、证书、OpenSSH、OpenSSL等格式。简而言之:XML是可移植的,而其他所有格式都是复杂专有的(例如ASN.1 DER,它甚至没有明确定义的标准)。 - Ian Boyd使用现有的标准格式,例如PEM。您的加密库应提供从PEM格式文件中加载和保存密钥的函数。
指数和模数是公钥。D和模数是私钥。其他值允许私钥持有者更快地进行计算。
我认为@Ian Boyd的回答不太准确,格式应该是SSH2,而不是OpenSSH,因为RFC4716定义了SSH2的格式,OpenSSH格式是专有的:
注意:OpenSSH使用四个带有空格的破折号(---- ),而不是五个没有空格的破折号(-----)。
公钥由模数和指数标识。私钥由其他成员标识。
78(十进制)= 4E(十六进制)
和65,535(十进制)= FFFF(十六进制)
一样,您看到的非常长的值(以十六进制显示,每2个字符有一个空格)是一个十进制中非常大的数字。 - Ian Boyd