wake-up-neo.net

"Ungültiger Anbietertyp angegeben" CryptographicException, wenn versucht wird, den privaten Schlüssel des Zertifikats zu laden

Ich versuche, den privaten Schlüssel eines Zertifikats zu lesen, das mir von einem Drittanbieter-Dienstanbieter zur Verfügung gestellt wurde, sodass ich damit einige XML-Dateien verschlüsseln kann, bevor ich sie über das Kabel an sie schicke. Ich mache das programmgesteuert in C #, aber ich denke, dies ist ein Problem mit Berechtigungen oder einer falschen Konfiguration. Ich konzentriere mich daher auf die Fakten, die am relevantesten erscheinen:

  • Ich denke nicht, dass dieses Problem mit dem Code zusammenhängt. Mein Code funktioniert auf anderen Computern und das Problem betrifft Beispielcode von Microsoft.
  • Das Zertifikat wurde als PFX-Datei bereitgestellt und dient nur zu Testzwecken. Daher umfasst es auch eine Dummy-Zertifizierungsstelle.
  • Mit MMC.exe kann ich das Zertifikat in den persönlichen Speicher des lokalen Computers importieren, bevor ich allen relevanten Konten Berechtigungen für den privaten Schlüssel erteilt und die Zertifizierungsstelle per Drag & Drop in die vertrauenswürdigen Stammzertifizierungsstellen zieht.
  • Mit C # kann ich das Zertifikat laden (durch seinen Fingerabdruck identifiziert) und mit X509Certificate2.HasPrivateKey überprüfen, ob es einen privaten Schlüssel hat. Der Versuch, den Schlüssel zu lesen, verursacht jedoch einen Fehler. In .NET wird beim Versuch, auf die Eigenschaft X509Certificate2.PrivateKey zuzugreifen, eine CryptographicException mit der Meldung "Ungültiger Anbietertyp angegeben" ausgegeben. In Win32 gibt der Aufruf der Methode CryptAcquireCertificatePrivateKey den entsprechenden HRESULT-Code NTE_BAD_PROV_TYPE zurück.
  • Dies ist dieselbe Ausnahme, die auch auftritt, wenn zwei eigene Codebeispiele von Microsoft zum Lesen des privaten Schlüssels des Zertifikats verwendet werden.
  • Durch Installieren des gleichen Zertifikats im entsprechenden Speicher des aktuellen Benutzers anstelle des lokalen Computers kann der private Schlüssel erfolgreich geladen werden.
  • Ich habe Windows 8.1 mit lokalen Administratorrechten und habe versucht, meinen Code sowohl im normalen als auch im erhöhten Modus auszuführen. Kollegen unter Windows 7 und Windows 8 konnten den Schlüssel für das gleiche Zertifikat aus dem lokalen Computerspeicher laden.
  • Ich kann den privaten Schlüssel des selbstsignierten IIS Testzertifikats, das sich am selben Speicherort befindet, erfolgreich lesen.
  • Ich ziele bereits auf .NET 4.5 ab (dieser Fehler wurde bei älteren Versionen des Frameworks gemeldet).
  • Ich denke nicht, dass dies ein Problem mit Zertifikatvorlagen ist, weil ich davon ausgehen würde, dass dies sowohl den lokalen Computer als auch die aktuellen Benutzer-Stores gleichermaßen beeinflusst.

Im Gegensatz zu meinen Kollegen habe ich bereits mehrfach versucht, das Zertifikat auf verschiedene Weise zu deinstallieren und erneut zu installieren, z. B. über IIS Manager und auch ein älteres Zertifikat desselben Ausstellers. Ich sehe in MMC keine Spuren von alten oder doppelten Zertifikaten. Ich habe jedoch viele private Schlüsseldateien mit identischer Größe, die nach den letzten Schreibzeiten nach verschiedenen Installationsversuchen zurückgelassen worden sein müssen. Diese befinden sich an den folgenden Speicherorten für den lokalen Computer bzw. die aktuellen Benutzerspeicher:

c:\ProgramData\Microsoft\Crypto\RSA\MachineKeys

c:\Benutzer \\ AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21- [Rest der Benutzer-ID]

Kann jemand bitte sagen, ob:

  • Es ist eine gute Idee, das Zertifikat mit MMC zu deinstallieren, alle Dateien zu löschen, die wie verwaiste private Schlüssel aussehen, und dann das Zertifikat erneut zu installieren und es erneut zu versuchen.
  • Gibt es noch andere Dateien, die ich manuell löschen soll?
  • Gibt es noch etwas, was ich versuchen sollte?

UPDATE - Ein Codebeispiel wurde hinzugefügt, das den Versuch zeigt, einen privaten Schlüssel zu lesen:

