将私钥/公钥从X509证书导出到PEM

rud*_*ias 8 c# x509certificate2 pem .net-core

有没有方便的方法从使用.NET Core的 PEM格式的.p12证书导出私钥/公钥?没有在低级别操作字节?我用谷歌搜索了几个小时,几乎没有任何东西在.net核心中可用,或者它没有记录在任何地方..

我们有一个X509Certificate2

var cert = new X509Certificate2(someBytes, pass);
var privateKey = cert.GetRSAPrivateKey();
var publicKey = cert.GetRSAPublicKey();
// assume everything is fine so far
Run Code Online (Sandbox Code Playgroud)

现在我需要将密钥导出为两个单独的PEM密钥.我已经尝试PemWriter过BouncyCastle,但是这些类型与Core的System.Security.Cryptography不兼容..没有运气.

- -编辑 - -

换句话说,我找到了一种方法来写这个:

$ openssl pkcs12 -in path/to/cert.p12 -out public.pub -clcerts -nokeys
$ openssl pkcs12 -in path/to/cert.p12 -out private.key -nocerts
Run Code Online (Sandbox Code Playgroud)

有人有想法吗?

谢谢 ...

bar*_*njs 18

答案介于"不"和"不是真的"之间.

我将假设您不希望p12输出gunk位于顶部public.pubprivate.key.

public.pub只是证书.该openssl命令行工具喜欢PEM编码的数据,所以我们写一个PEM编码的证书(注意,这是一个证书,而不是一个公开金钥.包含一个公开密钥,但本身不是一个):

using (var cert = new X509Certificate2(someBytes, pass))
{
    StringBuilder builder = new StringBuilder();
    builder.AppendLine("-----BEGIN CERTIFICATE-----");
    builder.AppendLine(
        Convert.ToBase64String(cert.RawData, Base64FormattingOptions.InsertLineBreaks));
    builder.AppendLine("-----END CERTIFICATE-----");

    return builder.ToString();
}
Run Code Online (Sandbox Code Playgroud)

私钥更难.假设密钥是可导出的(如果你在Windows或macOS上,它不是,因为你没有断言X509KeyStorageFlags.Exportable),你可以获得参数privateKey.ExportParameters(true).但现在你必须把它写下来.

