web-dev-qa-db-ja.com

X509証明書からPEMへの秘密/公開鍵のエクスポート

.NETを使用してPEM形式の.p12証明書から秘密/公開鍵をエクスポートする便利な方法はありますかCore?低レベルのバイトを操作せずに?私は何時間もグーグルで検索しましたが、.netコアではほとんど何も使用できません。またはどこにも文書化されていません。

X509Certificate2を作ろう

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

次に、キーを2つの個別のPEMキーとしてエクスポートする必要があります。私はすでにBouncyCastleでPemWriterを試しましたが、その型は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

誰かがアイデアを持っていますか?

ありがとう...

9
rudolfdobias

答えは「いいえ」と「実際にはない」の間のどこかにあります。

_public.pub_と_private.key_の上部にp12出力ガンクが必要ないことを前提としています。

_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();
}
_

秘密鍵はより難しいです。キーがエクスポート可能であると仮定すると(WindowsまたはmacOSの場合、_X509KeyStorageFlags.Exportable_をアサートしていないため、エクスポートできません)、privateKey.ExportParameters(true)を使用してパラメーターを取得できます。しかし今、あなたはそれを書き留めなければなりません。

RSA秘密鍵は、タグが「RSA PRIVATE KEY」で、ペイロードがASN.1( ITU-T X.68 )RSAPrivateKey(PKCS#1 /-)であるPEMエンコードファイルに書き込まれます。 RFC3447 )構造、通常DERエンコード( ITU-T X.69 )-署名されていないため、特定のDER制限はありませんが、多くの読者が想定している可能性がありますDER。

または、PKCS#8( RFC 5208 )PrivateKeyInfo(タグ: "PRIVATE KEY")、またはEncryptedPrivateKeyInfo(タグ: "ENCRYPTED PRIVATE KEY")にすることもできます。 EncryptedPrivateKeyInfoは、RSAPrivateKeyをカプセル化するPrivateKeyInfoをラップするため、ここから開始します。

_  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
  }
_

ここで、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
_

次に、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
_

だから、もう一度やってみましょう... RFCもここでversion = 0が欲しいと言っています。 AlgorithmIdentifierは RFC528 にあります。

_// 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]
_

長さを埋め戻す:

"b"シリーズは13(0x0D)です。これは、所定の長さのものだけが含まれているためです。

"a"シリーズは(2 + 1)+(2 + 13)+(3 + 0xF5)= 266(0x010A)になりました。

_30 82 01 0A  02 01 00 30  0D ...
_

これで、「プライベートキー」としてPEMできます。

暗号化しますか?それはまったく別の球技です。

17
bartonjs

私はうまくいく解決策を見つけました。 Windowsで証明書ストアからPEMファイルに移動する方法の正確な例が見つかりませんでした。確かに、これは一部の証明書では機能しない可能性がありますが、自分で作成した証明書を使用している場合(たとえば、2つのマシン間でセキュリティが必要なだけで、エンドユーザーには表示されないように制御している場合)、これは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();
}
2
CarComp