我需要使用私钥和公钥(.key 和 .cer 文件)在 .Net 中创建一个 RSAParameters 对象。是否可以在.Net 中做到这一点而不使用第三方软件?如果是这样,我应该在哪里寻找。
我最终需要从这个对象中提取指数、模数、D、P、Q、DP、DQ、InverseQ,以便为密码服务器创建一个密钥块。
谢谢!
我需要使用私钥和公钥(.key 和 .cer 文件)在 .Net 中创建一个 RSAParameters 对象。是否可以在.Net 中做到这一点而不使用第三方软件?如果是这样,我应该在哪里寻找。
我最终需要从这个对象中提取指数、模数、D、P、Q、DP、DQ、InverseQ,以便为密码服务器创建一个密钥块。
谢谢!
“.key”和“.cer”文件扩展名绝不是密钥编码方式的明确规范。但是,“.cer”文件可能是 X.509 证书,其中包含(以及许多其他内容)公钥;因此,您可能希望使用X509Certificate
和X509Certificate2
类(在System.Security.Cryptography.X509Certificates
命名空间中)来解码证书并提取公钥。
但是,您需要私钥,而不是公钥。X509Certificate2 上的MSDN 文档非常令人困惑,因为它使用术语“证书”来指定公共部分(您在“.cer”文件中的内容)或公共和私有部分的联合,作为单个文件(在MSDN 描述为“PKCS7 (Authenticode)”的格式)。
编码的 RSA 私钥通常遵循PKCS#1中描述的格式,这并不复杂,但仍然依赖于ASN.1,其使用需要小心。有时,这样的 RSA 密钥被包装到一个更大的结构中,该结构还指定了密钥类型(即密钥用于 RSA);有关详细信息,请参阅PKCS#8。此外,这两种密钥编码通常都以 PEM 格式呈现:即带有页眉 ( ) 和页脚的Base64 。-----BEGIN RSA PRIVATE KEY-----
当然,您的私钥可以是任何格式(“.key”扩展名并不过分提供信息)。可选地,PKCS#8 和 PEM 都可以对称加密(使用密码派生密钥)。还有PKCS#12格式,可以看作是证书和私钥集合的存档格式,对之前的格式进行了包装;PKCS#12 包含许多加密层,在微软世界中以“PFX”(或“证书文件”的名称,一直令人困惑)而闻名。
使用一些代码就可以解码所有这些格式,但此时建议使用已经完成此类工作的库,而不是自己编写。Bouncy Castle是这项工作的常见嫌疑人。
OpenSSL命令行工具可以帮助您在某些密钥和证书格式之间进行转换。
编辑:如果您的私钥是 PKCS#8 DER 格式并且不受密码保护(PKCS#8 可以做到这一点),那么您可以使用相对简单的代码对其进行解码。DER 是一组用于将结构化数据转换为字节序列的规则。一个数据元素被编码为三个连续的部分:
因此名称为“TLV”(作为“标签、长度、值”)。一些元素本身就是包含子元素的结构,在这种情况下,值包含在子元素的编码的串联中,每个子元素都有自己的标签、长度和值。
一个标签通常是一个字节;对于 PKCS#8 和 RSA 密钥,您对标签 0x30(对于“SEQUENCE”,即具有子元素的元素)、0x02(“INTEGER”:一个整数值)和 0x04(“OCTET STRING”:一个 blob)感兴趣.
长度被编码为以下之一:
对于 INTEGER,该值应以有符号大端约定解释(第一个字节是最高有效字节,第一个字节的高位指定整数符号;对于 RSA,所有值都是正数,因此第一个字节应值介于 0 和 127 之间)。请注意System.Numerics.BigInteger
,在 .NET 4.0 中,有一个可以解码一堆字节的构造函数,但它希望它们采用 little-endian 约定,而不是 big-endian,因此您必须颠倒字节的顺序。
PKCS#8 的结构是:
PrivateKeyInfo ::= SEQUENCE {
version Version,
privateKeyAlgorithm AlgorithmIdentifier,
privateKey OCTET STRING,
attributes [0] Attributes OPTIONAL
}
Version ::= INTEGER { v1(0) }
那是 ASN.1 符号。这里必须理解的是对象是一个 SEQUENCE 元素:它被编码为一个 SEQUENCE 标记(0x30),然后是一个长度(n),然后是一个值(确切地说是n个字节)。然后,该值包含一系列编码元素,每个元素都采用 TLV 格式。第一个元素是一个整数,在正常情况下应该有数值 0(零编码为 '0x02 0x01 0x00')。第二个元素是 an AlgorithmIdentifier
,这里不详述;它实际上是一个 SEQUENCE 并且它标识了密钥类型(这里应该说“这是一个 RSA 密钥”);只需读取标签(应为 0x30),然后读取长度,然后跳过该值。第三个元素是一个 OCTET STRING:一个 0x04 标记,然后是长度m和m值字节。这就是我们感兴趣的内容。应该提取 OCTET STRING 的内容。我们将在下一段中对其进行解码。SEQUENCE的第四个元素PrivateKeyInfo
是可选的(它可能根本不存在,通常也不会存在),可用于对这种格式的各种扩展进行编码。
假设您已经提取了 OCTET STRING 的内容。这是一个字节序列,实际上是一个结构的 DER 编码,在 ASN.1 中,它看起来像这样:
RSAPrivateKey ::= SEQUENCE {
version INTEGER,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
-- otherPrimeInfos must be absent if version is two-prime,
-- present if version is multi-prime.
}
所以 OCTET STRING 的内容应该以 0x30(序列的标记)开始,然后是长度(r),然后是r字节。那些r字节是九个 INTEGER 的编码。第一个 INTEGER 应该是 0;如果不是,那么 RSA 密钥有两个以上的主要因素,你注定要失败。随后的八个 INTEGER 是您要查找的整数;只需解码它们,你就完成了。最后一个字段 ( otherPrimeInfos
) 是可选的,如果您的 RSA 密钥是“普通”RSA 密钥(具有两个素因子,而不是三个或更多),则应该不存在。