Я пытаюсь выполнить обмен ключами Диффи-Хеллмана, используя 2 сертификата ECDSA x509.
Вот метод, в котором я извлекаю ключи из сертификатов для вычисления производного ключа.
private byte[] GetDerivedKey(X509Certificate2 publicCertificate, X509Certificate2 privateCertificate)
{
byte[] derivedKey;
using (var privateKey = privateCertificate.GetECDsaPrivateKey())
using (var publicKey = publicCertificate.GetECDsaPublicKey())
{
var privateParams = privateKey.ExportParameters(true); //This line is failing
var publicParams = publicKey.ExportParameters(false);
using (var privateCng = ECDiffieHellmanCng.Create(privateParams))
using (var publicCng = ECDiffieHellmanCng.Create(publicParams))
{
derivedKey = privateCng.DeriveKeyMaterial(publicCng.PublicKey);
}
}
return derivedKey;
}
Я прокомментировал сбойную строку privateKey.ExportParameters(true)
с ошибкой:
System.Security.Cryptography.CryptographicException: Запрошенная операция не поддерживается.
в System.Security.Cryptography.NCryptNative.ExportKey (ключ SafeNCryptKeyHandle, строковый формат)
в System.Security.Cryptography.CngKey.Export (формат CngKeyBlobFormat)
в System.Ptography.ECCng.ExportParameters (ключ CngKey, логическое значение includePrivateParameters, ECParameters & ecparams)
в System.Security.Cryptography.ECDsaCng.ExportParameters (логическое значение includePrivateParameters)
Поскольку это самозаверяющий сертификат, который я, Я предполагаю, что я делаю что-то не так.
Сначала я создаю сертификат корневого CA и передаю закрытый ключ для подписи своего сертификата.
private X509Certificate2 CreateECSDACertificate(string certificateName,
string issuerCertificateName,
TimeSpan lifetime,
AsymmetricKeyParameter issuerPrivateKey,
string certificateFriendlyName = null)
{
// Generating Random Numbers
var randomGenerator = new CryptoApiRandomGenerator();
var random = new SecureRandom(randomGenerator);
var signatureFactory = new Asn1SignatureFactory("SHA256WithECDSA", issuerPrivateKey, random);
// The Certificate Generator
var certificateGenerator = new X509V3CertificateGenerator();
// Serial Number
var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
// Issuer and Subject Name
var subjectDistinguishedName = new X509Name($"CN={certificateName}");
var issuerDistinguishedName = new X509Name($"CN={issuerCertificateName}");
certificateGenerator.SetSubjectDN(subjectDistinguishedName);
certificateGenerator.SetIssuerDN(issuerDistinguishedName);
// Valid For
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.Add(lifetime);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
//key generation
var keyGenerationParameters = new KeyGenerationParameters(random, _keyStrength);
var keyPairGenerator = new ECKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
var certificate = certificateGenerator.Generate(signatureFactory);
var store = new Pkcs12Store();
var certificateEntry = new X509CertificateEntry(certificate);
store.SetCertificateEntry(certificateName, certificateEntry);
store.SetKeyEntry(certificateName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });
X509Certificate2 x509;
using (var pfxStream = new MemoryStream())
{
store.Save(pfxStream, null, new SecureRandom());
pfxStream.Seek(0, SeekOrigin.Begin);
x509 = new X509Certificate2(pfxStream.ToArray());
}
x509.FriendlyName = certificateFriendlyName;
return x509;
}
Метод .HasPrivateKey()
возвращает true,который я прочитал, может вернуть ложное срабатывание.
Когда я добавляю свои сертификаты в магазин, я могу проверить цепочку сертификатов.
[Test]
public void CreateSelfSignedCertificate_AfterAddingToStore_CanBuildChain()
{
var result = _target.CreateSelfSignedCertificate(_subject, _issuer, TimeSpan.FromDays(356), _certificateFriendlyName, _issuerFriendlyName);
_store.TryAddCertificateToStore(result.CertificateAuthority, _caStoreName, _location);
_store.TryAddCertificateToStore(result.Certificate, _certStoreName, _location);
var chain = new X509Chain
{
ChainPolicy =
{
RevocationMode = X509RevocationMode.NoCheck
}
};
var chainBuilt = chain.Build(result.Certificate);
if (!chainBuilt)
{
foreach (var status in chain.ChainStatus)
{
Assert.Warn(string.Format("Chain error: {0} {1}", status.Status, status.StatusInformation));
}
}
Assert.IsTrue(chainBuilt, "Chain");
}
Сначала я подумал, что, возможно, частный сертификат должен был прийти из магазина сертификатов, поэтому яимпортировал его, а затем вытащил обратно, но я получаю ту же ошибку, что является еще одной причиной, по которой я считаю, что я что-то не так делаю.
РЕДАКТИРОВАТЬ:
У меня есть другой класс, генерирующий RSA x509, использующий тот же код для помещения закрытого ключа в сертификат.Это позволяет мне экспортировать закрытый ключ RSA.
Переменная _keyStrength
равна 384, и моя фабрика подписей использует "SHA256withECDSA"
.Я также пытался использовать "SHA384withECDSA"
, но я получаю ту же ошибку.