Die Vorschauversion des kumulativen Updates vom November 2020 für Windows 10 Version 2004 und niedriger sowie das Sicherheits- und Qualitätsrollup-Release vom Januar 2021 für .NET Framework 4.8 haben eine Änderung veröffentlicht, um den sauber-Up-Prozess für X509Certificate2-Zertifikate zu verbessern. Im Folgenden finden Sie weitere Erläuterungen zur Lebensdauer eines privaten Schlüssels unter Windows.
Seit .NET Framework 2.0 gibt es zwei verschiedene Möglichkeiten, ein Zertifikat und den zugehörigen privaten Schlüssel aus einer PKCS#12 PFX-Datei zu laden: als ein Zertifikatobjekt über Member in der X509Certificate2-Klasse oder als alle zertifikate, die in der PFX über die X509Certificate2Collection.Import-Methoden vorhanden sind.
Das Standardverhalten besteht darin, dass der private Schlüssel über eine der Systemkryptografiebibliotheken in einen persistenten (benannten) Schlüssel geladen wird, der indirekt eine Datei auf den Datenträger schreibt. Die meisten Aufrufer, die eine PFX laden, verwenden das Zertifikatobjekt nur vorübergehend. Wenn .NET also die nativen Ressourcen freigibt, die dem Zertifikat zugeordnet sind, wird auch der private Schlüssel gelöscht. Die Standard Ausnahmen für dieses Verhalten sind
-
Das Flag X509KeyStorageFlags.PersistKeySet, das dazu führt, dass die Datei geschrieben, aber nicht gelöscht wird.
-
Das Flag X509KeyStorageFlags.EphemeralKetSet, das dazu führt, dass der private Schlüssel ohne Sicherungsdatei in den Arbeitsspeicher geladen wird,
-
Ungewöhnliche Prozessbeendigung, die auftreten kann, wenn die Schlüssellöschung aussteht.
Kontraintuitiv haben die beiden verschiedenen Methoden zum Laden von Zertifikaten immer unterschiedliche Mechanismen verwendet, um nachzuverfolgen, wann der zeitpunkt zum Löschen des privaten Schlüssels geeignet ist. Beim Laden eines einzelnen Zertifikats über die X509Certificate2-Klasse wird ein Marker verwendet, der nur für die .NET-Runtime sichtbar ist und nur für diesen einen Objektverweis gilt. Beim Laden einer PFX-Datei über X509Certificate2Collection.Import wird ein Marker für das systemeigene Zertifikatobjekt verwendet, was dazu führt, dass die "Löschkontofähigkeit" für alle verwalteten Objekte freigegeben wird, die dasselbe native Zertifikat darstellen. Dies bedeutet, dass das neue X509Certificate2(otherCert.Handle) ist. Dispose() bewirkt, dass die Datei mit dem privaten Schlüssel während des Aufrufs von Dispose() gelöscht wird, wenn otherCert über X509Certificate2Collection.Import aus einer PFX geladen wurde, aber nicht, wenn sie aus einer PFX über X509Certificate2-Klassenmember geladen wurde. Einige Teile von .NET erstellen intern neue verwaltete X509Certificate2-Objekte aus nativen Zertifikathandles, z. B. die X509Chain-Klasse und die X509Certificate2Collection.Find-Methode. Diese Teile des Frameworks und alle Teile, die sie direkt oder indirekt verwenden, können zu einem vorzeitigen Löschen von Schlüsseln führen, da die von ihnen erstellten Objekte garbage collection werden.
In der ersten Version von .NET Framework 4.8 wurde ein Fehler eingeführt, der dazu führte, dass X509Certificate2Collection.Import den Löschmarker nicht anwendet, auch wenn weder PersistKeySet noch EphemeralKeySet angegeben wurden. .NET hat im Sicherheits- und Qualitätsrollup vom Januar 2021 eine Korrektur für diesen Fehler veröffentlicht, um die unbeabsichtigte Anhäufung von nicht mehr verwendeten Dateien mit privaten Schlüsseln zu verhindern. Aufrufer, die von diesem Fix betroffen sind, können das PersistKeySet-Flag angeben, um zum (unbeabsichtigt unterschiedlichen) .NET Framework 4.8 RTM-Verhalten zurückzukehren. Dies führt jedoch zu einer Dateiakkumulation, die mit benutzerdefinierter Bereinigungslogik behoben werden muss.
X509Certificate2Collection.Import unter .NET Core für Windows und .NET 5+ für Windows verhält sich genauso wie .NET Framework 2.0-4.7.2 (und .NET Framework 4.8 mit allen angewendeten Updates). Das .NET-Team erwägt, zu einem verbesserten Design in .NET 6 (oder zukünftigen Versionen) zu wechseln, aber dieses Entwurfsupdate gilt nicht für .NET Framework, .NET Core oder .NET 5.