我试图使用ECDSA生成(自签名)私钥证书。目标是获得“相同”(pkcs12)证书,与使用openssl时相同:
openssl ecparam -genkey -name secp256r1 -out mykey.key
openssl req -new -key mykey.key -out myreq.csr
openssl req -x509 -days 7 -key mykey.key -in myreq.csr -out mycert.crt
openssl pkcs12 -export -out mycert.pfx -inkey mykey.key -in mycert.crt我已经使用BouncyCastle来帮助我创建基于RSA的证书,所以接下来的步骤或多或少遵循我创建RSA证书的方式。
(注意,BC前缀用于来自BouncyCastle的类,MS用于.NET类)
1生成密钥对:私钥和公钥
BC.IAsymmetricCipherKeyPairGenerator bcKpGen = BC.GeneratorUtilities.GetKeyPairGenerator("ECDSA");
bcKpGen.Init(new BC.ECKeyGenerationParameters(BC.SecObjectIdentifiers.SecP256r1, new BC.SecureRandom()));
BC.AsymmetricCipherKeyPair bcSubjKeys = bcKpGen.GenerateKeyPair();2使用私钥与一些附加数据(主题、有效期等)签署公钥。
BC.X509V3CertificateGenerator bcXgen = new BC.X509V3CertificateGenerator();
// .. set subject, validity period etc
bcXgen.SetPublicKey(bcSubjKeys.Public);
BC.ISignatureFactory bcSigFac = new BC.Asn1SignatureFactory("SHA256WITHECDSA", bcSubjKeys.Private);
BC.X509Certificate bcCert = bcXgen.Generate(bcSigFac);3“连接”来自step1的私钥和来自step2的证书以获得私钥证书。
如果我对没有私钥的证书没有意见,我可以这样做:
MS.X509Certificate mcCert = new MS.X509Certificate2(bcCert.GetEncoded(), null);我就完蛋了。
当试图设置私钥时,问题就出现了:
msCert.PrivateKey = ConvertBouncyToNetSomehow(bcSubjKeys.Private)(请注意,msCert.PrivateKey类型为MS.AsymmetricAlgorithm,bcSubjKeys.Private类型为BC.ECPrivateKeyParameters)
似乎合适的方法是使用MS.ECDsaCng类(它继承自MS.AsymmetricAlgorithm),但是:
1我发现将BC.ECPrivateKeyParameters转换为MS.CngKey (MS.ECDsaCng所需)的唯一方法是通过pkcs8格式:
BC.PrivateKeyInfo bcPKInfo = BC.PrivateKeyInfoFactory.CreatePrivateKeyInfo(bcSubjKeys.Private);
byte[] pkArr = bcPKInfo.GetDerEncoded();
MS.CngKey msPKCng = MS.CngKey.Import(pkArr, MS.CngKeyBlobFormat.Pkcs8PrivateBlob);但是使用这种方法会丢失一些信息,因为msPKCng.AlgorithmGroup的值为"ECDH",而bcSubjKeys.Private.AlgorithmName则表示为"ECDSA"。另外,ECDH不能与MS.ECDsaCng一起使用.
不过..。如果.我可以继续使用MS.ECDiffieHellmanCng而不是请求的MS.ECDsaCng。
2实现MS.X509Certificate2.set_PrivateKey需要对象实现接口MS.ICspAsymmetricAlgorithm。但是他们中的任何一个(ECDsaCng,ECDiffieHellmanCng)都没有实现它。
在这一点上,似乎必须使用不同的方法(因为MS.ICspAsymmetricAlgorithm条件),例如导出证书和私钥到pkcs文件并使用X509Certificate2.Import(..)。
有什么暗示吗?问候
发布于 2016-07-02 23:29:12
不幸的是,现在还不可能马上开箱而出。您可以使用P/调用和.NET 4.6.2 (目前正在预览)获得其余部分。或者,只要绕道通过.NET核心,您就可以构建一个在.NET 4.6.1中工作的PFX。
"ECDSA“与"ECDH”
Windows CNG库将ECC拆分为ECDSA和ECDH。ECDSA密钥对象只能用于ECDSA;但是,每当Windows无法确定PFX导入(或PKCS#8导入)期间的使用情况时,它就调用私钥ECDH。为什么?由于Windows允许ECDH密钥对象同时执行密钥协商( ECDH )和数字签名(ECDSA),所以ECDH更加灵活。
但是.NET 4.6.1并不知道这一点。
.NET核心没有这个限制(参见https://github.com/dotnet/corefx/pull/5850),.NET 4.6.2也取消了这个限制(每一个https://github.com/Microsoft/dotnet/blob/master/releases/net462/dotnet462-changes.md#user-content-bcl)。
生成"ECDSA“键,而不是"ECDH”
.NET核心现在在ECDsa上有一个ImportParameters方法。如果可以将BC.ECPrivateKeyProperty对象转换为MS.ECParameters结构,则可以将blob导入ECDsaCng对象。(确保将其用作命名曲线,而不是显式复制所有曲线参数)。
由于它是有意导入到ECDsa对象中的,因此它将获得一个ECDSA密钥,并且该信息将嵌入到PFX中。
建立PFX (将其捆绑在一起)
只要稍微调用一点P/调用,就可以说服Windows使用一个短暂的密钥来构建PFX。虽然.NET无法从证书中访问临时私钥,但如果从PFX加载,它将能够使用它:
[DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)]
private static extern unsafe bool CertSetCertificateContextProperty(IntPtr pCertContext, CertContextPropId dwPropId, CertSetPropertyFlags dwFlags, SafeNCryptKeyHandle pvData);
internal enum CertContextPropId : int
{
CERT_NCRYPT_KEY_HANDLE_PROP_ID = 78,
}
[Flags]
internal enum CertSetPropertyFlags : int
{
None = 0,
}
private static X509Certificate2 MateECDsaPrivateKey(
X509Certificate2 cert,
CngKey privateKey)
{
// Make a new certificate instance which isn't tied to the current one
using (var tmpCert = new X509Certificate2(cert.RawData))
{
SafeNCryptKeyHandle keyHandle = privateKey.Handle;
// Set the ephemeral key handle property
if (!CertSetCertificateContextProperty(
tmpCert.Handle,
CertContextPropId.CERT_NCRYPT_KEY_HANDLE_PROP_ID,
CertSetPropertyFlags.None,
keyHandle))
{
throw new CryptographicException(Marshal.GetLastWin32Error());
}
// You could emit this, if you prefer.
byte[] pfxBytes = tmpCert.Export(X509ContentType.Pkcs12);
// Clear the key handle out again to prevent double-free
keyHandle = new SafeNCryptKeyHandle();
if (!CertSetCertificateContextProperty(
tmpCert.Handle,
CertContextPropId.CERT_NCRYPT_KEY_HANDLE_PROP_ID,
CertSetPropertyFlags.None,
keyHandle))
{
throw new CryptographicException(Marshal.GetLastWin32Error());
}
// Now load a new certificate which has a temporary keyfile on disk.
// Note: If you don't want exportability here, don't request it.
var matedCert = new X509Certificate2(pfxBytes, (string)null, X509KeyStorageFlags.Exportable);
using (ECDsa ecdsa = matedCert.GetECDsaPrivateKey())
{
if (ecdsa == null)
{
throw new InvalidOperationException("It didn't work");
}
}
return matedCert;
}
}您需要.NET 4.6.1 (或更新版本)才能访问GetECDsaPrivateKey()。
发布于 2021-03-27 08:42:11
我能够使用MateECDsaPrivateKey,通过使用CertificateRequest创建一个自签名的X509并传递ECDsa密钥。然后使用导出(X509ContentType.Cert)导出公钥。然后使用MateECDsaPrivateKey重新连接原始私钥。
我的用例是创建一个X509证书来存储ECDiffieHellman所需的密钥。如果基础Cng密钥处理不属于ECDH算法组,则它不喜欢该密钥。即使您将基础Cng密钥类型算法组设置为ECDH,它也会被重置为ECDsa。ECDiffieHellmanCng需要ECDH
通过使用MateECDsaPrivateKey,我能够重新连接X509公钥、正确的ECDH私钥。这只是Windows上的一个问题,在非Windows平台上,底层的密钥处理就不那么挑剔了.
我创建了https://dev.azure.com/ctx-eng-automation-devops/ElipticCurveX509Certificates,它根据操作系统平台运行正确的进程。它还对X509Certificate2进行了扩展,以使用正确的方法提取ECDH私钥来创建ECDiffieHellman对象。
https://www.nuget.org/packages/ElipticCurveX509Certificates/
https://stackoverflow.com/questions/36624105
复制相似问题