.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
誰かがアイデアを持っていますか?
ありがとう...
答えは「いいえ」と「実際にはない」の間のどこかにあります。
_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できます。
暗号化しますか?それはまったく別の球技です。
私はうまくいく解決策を見つけました。 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();
}