我目前正试图仅从解码的PEM文件创建RSACryptoServiceProvider
对象。经过几天的搜索,我确实设法解决了一个可行的解决方案,但并不是一个可以立即投入生产的解决方案。
简而言之,为了从组成PEM文件中公钥的字节创建RSACryptoServiceProvider
对象,我必须创建一个指定 key 大小的对象(当前使用2048个SHA256),然后使用RSAParameters
导入Exponent
对象和Modulus
设置。我正在这样做
byte[] publicKeyBytes = Convert.FromBase64String(deserializedPublicKey.Replace("-----BEGIN PUBLIC KEY-----", "")
.Replace("-----END PUBLIC KEY-----", ""));
// extract the modulus and exponent based on the key data
byte[] exponentData = new byte[3];
byte[] modulusData = new byte[256];
Array.Copy(publicKeyBytes, publicKeyBytes.Length - exponentData.Length, exponentData, 0, exponentData.Length);
Array.Copy(publicKeyBytes, 9, modulusData, 0, modulusData.Length);
// import the public key data (base RSA - works)
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(dwKeySize: 2048);
RSAParameters rsaParam = rsa.ExportParameters(false);
rsaParam.Modulus = modulusData;
rsaParam.Exponent = exponentData;
rsa.ImportParameters(rsaParam);
虽然这样做有效,但不能假设
deserializedPublicKey
恰好是270个字节,并且我需要的模数位于位置9且长度始终为256个字节。在给定一组公钥字节的情况下,如何更改此值以正确选择模量和指数字节?我试图理解ASN.1标准,但是很幸运地从中找到了我需要的东西-该标准有点拜占庭式。
任何帮助表示赞赏。
最佳答案
您无需导出现有参数,然后在它们之上重新导入。这迫使您的计算机生成RSA key ,然后将其丢弃。因此,为构造函数指定一个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描述了抽象语法符号1(ASN.1),这是DER是二进制编码的语法)
我们现在可以做一些解析:
30
这标识了已设置CONSTRUCTED位(0x20)的SEQUENCE(0x10),这意味着它包含其他DER/标记的值。 (序列始终由DER构成)
81 A0
下一部分是长度。由于设置了高位(> 0x7F),因此第一个字节为“长度长度”值。它指示真实长度在接下来的1个字节中编码(
lengthLength & 0x7F
)。因此,此SEQUENCE的内容总计为160个字节。 (在这种情况下,“其余数据”,但SEQUENCE可能已包含在其他内容中)。因此,让我们阅读内容:30 0D
我们再次看到我们的构造序列(
0x30
),其长度值为0x0D
,因此我们有一个13字节的有效负载。06 09 2A 86 48 86 F7 0D 01 01 01 05 00
06
是OBJECT IDENTIFIER,带有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。 BIT STRING编码为[标签] [长度] [未使用的位数]。未使用的位基本上总是零。因此,这是一个位序列,长度为0x8E
个字节,并且全部使用了它们。从技术上讲,我们应该到此为止,因为未设置CONSTRUCTED。但是,由于我们碰巧知道此结构的格式,因此我们将其值视作还是将CONSTRUCTED位置1:
30 81 8A
这是我们的 friend 再次构造的序列,
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://tools.ietf.org/html/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 key 编写解析器应该相当简单。我建议您将其编写为
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
}
关于c# - 从公钥正确创建RSACryptoServiceProvider,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41808094/