RSA私钥被写入PEM编码文件,其标签为"RSA PRIVATE KEY",其有效载荷为ASN.1(ITU-T X.680)RSAPrivateKey(PKCS#1/RFC3447)结构,通常为DER编码(ITU-T X.690建议书) - 虽然它没有签名,但没有特定的DER限制,但许多读者可能会假设DER.

或者,它可以是PKCS#8(RFC 5208)PrivateKeyInfo(标记:"PRIVATE KEY"),或EncryptedPrivateKeyInfo(标记:"加密的私钥").由于EncryptedPrivateKeyInfo包装了PrivateKeyInfo,它封装了RSAPrivateKey,我们将从那里开始.

  RSAPrivateKey ::= SEQUENCE {
      version           Version,
      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
  }
Run Code Online (Sandbox Code Playgroud)

现在忽略关于otherPrimeInfos的部分. exponent1是DP,exponent2是DQ,coefficient是InverseQ.

让我们使用预先发布的384位RSA密钥.

RFC 3447说我们想要Version = 0.其他一切都来自结构.

// SEQUENCE (RSAPrivateKey)
30 xa [ya [za]]
   // INTEGER (Version=0)
   02 01
         00
   // INTEGER (modulus)
   // Since the most significant bit if the most significant content byte is set,
   // add a padding 00 byte.
   02 31
         00
         DA CC 22 D8 6E 67 15 75 03 2E 31 F2 06 DC FC 19
         2C 65 E2 D5 10 89 E5 11 2D 09 6F 28 82 AF DB 5B
         78 CD B6 57 2F D2 F6 1D B3 90 47 22 32 E3 D9 F5
   // INTEGER publicExponent
   02 03
         01 00 01
   // INTEGER (privateExponent)
   // high bit isn't set, so no padding byte
   02 30
         DA CC 22 D8 6E 67 15 75 03 2E 31 F2 06 DC FC 19
         2C 65 E2 D5 10 89 E5 11 2D 09 6F 28 82 AF DB 5B
         78 CD B6 57 2F D2 F6 1D B3 90 47 22 32 E3 D9 F5
   // INTEGER (prime1)
   // high bit is set, pad.
   02 19
         00
         FA DB D7 F8 A1 8B 3A 75 A4 F6 DF AE E3 42 6F D0
         FF 8B AC 74 B6 72 2D EF
   // INTEGER (prime2)
   // high bit is set, pad.
   02 19
         00
         DF 48 14 4A 6D 88 A7 80 14 4F CE A6 6B DC DA 50
         D6 07 1C 54 E5 D0 DA 5B
   // INTEGER (exponent1)
   // no padding
   02 18
         24 FF BB D0 DD F2 AD 02 A0 FC 10 6D B8 F3 19 8E
         D7 C2 00 03 8E CD 34 5D
   // INTEGER (exponent2)
   // padding required
   02 19
         00
         85 DF 73 BB 04 5D 91 00 6C 2D 45 9B E6 C4 2E 69
         95 4A 02 24 AC FE 42 4D
   // INTEGER (coefficient)
   // no padding
   02 18
         1A 3A 76 9C 21 26 2B 84 CA 9C A9 62 0F 98 D2 F4
         3E AC CC D4 87 9A 6F FD
Run Code Online (Sandbox Code Playgroud)

现在我们计算进入RSAPrivateKey结构的字节数.我算上0xF2(242).由于这大于0x7F,我们需要使用多字节长度编码:81 F2.

所以现在使用字节数组,30 81 F2 02 01 00 ... 9A 6F FD您可以将其转换为多行Base64并将其包装在"RSA PRIVATE KEY"PEM装甲中.但也许你想要一个PKCS#8.

  PrivateKeyInfo ::= SEQUENCE {
    version                   Version,
    privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
    privateKey                PrivateKey,
    attributes           [0]  IMPLICIT Attributes OPTIONAL }

  Version ::= INTEGER
  PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
  PrivateKey ::= OCTET STRING
Run Code Online (Sandbox Code Playgroud)

那么,让我们再做一次...... RFC说我们也想要版本= 0.AlgorithmIdentifier可以在RFC5280中找到.

// SEQUENCE (PrivateKeyInfo)
30 xa [ya [za]]
   // INTEGER (Version=0)
   02 01
         00
   // SEQUENCE (PrivateKeyAlgorithmIdentifier / AlgorithmIdentifier)
   30 xb [yb [zb]]
      // OBJECT IDENTIFIER id-rsaEncryption (1.2.840.113549.1.1.1)
      06 09 2A 86 48 86 F7 0D 01 01 01
      // NULL (per RFC 3447 A.1)
      05 00
   // OCTET STRING (aka byte[]) (PrivateKey)
   04 81 F5
      [the previous value here,
       note the length here is F5 because of the tag and length bytes of the payload]
Run Code Online (Sandbox Code Playgroud)

回填长度:

"b"系列是13(0x0D),因为它只包含预定长度的东西.

"a"系列现在是(2 + 1)+(2 + 13)+(3 + 0xF5)= 266(0x010A).

30 82 01 0A  02 01 00 30  0D ...
Run Code Online (Sandbox Code Playgroud)

现在您可以将PEM视为"私钥".

加密吗?那是一场完全不同的球赛.

  • 可惜的是没有更多的投票,您为此做出了很多努力。 (3认同)

Car*_*omp 5

我想出了一个效果很好的解决方案。我找不到有关如何在 Windows 中从证书存储转到 pem 文件的确切示例。当然,这可能不适用于某些证书,但如果您正在使用自己创建的证书(例如,如果您只需要控制最终用户看不到的两台机器之间的安全性),这是一种很好的方式回到 pem/pk(linux 风格)。

我使用了在http://www.bouncycastle.org/csharp/ 上找到的实用程序

X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);

X509Certificate2 caCert = certStore.Certificates.Find(X509FindType.FindByThumbprint, "3C97BF2632ACAB5E35B48CB94927C4A7D20BBEBA", true)[0];


RSACryptoServiceProvider pkey = (RSACryptoServiceProvider)caCert.PrivateKey;


AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetRsaKeyPair(pkey);
using (TextWriter tw = new StreamWriter("C:\\private.pem"))
{
    PemWriter pw = new PemWriter(tw);
    pw.WriteObject(keyPair.Private);
    tw.Flush();
}
Run Code Online (Sandbox Code Playgroud)