是否有任何方便的方法可以使用.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
现在我需要将密钥导出为两个单独的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
有人有想法吗?
谢谢...
答案 0 :(得分:8)
答案介于" no"并且"不是真的"。
我将假设您不希望p12输出gunk位于public.pub
和private.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();
}
私钥更难。假设密钥是可导出的(如果您在Windows或macOS上,它不是,因为您没有断言X509KeyStorageFlags.Exportable
),您可以使用{{1}获取参数}。但现在你必须写下来。
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包含了封装RSAPrivateKey的PrivateKeyInfo,我们只是从那里开始。
privateKey.ExportParameters(true)
现在忽略关于otherPrimeInfos的部分。 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
}
是DP,exponent1
是DQ,exponent2
是InverseQ。
让我们使用pre-published 384-bit RSA key。
RFC 3447说我们想要Version = 0。其他一切都来自结构。coefficient
现在我们计算进入RSAPrivateKey结构的字节数。我算上0xF2(242)。由于它大于0x7F,我们需要使用多字节长度编码:// 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
。
现在使用字节数组81 F2
,您可以将其转换为多行Base64并将其包装在" RSA PRIVATE KEY" PEM装甲。但也许你想要一个PKCS#8。
30 81 F2 02 01 00 ... 9A 6F FD
所以,让我们再做一次...... RFC说我们也想要版本= 0。 AlgorithmIdentifier可以在RFC5280中找到。
PrivateKeyInfo ::= SEQUENCE {
version Version,
privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
privateKey PrivateKey,
attributes [0] IMPLICIT Attributes OPTIONAL }
Version ::= INTEGER
PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
PrivateKey ::= OCTET STRING
回填长度:
" b" series是13(0x0D),因为它只包含预定长度的东西。
" a"系列现在是(2 + 1)+(2 + 13)+(3 + 0xF5)= 266(0x010A)。
// 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]
现在您可以将其作为" PRIVATE KEY"。
加密吗?这是一场完全不同的球赛。
答案 1 :(得分:1)
X509certificate2->私有,公共和证书pems ...我刚刚发现您可以用5或6行外行代码来做到这一点!
有一个名为Chilkat的免费软件包(带有一些冷门商标)。它具有一些非常直观的Certificate Classes,下面是一些示例代码,说明如何创建自签名pfx格式的证书并将其导出到PEM!因此,这是一个X509Certificate2
实例,该实例带有证书,相关的公钥和对其进行签名的私钥,然后将其导出为三个单独的Pem文件。一个用于证书(包括公钥),一个用于公钥,一个用于私钥。非常简单(花了一周的阅读时间才弄清楚,哈哈)。然后检出https://github.com/patrickpr/YAOG以获得一个不错的OpenSSL Windows Gui,以查看/创建证书(如结果屏幕截图所示)。
using Chilkat;
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace CertPractice
{
static public class CertificateUtilityExample
{
public static X509Certificate2 GenerateSelfSignedCertificate()
{
string secp256r1Oid = "1.2.840.10045.3.1.7"; //oid for prime256v1(7) other identifier: secp256r1
string subjectName = "Self-Signed-Cert-Example";
var ecdsa = ECDsa.Create(ECCurve.CreateFromValue(secp256r1Oid));
var certRequest = new CertificateRequest($"CN={subjectName}", ecdsa, HashAlgorithmName.SHA256);
//add extensions to the request (just as an example)
//add keyUsage
certRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, true));
X509Certificate2 generatedCert = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddYears(10)); // generate the cert and sign!
//----------------end certificate generation, ie start here if you already have an X509Certificate2 instance----------------
X509Certificate2 pfxGeneratedCert = new X509Certificate2(generatedCert.Export(X509ContentType.Pfx)); //has to be turned into pfx or Windows at least throws a security credentials not found during sslStream.connectAsClient or HttpClient request...
Chilkat.Cert chilkatVersionOfPfxGeneratedCert = new Chilkat.Cert(); // now use Chilcat Cert to get pems
chilkatVersionOfPfxGeneratedCert.LoadPfxData(generatedCert.Export(X509ContentType.Pfx), null); // export as binary pfx to load into a Chilkat Cert
PrivateKey privateKey = chilkatVersionOfPfxGeneratedCert.ExportPrivateKey(); // get the private key
privateKey.SavePemFile(@"filepath"); //save the private key to a pem file
Chilkat.PublicKey publicKey = chilkatVersionOfPfxGeneratedCert.ExportPublicKey(); //get the public key
publicKey.SavePemFile(true, @"filepath"); //save the public key
chilkatVersionOfPfxGeneratedCert.ExportCertPemFile(@"filepath"); //save the public Cert to pem file
return pfxGeneratedCert;
}
}
答案 2 :(得分:0)
我找到了一个效果很好的解决方案。我找不到一个如何在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();
}
答案 3 :(得分:0)
基于@bartonjs 的知识(his answer),我写了一个小类,应该很容易使用。
所以现在也有一个完整的例子,无需使用外部dlls/nuget包
我唯一需要做的改变是:
通过我的课程,可以使用证书和私钥将 Let's Encrypt 证书从 PFX 格式转换为 PEM 格式。
如何使用我的课程
def check_best_value(model, generator , callback , bs , ep , sh):
bs_acc = []
bs_val = []
epoch_acc = []
epoch_val = []
shape_acc = []
shape_val = []
callback = EarlyStopping(monitor = 'val_loss' , patience = 1 , verbose = 1 ,
restore_best_weights=True , mode = 'auto')
for bs_size in bs:
for ep_size in ep:
for img_size in sh:
train_gen = generator.flow_from_directory(TRAIN_DIR ,
target_size = (img_size , img_size),
batch_size = bs_size ,
class_mode = 'categorical',
subset = 'training',
seed = 14)
val_gen = generator.flow_from_directory(TRAIN_DIR ,
target_size = (img_size , img_size),
batch_size = bs_size ,
class_mode = 'categorical',
subset = 'validation',
seed = 14)
h = model.fit(train_gen,
steps_per_epoch=train_gen.samples // bs_size,
epochs=ep_size,
validation_data=val_gen,
validation_steps= val_gen.samples // bs_size,
callbacks = [callback],
verbose=0)
shape_acc = shape_acc.append(h.history["accuracy"])
shape_val = shape_val.append(h.history["val_accuracy"])
epoch_acc = epoch_acc.append(h.history["accuracy"])
epoch_val = epoch_val.append(h.history["val_accuracy"])
bs_acc = bs_acc.append(h.history["accuracy"])
bs_val = bs_val.append(h.history["val_accuracy"])
return bs_acc , bs_val , epoch_acc , epoch_val , shape_acc , shape_val
我在那个班级背后的代码
var certificateLogic = new CertificateLogic("fileName.pfx", "privateKeyOfPfx");
certificateLogic.LoadCertificate();
certificateLogic.GenerateSaveCertificatePem();
certificateLogic.GenereateSavePrivateKeyPem();