Insertar certificado (con clave privada) en Root, el almacén de certificados LocalMachine falla en .NET 4

Tengo problemas para insertar un nuevo certificado de CA con una clave privada en el almacén de certificados Root de la máquina local.

Esto es lo que pasa:

//This doesn't help either. new StorePermission (PermissionState.Unrestricted) { Flags = StorePermissionFlags.AddToStore }.Assert(); var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine); privkey.PersistKeyInCsp = true; //This shouldn't be necessary doesn't make a difference what so ever. RSACryptoServiceProvider.UseMachineKeyStore = true; cert.PrivateKey = privkey; store.Open (OpenFlags.MaxAllowed); store.Add (cert); store.Close (); 

El certificado se inserta y todo se ve elegante: (¡mira!) tenga en cuenta que dice que tiene una clave privada

Nota: dice que tiene una clave privada.

Entonces dirías que uno podría encontrarlo con FindPrivateKey

 C:\Users\Administrator\Desktop>FindPrivateKey.exe Root LocalMachine -t "54 11 b1 f4 31 99 19 d3 5a f0 5f 01 95 fc aa 6f 71 12 13 eb" FindPrivateKey failed for the following reason: Unable to obtain private key file name Use /? option for help 

Es lindo … ¡PERO ES INCORRECTO! (2 perros estúpidos de referencia)

Y el diálogo de exportación del Certificado me da este mensaje muy bueno: texto alternativo

Este código se ejecuta al suplantar a un administrador que usa este fragmento: haga clic aquí

Me encantaría saber POR QUÉ?

(probado en Windows Server 2008 R2 y Windows 7)

¡Voy a ser condenado!

Funciona cuando lo compilo en v3.5 !!!!

¿Qué hacer?

Tenía exactamente el mismo problema y la solución resultó ser realmente simple. Todo lo que tuve que hacer es pasar

 X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet 

al ctor de X509Certificate2. Ahora está utilizando DotNetUtilities para convertir el certificado bouncycastle al .net one, pero el método helper crea el cert .net con DefaultKeySet (en lugar de MachineKeySet + PersistKeySet).

Y arregle la clave privada de esta manera:

 var cspParams = new CspParameters { KeyContainerName = Guid.NewGuid().ToString(), KeyNumber = (int)KeyNumber.Exchange, Flags = CspProviderFlags.UseMachineKeyStore }; var rsaProvider = new RSACryptoServiceProvider(cspParams); 

Espero que esto ayude.

Me parece que deberías importar la clave de otra manera. Consulte http://support.microsoft.com/kb/950090 para ver un ejemplo.

Además, no me parece bueno guardar la clave privada en UseMachineKeyStore . En la mayoría de los casos, necesita importar el certificado con la clave privada en Mi tienda de algún usuario e importar en el certificado Root only sin clave privada.

Si necesita guardar la clave privada en el almacén de claves de la máquina, debe proteger al menos la clave para que la lea solo para algunos usuarios seleccionados y no para Todos. El contenedor de claves es solo un archivo en el sistema de archivos (vea los archivos en el directorio “% ALLUSERSPROFILE% \ Microsoft \ Crypto \ Keys”) que tiene descriptores de seguridad como otros archivos en NTFS. Para cambiar las descripciones de seguridad de los archivos, puede usar la propiedad CspKeyContainerInfo.CryptoKeySecurity y AddAccessRule , RemoveAccessRule , etc.

ACTUALIZADO : en primer lugar, perdón por la respuesta larga.

Podría dividir el código de tu progtwig en dos partes. En la primera parte, usted genera un certificado autofirmado que se puede usar como un certificado CA y lo guarda como un archivo rootcert.pfx . En la segunda parte, usted importa el certificado, pero use RSACryptoServiceProvider con las propiedades de la clave creada anteriormente en lugar de utilizar rootcert.pfx .

