generare un certificato usando ECDSA in c #

Sto provando a generare un certificato (autofirmato) con chiave privata usando ECDSA. L’objective è ottenere il certificato “lo stesso” (pkcs12) come quando si usa 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 

Uso già BouncyCastle per aiutarmi nella creazione di certificati basati su RSA, quindi i prossimi passaggi seguono più o meno il modo in cui utilizzo per creare certificati RSA.

(nota che il prefisso BC è usato per le classi da BouncyCastle, MS per le classi .NET)

1 generare coppie di chiavi: chiavi private e pubbliche

 BC.IAsymmetricCipherKeyPairGenerator bcKpGen = BC.GeneratorUtilities.GetKeyPairGenerator("ECDSA"); bcKpGen.Init(new BC.ECKeyGenerationParameters(BC.SecObjectIdentifiers.SecP256r1, new BC.SecureRandom())); BC.AsymmetricCipherKeyPair bcSubjKeys = bcKpGen.GenerateKeyPair(); 

2 utilizzare la chiave privata per firmare la chiave pubblica con alcuni dati aggiuntivi (object, periodo di validità ecc.)

 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 chiave “join” privata dal passaggio 1 e certificato dal passaggio 2 per ottenere il certificato con chiave privata.

Se sto bene con il certificato senza chiave privata, potrei fare qualcosa come:

 MS.X509Certificate mcCert = new MS.X509Certificate2(bcCert.GetEncoded(), null); 

e ho finito.

I problemi arrivano quando si tenta di impostare la chiave privata:

 msCert.PrivateKey = ConvertBouncyToNetSomehow(bcSubjKeys.Private) 

(si noti che typeof msCert.PrivateKey è MS.AsymmetricAlgorithm e il tipo di bcSubjKeys.Private è BC.ECPrivateKeyParameters )

Sembra che il modo appropriato stia usando MS.ECDsaCng class (che eredita da MS.AsymmetricAlgorithm ), ma:

1 L’unico modo per convertire BC.ECPrivateKeyParameters in MS.CngKey (richiesto da MS.ECDsaCng ) è tramite il formato pkcs8:

 BC.PrivateKeyInfo bcPKInfo = BC.PrivateKeyInfoFactory.CreatePrivateKeyInfo(bcSubjKeys.Private); byte[] pkArr = bcPKInfo.GetDerEncoded(); MS.CngKey msPKCng = MS.CngKey.Import(pkArr, MS.CngKeyBlobFormat.Pkcs8PrivateBlob); 

ma usando questo approccio alcune informazioni sono perse perché il valore di msPKCng.AlgorithmGroup è "ECDH" mentre bcSubjKeys.Private.AlgorithmName dice "ECDSA" . Anche la chiave ECDH non può essere utilizzata con MS.ECDsaCng .

Tuttavia .. Potrei continuare con MS.ECDiffieHellmanCng invece di MS.ECDsaCng richiesto se ..

L’ implementazione di MS.X509Certificate2.set_PrivateKey richiede l’interfaccia MS.ICspAsymmetricAlgorithm . Ma nessuno di loro ( ECDsaCng , ECDiffieHellmanCng ) lo implementa.

A questo punto sembra necessario utilizzare un approccio diverso (a causa della condizione MS.ICspAsymmetricAlgorithm ), ad esempio certificato di esportazione e chiave privata nel file pkcs e uso di X509Certificate2.Import(..) .

Qualche suggerimento? Saluti

    Sfortunatamente, non è ansible farlo subito fuori dagli schemi in questo momento. È ansible ottenere il resto del modo con P / Invokes e .NET 4.6.2 (attualmente in anteprima). Oppure, con una deviazione tramite .NET Core, è ansible creare un PFX che funzioni in .NET 4.6.1.

    “ECDSA” vs “ECDH”

    Le librerie CNG di Windows dividono ECC in ECDSA e ECDH. Gli oggetti chiave ECDSA possono essere utilizzati solo per ECDSA; ma ogni volta che Windows non riesce a determinare l’utilizzo durante l’importazione PFX (o l’importazione PKCS # 8) chiama una chiave privata ECDH. Perché? Poiché Windows consente agli oggetti chiave ECDH di utilizzare sia l’accordo chiave (ECDH) che la firma digitale (ECDSA), pertanto ECDH è più flessibile.

    Ma .NET 4.6.1 non lo sapeva.

    .NET Core non ha questa limitazione (vedi https://github.com/dotnet/corefx/pull/5850 ), e .NET 4.6.2 ha anche rimosso la restrizione (per https://github.com/Microsoft /dotnet/blob/master/releases/net462/dotnet462-changes.md#user-content-bcl ).

    Generazione di chiavi “ECDSA”, anziché “ECDH”

    .NET Core ora ha un metodo ImportParameters su ECDsa. Se è ansible convertire l’object BC.ECPrivateKeyProperty in una struttura MS.ECParameters, è ansible importare il BLOB in un object ECDsaCng. (Assicurati di usarlo come una curva denominata, invece di copiare esplicitamente tutti i parametri della curva).

    Poiché è stato importato in modo mirato in un object ECDsa, ottiene una chiave ECDSA e tali informazioni verranno incorporate nel PFX.

    Costruire il PFX (legandolo tutto insieme)

    Con un po ‘di P / Invocazione puoi convincere Windows a build un PFX usando una chiave effimera. Mentre .NET non può accedere alle chiavi private effimere dai certificati, sarà in grado di utilizzarlo se caricato da un 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; } } 

    Avrai bisogno di .NET 4.6.1 (o più recente) per accedere a GetECDsaPrivateKey ().