2

我需要使用私钥和公钥(.key 和 .cer 文件)在 .Net 中创建一个 RSAParameters 对象。是否可以在.Net 中做到这一点而不使用第三方软件?如果是这样,我应该在哪里寻找。

我最终需要从这个对象中提取指数、模数、D、P、Q、DP、DQ、InverseQ,以便为密码服务器创建一个密钥块。

谢谢!

4

1 回答 1

3

“.key”和“.cer”文件扩展名绝不是密钥编码方式的明确规范。但是,“.cer”文件可能是 X.509 证书,其中包含(以及许多其他内容)公钥;因此,您可能希望使用X509CertificateX509Certificate2类(在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)感兴趣.

长度被编码为以下之一:

  • 值n介于 0 和 127(含)之间的单个字节:这对长度n进行编码;
  • n等于或大于 129 的字节,后跟正好n-128个字节,以大端格式编码长度。例如,长度 324 将被编码为三个字节:0x82 0x01 0x44。这读作:“0x82 是 128+2,因此我必须读取两个额外的字节;长度是 256*0x01+0x44 = 324”。

对于 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 标记,然后是长度mm值字节。这就是我们感兴趣的内容。应该提取 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 密钥(具有两个素因子,而不是三个或更多),则应该不存在。

于 2011-03-21T12:15:59.077 回答