static void Main()
{
    // Exception occurs when trying to read the private key after loading certificate from here:
    X509Store store = new X509Store("MY", StoreLocation.LocalMachine);
    // Exception does not occur if certificate was installed to, and loaded from, here:
    //X509Store store = new X509Store("MY", StoreLocation.CurrentUser);

    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

    X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
    X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
    X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.MultiSelection);
    Console.WriteLine("Number of certificates: {0}{1}", scollection.Count, Environment.NewLine);

    foreach (X509Certificate2 x509 in scollection)
    {
        try
        {
            Console.WriteLine("Private Key: {0}", x509.HasPrivateKey ? x509.PrivateKey.ToXmlString(false) : "[N/A]");
            x509.Reset();
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
    store.Close();

    Console.ReadLine();
}
30
Simon13

Ich hatte das gleiche Problem unter Windows 8 und Server 2012/2012 R2 mit zwei neuen Zertifikaten, die ich kürzlich erhalten habe. Unter Windows 10 tritt das Problem nicht mehr auf (aber das hilft mir nicht, da der Code, der das Zertifikat verarbeitet, auf einem Server verwendet wird). Während die Lösung von Joe Strommen grundsätzlich funktioniert, würde das andere private Schlüsselmodell eine massive Änderung des Codes unter Verwendung der Zertifikate erfordern. Ich finde, dass eine bessere Lösung darin besteht, den privaten Schlüssel von CNG in RSA zu konvertieren, wie von Remy Blok hier erklärt.

Remy verwendet OpenSSL und zwei ältere Tools, um die Umwandlung des privaten Schlüssels durchzuführen. Wir wollten sie automatisieren und entwickelten eine OpenSSL-only-Lösung. Wenn Sie MYCERT.pfx mit dem privaten Schlüsselkennwort MYPWD im CNG-Format angegeben haben, müssen Sie einen neuen CONVERTED.pfx mit privatem Schlüssel im RSA-Format und das gleiche Kennwort erhalten:

  1. Öffentliche Schlüssel extrahieren, vollständige Zertifikatskette:

OpenSSL pkcs12 -in "MYCERT.pfx" -nokeys -out "MYCERT.cer" -passin "pass:MYPWD"

  1. Privaten Schlüssel extrahieren:

OpenSSL pkcs12 -in "MYCERT.pfx" -nocerts –out “MYCERT.pem" -passin "pass:MYPWD" -passout "pass:MYPWD"

  1. Konvertieren Sie den privaten Schlüssel in das RSA-Format:

OpenSSL rsa -inform PEM -in "MYCERT.pem" -out "MYCERT.rsa" -passin "pass:MYPWD" -passout "pass:MYPWD"

  1. Öffentliche Schlüssel mit dem privaten RSA-Schlüssel in die neue PFX einbinden:

OpenSSL pkcs12 -export -in "MYCERT.cer" -inkey "MYCERT.rsa" -out "CONVERTED.pfx" -passin "pass:MYPWD" -passout "pass:MYPWD"

Wenn Sie den konvertierten pfx laden oder in den Windows-Zertifikatspeicher anstelle des CNG-Formats pfx importieren, wird das Problem behoben und der C # -Code muss nicht geändert werden.

Ein zusätzlicher Fehler, den ich bei der Automatisierung festgestellt habe: Wir verwenden lange generierte Kennwörter für den privaten Schlüssel, und das Kennwort enthält möglicherweise ". Für die OpenSSL-Befehlszeile müssen" Zeichen im Kennwort als "" gekennzeichnet werden.

26

Der Link zu Alejandros Blog ist der Schlüssel.

Ich glaube, das liegt daran, dass das Zertifikat mit der CNG-API ("Crypto Next-Generation") auf Ihrem Computer gespeichert ist. Die alte .NET-API ist nicht damit kompatibel, daher funktioniert sie nicht.

Sie können den Wrapper Security.Cryptography für diese API verwenden ( verfügbar bei Codeplex ). Dadurch werden Erweiterungsmethoden zu X509Certificate/X509Certificate2 hinzugefügt, sodass Ihr Code in etwa so aussieht:

using Security.Cryptography.X509Certificates; // Get extension methods

X509Certificate cert; // Populate from somewhere else...
if (cert.HasCngKey())
{
    var privateKey = cert.GetCngPrivateKey();
}
else
{
    var privateKey = cert.PrivateKey;
}

Leider unterscheidet sich das Objektmodell für CNG-private Schlüssel erheblich. Ich bin nicht sicher, ob Sie sie wie in Ihrem ursprünglichen Codebeispiel nach XML exportieren können ... In meinem Fall musste ich nur einige Daten mit dem privaten Schlüssel signieren.

9
Joe Strommen

In meinem Fall habe ich versucht, ein selbstsigniertes Zertifikat mit dem Befehl New-SelfSignedCertificate von PowerShell zu verwenden. Standardmäßig wird ein Zertifikat mithilfe der CNG-API (Crypto-Next Generation) anstelle der älteren/klassischen Krypto-CAPI generiert. Einige ältere Code-Teile werden Probleme damit haben. In meinem Fall war es eine ältere Version des IdentityServer STS-Providers.

Indem ich dies am Ende meines New-SelfSignedCertificate-Befehls hinzufügte, überwand ich das Problem:

- KeySpec KeyExchange

Referenz auf dem Schalter für den Powershell-Befehl:

https://docs.Microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps

5
Greg Loehr

Dies ist ein weiterer Grund, warum dies passieren kann. Dies war ein seltsames Problem und nachdem ich mich einen Tag lang gekämpft hatte, löste ich das Problem. Als Versuch habe ich die Erlaubnis für den Ordner "" C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys " geändert, der private Schlüsseldaten für Zertifikate enthält, die den Machine Key Store verwenden. Wenn Sie die Berechtigung für diesen Ordner ändern, werden alle privaten Schlüssel als "Microsoft Software KSP-Anbieter" angezeigt, der nicht der Anbieter ist (in meinem Fall sollte es "Microsoft RSA Schannel Cryptographic Provider" sein).

Lösung: Zurücksetzen der Berechtigungen auf den Ordner "Machinekeys"

Die ursprüngliche Berechtigung für diesen Ordner finden Sie in hier . In meinem Fall habe ich die Berechtigung für "Jeder" geändert und Leseberechtigungen erteilt, wobei das Häkchen für "Besondere Berechtigungen" entfernt wurde. Also habe ich bei einem meiner Teammitglieder nachgefragt (Rechtsklick-Ordner> Eigenschaften> Sicherheit> Erweitert> "Jeder" auswählen> Bearbeiten> Klicken Sie auf "Erweiterte Einstellungen" in der Liste der Berechtigungskontrollfelder.) 

Special permissions

Hoffe, das wird jemanden den Tag retten!

Hier habe ich die Antwort source gefunden, es geht an ihn, dies zu dokumentieren. 

5
Dhanuka777

In meinem Fall funktionierte der folgende Code in localhost (sowohl NET 3.5 als auch NET 4.7) einwandfrei:

 var certificate = new X509Certificate2(certificateBytes, password);

 string xml = "....";
 XmlDocument xmlDocument = new XmlDocument();
 xmlDocument.PreserveWhitespace = true;
 xmlDocument.LoadXml(xml);

 SignedXml signedXml = new SignedXml(xmlDocument);
 signedXml.SigningKey = certificate.PrivateKey;

 //etc...

Bei der Bereitstellung in einer Azure-Webanwendung unter certificate.PrivateKey Ist dies jedoch fehlgeschlagen.

Es funktionierte, indem der Code wie folgt geändert wurde:

 var certificate = new X509Certificate2(certificateBytes, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
                                                                   //^ Here
 string xml = "....";
 XmlDocument xmlDocument = new XmlDocument();
 xmlDocument.PreserveWhitespace = true;
 xmlDocument.LoadXml(xml);

 SignedXml signedXml = new SignedXml(xmlDocument);
 signedXml.SigningKey = certificate.GetRSAPrivateKey();
                                      // ^ Here too

 //etc...

Dank Microsoft Azure ging ein ganzer Arbeitstag verloren, wieder einmal in meinem Leben.

3
sports

Ich hatte das gleiche Problem in unserer IIS App:

 System.Security.Cryptography.Pkcs.PkcsUtils.CreateSignerEncodeInfo(CmsSigner signer, Boolean silent)
    System.Security.Cryptography.Pkcs.SignedCms.Sign(CmsSigner signer, Boolean silent)
    System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent)

Das Regenerieren von Zertifikaten, wie hier erwähnt, hat nicht geholfen. Ich habe auch festgestellt, dass die Testkonsolen-App unter Poolbenutzern einwandfrei funktioniert.

Das Problem ist verschwunden, nachdem die Einstellung "32-Bit-Anwendungen aktivieren" für den IIS App-Pool deaktiviert wurde.

1
Vadim Kiselev

PowerShell-Version der Antwort von @ berend-engelbrecht, vorausgesetzt openssl installiert über chocolatey

function Fix-Certificates($certPasswordPlain)
{
    $certs = Get-ChildItem -path "*.pfx" -Exclude "*.converted.pfx"
    $certs | ForEach-Object{
        $certFile = $_

        $shortName = [io.path]::GetFileNameWithoutExtension($certFile.Name)
        Write-Host "Importing $shortName"
        $finalPfx = "$shortName.converted.pfx"


        Set-Alias openssl "C:\Program Files\OpenSSL\bin\openssl.exe"

        # Extract public key
        OpenSSL pkcs12 -in $certFile.Fullname -nokeys -out "$shortName.cer" -passin "pass:$certPasswordPlain"

        # Extract private key
        OpenSSL pkcs12 -in $certFile.Fullname -nocerts -out "$shortName.pem" -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain"

        # Convert private key to RSA format
        OpenSSL rsa -inform PEM -in "$shortName.pem" -out "$shortName.rsa" -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain" 2>$null

        # Merge public keys with RSA private key to new PFX
        OpenSSL pkcs12 -export -in "$shortName.cer" -inkey "$shortName.rsa" -out $finalPfx -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain"

        # Clean up
        Remove-Item "$shortName.pem"
        Remove-Item "$shortName.cer"
        Remove-Item "$shortName.rsa"

        Write-Host "$finalPfx created"
    }
}

# Execute in cert folder
Fix-Certificates password
0
fiat

Ich hatte auch dieses Problem und nachdem ich die Vorschläge in diesem Beitrag versucht hatte, ohne Erfolg. Ich konnte mein Problem beheben, indem ich das Zertifikat mit dem Digicert-Zertifikat-Dienstprogramm https://www.digicert.com/util/ neu lade. Dadurch kann der Anbieter ausgewählt werden, in den das Zertifikat geladen werden soll. In meinem Fall lade ich das Zertifikat in den Provider Microsoft RSA Schannel Cryptographic Provider, von dem ich erwartet hatte, dass es das Problem überhaupt erst lösen würde.

0
Hugh

Wie viele andere Antworten darauf hingewiesen haben, tritt dieses Problem auf, wenn der private Schlüssel ein CNG-Schlüssel (Windows Cryptography: Next Generation) anstelle eines "klassischen" Windows-Verschlüsselungs-API-Schlüssels (CAPI) ist.

Ab .NET Framework 4.6 kann auf den privaten Schlüssel (unter der Annahme, dass es sich um einen RSA-Schlüssel handelt) über eine Erweiterungsmethode auf X509Certificate2: cert.GetRSAPrivateKey() zugegriffen werden.

Wenn der private Schlüssel von CNG gehalten wird, gibt die Erweiterungsmethode GetRSAPrivateKey ein RSACng-Objekt zurück (neu im Framework in 4.6). Da CNG eine Pass-Through zum Lesen älterer CAPI-Softwareschlüssel hat, gibt GetRSAPrivateKey normalerweise auch für einen CAPI-Schlüssel eine RSACng zurück. Wenn CNG es jedoch nicht laden kann (z. B. ein HSM-Schlüssel ohne CNG-Treiber), gibt GetRSAPrivateKey eine RSACryptoServiceProvider zurück.

Beachten Sie, dass der Rückgabetyp für GetRSAPrivateKeyRSA ist. Ab .NET Framework v4.6 sollten Sie nicht mehr für Standardoperationen als RSA verwenden müssen. Der einzige Grund, RSACng oder RSACryptoServiceProvider zu verwenden, ist der, wenn Sie mit Programmen oder Bibliotheken, die den NCRYPT_KEY_HANDLE oder den Schlüsselbezeichner verwenden (oder einen persistenten Schlüssel nach Namen öffnen), zusammenarbeiten müssen. (In .NET Framework v4.6 gab es viele Stellen, an denen das Eingabeobjekt noch in RSACryptoServiceProvider umgewandelt wurde. Diese wurden jedoch alle von 4.6.2 entfernt (dies ist natürlich vor mehr als 2 Jahren an diesem Punkt)).

Die Unterstützung für ECDSA-Zertifikate wurde in 4.6.1 über eine Erweiterungsmethode GetECDsaPrivateKey hinzugefügt, und DSA wurde in 4.6.2 über GetDSAPrivateKey aktualisiert.

In .NET Core ändert sich der Rückgabewert von Get[Algorithm]PrivateKey je nach Betriebssystem. Für RSA ist dies RSACng/RSACryptoServiceProvider unter Windows, RSAOpenSsl unter Linux (oder einem anderen UNIX-ähnlichen Betriebssystem außer macOS) und ein nicht öffentlicher Typ unter macOS (was bedeutet, dass Sie es nicht über RSA hinausgeben können).

0
bartonjs