La versión preliminar de actualización acumulativa de noviembre de 2020 para Windows 10 versión 2004 y posteriores, así como la versión del paquete acumulativo de actualizaciones de calidad y seguridad de enero de 2021 para .NET Framework 4.8 publicaron un cambio para mejorar el proceso de limpieza para los certificados X509Certificate2. A continuación, proporcionamos aclaraciones adicionales sobre la vida de una clave privada en Windows.
Desde .NET Framework 2.0 ha habido dos maneras diferentes de cargar un certificado y su clave privada asociada desde un archivo PKCS#12 PFX: como un objeto de certificado a través de los miembros en la clase X509Certificate2 o como todos los certificados presentes en el PFX a través de los métodos X509Certificate2Collection.Import.
El comportamiento predeterminado es que la clave privada se carga en una clave persistente (con nombre) a través de una de las bibliotecas de criptografía del sistema, que indirectamente escribe un archivo en el disco. La mayoría de los autores de llamadas que cargan un PFX solo usan temporalmente el objeto certificate, por lo que cuando .NET libera los recursos nativos asociados con el certificado, también elimina la clave privada. Las excepciones principales a este comportamiento son
-
La marca X509KeyStorageFlags.PersistKeySet, que hace que el archivo se escriba pero no se elimine,
-
La marca X509KeyStorageFlags.EphemeralKetSet, que da como resultado que la clave privada se cargue en la memoria sin ningún archivo de copia de seguridad,
-
Finalización anormal del proceso, que puede ocurrir cuando está pendiente la eliminación de claves.
Contrainttivamente, las dos formas diferentes de cargar certificados siempre han utilizado mecanismos diferentes para realizar un seguimiento cuando es un momento adecuado para eliminar la clave privada. Al cargar un único certificado a través de la clase X509Certificate2 se usa un marcador que solo es visible para el tiempo de ejecución de .NET y solo se aplica a esa referencia de objeto. Al cargar un PFX a través de X509Certificate2Collection.Import se usa un marcador en el objeto de certificado nativo, lo que da como resultado que la "responsabilidad de eliminación" se comparta entre cualquier objeto administrado que represente el mismo certificado nativo. Esto significa que el nuevo X509Certificate2(otherCert.Handle). Dispose() hará que el archivo de clave privada se borre durante la llamada a Dispose() si otherCert se cargó desde un PFX a través de X509Certificate2Collection.Import, pero no si se cargó desde un PFX a través de cualquier X509Certificate2 miembros de clase. Algunas partes de .NET crean internamente nuevos objetos X509Certificate2 administrados a partir de controladores de certificado nativos, como la clase X509Chain y el método X509Certificate2Collection.Find. Estas partes del marco, y las porciones que directa o indirectamente las usan, pueden dar lugar a la eliminación prematura de claves, ya que los objetos que crean obtienen basura recopilada.
Se introdujo un error en la versión inicial de .NET Framework 4.8, lo que provocó que X509Certificate2Collection.Import no aplicara el marcador de eliminación aunque no se especificaran PersistKeySet ni EphemeralKeySet. .NET publicó una corrección para este error en el paquete acumulativo de actualizaciones de calidad y seguridad de enero de 2021 para evitar la acumulación no intencionada de archivos de clave privada abandonados. Los autores de llamadas afectados por esta corrección pueden especificar la marca PersistKeySet para volver al comportamiento de .NET Framework 4.8 RTM (diferente involuntariamente), aunque al hacerlo se producirá una acumulación de archivos que debe abordarse con lógica de limpieza personalizada.
X509Certificate2Collection.Import on .NET Core para Windows y .NET 5+ para Windows se comporta del mismo modo que .NET Framework 2.0-4.7.2 (y .NET Framework 4.8 con todas las actualizaciones aplicadas). El equipo de .NET está pensando en cambiar a un diseño mejorado en .NET 6 (o versiones futuras), pero esta actualización de diseño no se aplicará a .NET Framework, .NET Core ni .NET 5.