Sugiero que reemplace la segunda parte de su código a un código más simple y estándar: importar certificado con la clave privada de rootcert.pfx como se describe en http://support.microsoft.com/kb/950090 . Funciona muy bien.

No utilizo BouncyCastle, así que no pude comentar la primera parte de tu código, pero en general lo que haces en el código también puedes hacerlo con respecto a la utilidad MakeCert.exe del SDK de Windows. Puedes hacer como seguir

 MakeCert.exe -pe -ss MY -a sha1 -cy authority -len 2048 -m 120 -r -# 1 -n "CN=Some Root CA, C=NL, OU=BleedingEdge, ST=Somewhere, L=Somelane" 

Luego puede exportar el certificado con o sin clave privada con respecto al complemento del certificado (para mmc.exe). En el ejemplo anterior, no restrinjo CA para alguna EKU especial, por lo que puede usarla sin ninguna restricción, pero si necesita las restricciones, puede agregar parámetros adicionales a MakeCert.exe . También puede usar MakeCert.exe para crear otro certificado que esté firmado con el certificado de CA. Por lo tanto, solo puede crear una PKI pequeña con respecto a MakeCert.exe.

Me parece que la creación del certificado es realmente una parte separada de tu código. Tu problema principal está en la segunda parte.

Si desea importar un certificado de CA, debe tener en cuenta algunas cosas importantes:

  • Debe importarlo en Root o AuthRoot en localMachine en todas las computadoras (o muchas) de su organización, pero debe importar el certificado sin la clave privada . Puedes hacer esto con respecto al siguiente

CertMgr.exe -add -c CA.cer -s -r localMachine AuthRoot

  • Debe importar el certificado de CA con clave privada en la computadora en una computadora y solo para el usuario que emitirá otros certificados (quién firmará nuevos certificados con la clave privada de CA). Un uso para importar el certificado en My certificate store of CurrentUser . Entonces, el código en la computadora podría parecerse

siguiendo:

 // import PFX X509Certificate2 cert = new X509Certificate2 (@"c:\Oleg\rootcert.pfx", "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); // save certificate and private key X509Store storeMy = new X509Store (StoreName.My, StoreLocation.CurrentUser); storeMy.Open (OpenFlags.ReadWrite); storeMy.Add (cert); // get certificate without private key // one can import certificate from rootcert.cer instead byte[] certBlobWithoutPrivateKey = cert.Export (X509ContentType.Cert); // save pure certificate in Root of the local machine X509Certificate2 certWithoutPrivateKey = new X509Certificate2 (certBlobWithoutPrivateKey); X509Store storeRoot = new X509Store (StoreName.Root, StoreLocation.LocalMachine); storeRoot.Open (OpenFlags.ReadWrite); storeRoot.Add (certWithoutPrivateKey); 

El código funcionará si lo haces cambia StoreName.My y StoreLocation.CurrentUser a otros valores, pero no te recomiendo que hagas esto.

