2016-04-14 17 views
3

Ich versuche, (selbstsigniertes) Zertifikat mit privatem Schlüssel mit ECDSA zu generieren. Die Ziele sind „das gleiche“ (pkcs12) Zertifikat als bei der Verwendung von OpenSSL zu bekommen:Zertifikat mit ECDSA in c generieren #

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 

ich schon BouncyCastle verwenden, mir zu helfen mit dem Erstellen von RSA-basiertem Zertifikat (e), so dass beim nächsten Schritt mehr oder weniger folgen die Art, wie ich RSA-Zertifikate erstelle.

(beachten Sie, dass BC Präfix für Klassen von BouncyCastle verwendet wird, MS für .NET-Klassen)

Schlüsselpaar erzeugen: privaten und öffentlichen Schlüssel

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

Verwendung private Schlüssel um den öffentlichen Schlüssel mit einigen zusätzlichen Daten zu signieren (Betreff, Gültigkeitsdauer usw.)

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); 

"Join" privater Schlüssel aus Schritt1 und Zertifikat aus Schritt2, um Zertifikat mit privatem Schlüssel zu erhalten.

Wenn ich mit Zertifikat ok bin ohne private Schlüssel, konnte ich so etwas wie:

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

und ich bin fertig.

Die Frage (n) kommen, wenn private Schlüssel zu setzen versucht:

msCert.PrivateKey = ConvertBouncyToNetSomehow(bcSubjKeys.Private) 

(beachten Sie, dass typeof msCert.PrivateKey ist MS.AsymmetricAlgorithm und die Art der bcSubjKeys.Private ist BC.ECPrivateKeyParameters)

Es scheint, dass geeignete Weg ist, Verwendung MS.ECDsaCng Klasse (die von MS.AsymmetricAlgorithm erbt), aber:

die einzige Möglichkeit, f ound BC.ECPrivateKeyParameters-MS.CngKey (wird von MS.ECDsaCng) über pkcs8-Format zu konvertieren:

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

aber mit diesem Ansatz einige Informationen verloren gehen, weil Wert von msPKCng.AlgorithmGroup ist "ECDH" während bcSubjKeys.Private.AlgorithmName"ECDSA" sagt. Auch der ECDH-Schlüssel kann nicht mit MS.ECDsaCng verwendet werden.

Dennoch .. konnte ich mit MS.ECDiffieHellmanCng weiterhin statt angefordert von MS.ECDsaCng wenn ..

Implementierung von MS.X509Certificate2.set_PrivateKey erfordert das Objekt implementiert Schnittstelle MS.ICspAsymmetricAlgorithm. Aber keiner von ihnen (ECDsaCng, ECDiffieHellmanCng) implementieren es.

An diesem Punkt scheint ein anderer Ansatz verwendet zu werden (wegen MS.ICspAsymmetricAlgorithm Zustand), z. Exportieren Sie das Zertifikat und den privaten Schlüssel in die pkcs-Datei und verwenden Sie X509Certificate2.Import(..).

Irgendwelche Hinweise? Mit freundlichen Grüßen

Antwort

4

Leider ist es momentan nicht möglich, direkt aus der Box zu gehen. Sie können den Rest des Weges mit P/Invokes und .NET 4.6.2 (derzeit in der Vorschau) bekommen. Oder mit einem Umweg über .NET Core können Sie ein PFX erstellen, das in .NET 4.6.1 funktioniert.

"ECDSA" vs "ECDH"

Die Windows-CNG-Bibliotheken aufgeteilt ECC in ECDSA und ECDH. ECDSA-Schlüsselobjekte können nur für ECDSA verwendet werden; Wenn Windows jedoch die Verwendung während eines PFX-Imports (oder PKCS # 8-Imports) nicht ermitteln kann, ruft es einen privaten Schlüssel ECDH auf. Warum? Da ECDH-Schlüsselobjekte von Windows sowohl mit Schlüsselübereinstimmung (ECDH) als auch mit digitaler Signatur (ECDSA) ausgeführt werden, ist ECDH flexibler.

Aber .NET 4.6.1 wusste das nicht.

.NET Core verfügt nicht über diese Einschränkung (siehe https://github.com/dotnet/corefx/pull/5850), und .NET 4.6.2 hat auch die Einschränkung entfernt (per https://github.com/Microsoft/dotnet/blob/master/releases/net462/dotnet462-changes.md#user-content-bcl).

generieren "ECDSA" Tasten, statt "ECDH"

.NET-Core hat jetzt eine ImportParameters Methode auf ECDSA. Wenn Sie das BC.ECPrivateKeyProperty-Objekt in eine MS.ECParameters-Struktur konvertieren können, können Sie das Blob in ein ECDsaCng-Objekt importieren. (Stellen Sie sicher, dass Sie es als benannte Kurve verwenden, anstatt alle Kurvenparameter explizit zu kopieren).

Da es absichtlich in ein ECDsa-Objekt importiert wurde, erhält es einen ECDSA-Schlüssel, und diese Information wird in den PFX eingebettet.

die PFX Gebäude (alles zusammen zu binden)

Mit etwas P/Aufrufen von Windows überzeugen können eine PFX mit einem ephemeren Schlüssel zu bauen. Während .NET nicht kurzlebige private Schlüssel von Zertifikaten zugreifen kann, wird es in der Lage sein, davon Gebrauch zu machen, wenn aus einer PFX geladen:

[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; 
    } 
} 

Sie .NET benötigen 4.6.1 (oder neue) zu haben Zugriff auf GetECDsaPrivateKey().

+0

Es hat nicht funktioniert :-(Irgendwelche Hinweise? @bartonjs Haben Sie ein laufendes Beispiel für .net 4.6.1/2 –

+0

@DanielFisherlennybacon Sieht so aus, als hätte ich einen & -Fehler (und ein paar Tippfehler) PKCS8 Blob, der zu einem gültigen Cert-Blob gehört, https://gist.github.com/bartonjs/fac9ea9108dd184a7a66be77f0af3fef hat für mich gearbeitet – bartonjs

+0

Ich werde es dir ansehen Danke im Voraus! –