manifiesto leer firma encuentra certificados certificado almacén c# cryptography digital-signature

firma - leer certificado digital c#



CMS inicia sesión en.NET con cadena de certificados no en el almacén de certificados de confianza local (3)

Tengo certificados X509 que están almacenados en la red. Puedo leer la cadena desde el almacén de certificados de Windows remoto. Necesito firmar algunos datos e incluir la cadena en la firma para que sea posible validarla más tarde.

El problema es que no puedo encontrar una forma de poner la cadena de certificados en CsmSigner. He leído que toma el certificado del parámetro constructor e intenta construir una cadena con X509Chain.Build. Ignora los valores de la lista de certificados y falla (obviamente) porque no se puede encontrar ningún certificado en la tienda local de certificados de Windows.

A continuación, encontrará mi código de prueba (que funciona solo si los certificados se guardaron localmente en la tienda de Windows cert)

protected byte[] SignWithSystem(byte[] data, X509Certificate2 cert, X509Certificate[] chain) { ContentInfo contentInfo = new ContentInfo(data); SignedCms signedCms = new SignedCms(contentInfo, true); CmsSigner cmsSigner = new CmsSigner(cert); cmsSigner.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1"); //sha256 cmsSigner.IncludeOption = X509IncludeOption.WholeChain; if (chain != null) { //adding cert chain to signer cmsSigner.Certificates.AddRange(chain); signedCms.Certificates.AddRange(chain); } signedCms.ComputeSignature(cmsSigner); //fails here with System.Security.Cryptography.CryptographicException : A certificate chain could not be built to a trusted root authority. byte[] signedPkcs = signedCms.Encode(); return signedPkcs; }

¿Hay alguna forma de hacerlo funcionar sin cargar certificados a la tienda local? ¿Debo usar algún firmante alternativo?

Puedo intentar cargar certificados a la tienda, pero los problemas son

  • Tengo que agregar y eliminar certificados (se deben otorgar permisos)

  • Hay varios procesos que aplican la firma para que se agregue la sincronización entre procesos.

  • Esto no es lo que me gustaría hacer.


Para que quede claro, no soy un experto en seguridad o criptografía ... pero, por lo que sé, para que el receptor pueda validar la firma, el certificado raíz en la cadena de certificados que utilizó para firmar debe ser una raíz confiable para el receptor.

Si el receptor ya no tiene el certificado raíz en su tienda y está marcado como una raíz de confianza ... entonces no importa cómo firme los datos ... fallará la validación en el receptor. Y esto es por diseño.

Ver más en Chain of trust

Por lo tanto, la única solución real a su problema que veo es garantizar que el certificado raíz se aprovisione como raíz de confianza en ambos extremos ... Por lo general, lo hace una Autoridad de certificación .

Escenario de aplicación empresarial : normalmente, en una empresa, un grupo en el departamento de TI (que tiene acceso a todas las máquinas del dominio, como administradores de dominio) habilitaría este escenario asegurando que cada computadora del dominio tenga un certificado raíz propiedad de este grupo, presente en cada máquina como raíz de confianza, y un desarrollador de aplicaciones en la empresa normalmente solicita un nuevo certificado para usar con su aplicación, que tiene la cadena de confianza que se remonta al certificado raíz ya distribuido a todas las máquinas en el dominio.

Encontré a la persona de contacto de este grupo en su empresa y les puse un certificado que pueden usar para la firma.

Escenario de aplicaciones de Internet : existen Autoridades de certificación establecidas, que poseen sus certificados raíz, y trabajan con proveedores de sistemas operativos para garantizar que sus certificados raíz se encuentren en la tienda de confianza, ya que el proveedor del sistema operativo envía el sistema operativo a sus clientes. (Una de las razones por las que el uso del sistema operativo pirateado puede ser perjudicial. No se trata solo de virus / malware ...). Y es por eso que cuando utiliza un certificado emitido por VeriSign para firmar los datos, la firma puede ser validada por la mayoría de las otras máquinas en el mundo.


Agregar código de muestra que crea una firma de PKCS7 separada usando BouncyCastle (gracias a softwariness) sin el Almacén de certificados.

Utiliza las instancias .net X509Certificate2 como parámetro de entrada. El primer certificado en la colección debe estar vinculado con la clave privada para firmar los datos.

También me gustaría señalar que no es posible leer la clave privada asociada con el certificado de la tienda de certificados de Windows remota utilizando .net X509Certificate2.Private key property. Por defecto, la clave privada no se carga con el certificado utilizando X509Store (@ "/ remotemachine / MY", StoreLocation.LocalMachine) y cuando se accede a la propiedad X509Certificate2.PrivateKey en la máquina local, falla con el error "Keyset does not exists".

public void SignWithBouncyCastle(Collection<X509Certificate2> netCertificates) { // first cert have to be linked with private key var signCert = netCertificates[0]; var Cert = Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(signCert); var data = Encoding.ASCII.GetBytes(Cert.SubjectDN.ToString()); var bcCertificates = netCertificates.Select(_ => Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(_)).ToList(); var x509Certs = X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(bcCertificates)); var msg = new CmsProcessableByteArray(data); var gen = new CmsSignedDataGenerator(); var privateKey = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signCert.PrivateKey).Private; gen.AddSigner(privateKey, Cert, CmsSignedDataGenerator.DigestSha256); gen.AddCertificates(x509Certs); var signature = gen.Generate(msg, false).GetEncoded(); Trace.TraceInformation("signed"); CheckSignature(data, signature); Trace.TraceInformation("checked"); try { CheckSignature(new byte[100], signature); } catch (CryptographicException cex) { Trace.TraceInformation("signature was checked for modified data ''{0}''", cex.Message); } } void CheckSignature(byte[] data, byte[] signature) { var ci = new ContentInfo(data); SignedCms signedCms = new SignedCms(ci, true); signedCms.Decode(signature); foreach (X509Certificate cert in signedCms.Certificates) Trace.TraceInformation("certificate found {0}", cert.Subject); signedCms.CheckSignature(true); }


Ejemplo de firma CMS con BouncyCastle para .NET

Puede usar la biblioteca de cifrado BouncyCastle para .NET, que contiene su propio certificado X509 y maquinaria de firma de CMS. Muchos de los ejemplos y la documentación en la web son para Java, ya que BouncyCastle fue primero una biblioteca de Java. He utilizado la respuesta a esta pregunta de como punto de partida para el certificado y la carga de claves, y he agregado la firma de CMS. Puede que tenga que modificar los parámetros para producir los resultados que desea para su caso de uso.

He hecho que la función de firma se vea aproximadamente como la tuya, pero ten en cuenta que ahora la clave privada es un parámetro separado.

using System; using System.Collections.Generic; using System.IO; using System.Linq; using Org.BouncyCastle.Cms; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.X509.Store; class Program { protected static byte[] SignWithSystem(byte[] data, AsymmetricKeyParameter key, X509Certificate cert, X509Certificate[] chain) { var generator = new CmsSignedDataGenerator(); // Add signing key generator.AddSigner( key, cert, "2.16.840.1.101.3.4.2.1"); // SHA256 digest ID var storeCerts = new List<X509Certificate>(); storeCerts.Add(cert); // NOTE: Adding end certificate too storeCerts.AddRange(chain); // I''m assuming the chain collection doesn''t contain the end certificate already // Construct a store from the collection of certificates and add to generator var storeParams = new X509CollectionStoreParameters(storeCerts); var certStore = X509StoreFactory.Create("CERTIFICATE/COLLECTION", storeParams); generator.AddCertificates(certStore); // Generate the signature var signedData = generator.Generate( new CmsProcessableByteArray(data), false); // encapsulate = false for detached signature return signedData.GetEncoded(); } static void Main(string[] args) { try { // Load end certificate and signing key AsymmetricKeyParameter key; var signerCert = ReadCertFromFile(@"C:/Temp/David.p12", "pin", out key); // Read CA cert var caCert = ReadCertFromFile(@"C:/Temp/CA.cer"); var certChain = new X509Certificate[] { caCert }; var result = SignWithSystem( Guid.NewGuid().ToByteArray(), // Any old data for sake of example key, signerCert, certChain); File.WriteAllBytes(@"C:/Temp/Signature.data", result); } catch (Exception ex) { Console.WriteLine("Failed : " + ex.ToString()); Console.ReadKey(); } } public static X509Certificate ReadCertFromFile(string strCertificatePath) { // Create file stream object to read certificate using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read)) { var parser = new X509CertificateParser(); return parser.ReadCertificate(keyStream); } } // This reads a certificate from a file. // Thanks to: http://blog.softwarecodehelp.com/2009/06/23/CodeForRetrievePublicKeyFromCertificateAndEncryptUsingCertificatePublicKeyForBothJavaC.aspx public static X509Certificate ReadCertFromFile(string strCertificatePath, string strCertificatePassword, out AsymmetricKeyParameter key) { key = null; // Create file stream object to read certificate using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read)) { // Read certificate using BouncyCastle component var inputKeyStore = new Pkcs12Store(); inputKeyStore.Load(keyStream, strCertificatePassword.ToCharArray()); var keyAlias = inputKeyStore.Aliases.Cast<string>().FirstOrDefault(n => inputKeyStore.IsKeyEntry(n)); // Read Key from Aliases if (keyAlias == null) throw new NotImplementedException("Alias"); key = inputKeyStore.GetKey(keyAlias).Key; //Read certificate into 509 format return (X509Certificate)inputKeyStore.GetCertificate(keyAlias).Certificate; } } }

.NET CMS (Quick-fix con el resto de la cadena omitida de la firma)

Puedo reproducir su problema con un certificado cuya raíz no está en el almacén de certificados de confianza, y confirmar que agregar la cadena de certificados a la colección de Certificates cmsSigner / signedCms no evita la A certificate chain could not be built to a trusted root authority signedCms A certificate chain could not be built to a trusted root authority error de A certificate chain could not be built to a trusted root authority .

Puede firmar con éxito configurando cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;

Sin embargo, si haces esto, no obtendrás el resto de la cadena en la firma. Esto probablemente no es lo que quieres.

Como un aparte, en su ejemplo usted está usando X509Certificate para la matriz de certificados en la cadena, pero pasándolos a una X509Certificate2Collection (tenga en cuenta el "2" allí). X509Certificate2 deriva de X509Certificate , pero si no es realmente un X509Certificate2 que coloca en una de esas colecciones, obtendrá un error de conversión si algo itera sobre la colección (no se obtiene un error al agregar un certificado del tipo incorrecto) desafortunadamente, porque X509Certificate2Collection también se deriva de X509CertificateCollection y hereda sus métodos add).