En general, la importación de certificados en código .NET parece un poco extraña y no muestra lo que se hará bajo el capó. Windows solo conoce los contenedores de claves donde las claves privadas (para ser exactamente el par de claves) se guardarán con respecto a los CSP y los almacenes de certificados donde se guardarán los certificados (consulte http://msdn.microsoft.com/en-us/library/bb204781 .aspx sobre la ubicación de la tienda). Para poder guardar información sobre el contenedor de claves en el almacén de certificados, Microsoft introdujo las denominadas Propiedades Extendidas de Certificado . Si usa en .NET las propiedades de X509Certificate2 como X509Certificate2 , FriendlyName , HasPrivateKey , Archived , etc., trabaja con las Propiedades Extendidas del certificado. Así que te recomiendo que importes el certificado de CA dos veces. Uno en Root o AuthRoot sin establecer CERT_KEY_PROV_INFO_PROP_ID Certificate Extended Properties y una vez más en My store con la configuración de información sobre el lugar de Key Container con la clave privada ( CERT_KEY_PROV_INFO_PROP_ID ). Además, puede considerar eliminar la clave privada directamente después del uso, importarla solo si realmente necesita usarla y no mantenerla de manera permanente . Todo esto es importante para tener una mejor seguridad.

He encontrado este problema y parece que incluso el usuario con el que ejecuta la herramienta FindPrivateKey no tiene acceso a la clave y, por lo tanto, recibirá el mensaje “No se puede obtener el nombre de archivo de clave privada”. Puede ejecutar la herramienta como proceso LocalSystem.

Más información aquí:

http://www.itsolutionbraindumps.com/2011/02/finding-private-key-for-your.html

Dinko

nuevo X509Certificate2 (localPFXPath, inputPass, X509KeyStorageFlags.MachineKeySet & X509KeyStorageFlags.PersistKeySet); con el & en lugar del | trabajó para mi.

Por lo general, los certificados en Root no tendrán clave privada para administrar. Debe importar a Mi carpeta si está asociando clave en la solicitud web. Tengo una excepción TLS / SSl donde tengo una cadena de certificados de cliente. Si almacena toda la cadena de certificados en Mi tienda, entonces me deshice de esa excepción. Donde el problema es con las cuentas de usuario. La utilidad para almacenar los certificados usa una cuenta de usuario actual y la aplicación real se ejecuta en la cuenta del sistema.

El problema básico es que la API de certificados .NET es solo una envoltura alrededor de la API de administrador de certificados advapi32 de C ++, por lo que no puede especificar todas las opciones que pasan a esta API que es realmente responsable de insertar el certificado en Windows cert store y persistiendo las llaves. La conclusión es que la opción ” UseMachineStore ” debe pasarse a CspProviderFlags, que a su vez pasa al CAPI.CRYPT_MACHINE_KEYSET . Este es el pequeño tipo que determina si la clave se conserva persistente o no. Parece que hay varias razones diferentes por las que esta opción no se establece aunque configure X509KeyStorageFlags.PersistKeySet y MachineKeySet y Exportable . Todas estas opciones solo viven mientras la llave estúpida permanezca en la carpeta C: \ ProgramData \ Microsoft \ Crypto \ RSA \ MachineKeys \. Si CRYPT_MACHINE_KEYSET no se establece en el momento de la importación, entonces advapi32 se lleva la llave tan pronto como el GC extrae el asa del certificado.

Solución: agregue el certificado a la raíz de confianza ANTES de importar el certificado a la tienda personal de la máquina. Al leer los registros de CAPI2 , de hecho veo dos llamadas a “Objetos X509” cada vez que se “Importan” los Certificados. Uno siempre tiene , (lo que deseamos) pero el otro no lo hace A MENOS QUE “Verify Chain Policy” no devuelva ningún error. Así que parece que advapi32 está verificando la “validez” del certificado y devuelve una excepción que se traga X509Certificate2 ( me encanta cuántos bloques de catch vacíos tienen en ese código ) o advapi32 decide unilateralmente no insistir en las claves por falta de confianza certificados. (Por cierto, sospecho que esto es un cambio de comportamiento entre 2008 y 20012, pero no lo he probado). Para evitar esto, agregué un If-check a mi código para agregar el certificado de que si el Issuer es igual al Sujeto (es un certificado autofirmado) luego agregue el certificado a la raíz antes de agregarlo a Mi.

  if (certificate.Issuer.Equals(certificate.Subject)) { using (X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine)) { store.Open(OpenFlags.ReadWrite); store.Add(certificate); store.Close(); } } using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine)){ store.Open(OpenFlags.ReadWrite); store.Add(certificate); store.Close(); } 

Nota: He encontrado que esto no es necesario si utilizo un certificado que ya no tiene un Identificador de clave de sujeto. De alguna manera, cuando activa la API para generar realmente el SKI en lugar de entregarlo, activa el condicional para pasar la bandera mágica CRYPT_MACHINE_KEYSET a advapi32.