viernes, 21 de agosto de 2009 a las 09:20hs por Gustavo Cantero (The Wolf)
Luego de mis artículos sobre certificados digitales, firma digital y hash sólo me queda escribir el último de esta serie en donde me gustaría mostrar cómo buscar, leer y utilizar estos certificados X.509 desde .NET.
Consultar los repositorios de certificados
Como primer paso vamos a recorrer los certificados que tenemos instalados en nuestra máquina. Para esto debemos utilizar la clase X509Store, la cual nos da la posibilidad de consultar un repositorio de certificados, por ejemplo, el repositorio raíz (Root), el repositorio donde están los certificados de las autoridades certificantes (CertificateAuthority), o el repostorio personal (My). También tenemos que elegir la ubicación del certificado, es decir, si vamos a querer consultar los certificados que están a nivel de máquina o de usuario. Tengan en cuenta que si van a utilizar la clase X509Store desde ASP.NET deben leer los certificados que están a nivel de máquina, ya que el usuario ASP.NET rara vez va a tener algún certificado instalado.
Luego de elegido el repositorio que queremos abrir debemos utilizar el método Open para que consulte al mismo.
Una vez abierto el repositorio, la propiedad Certificates contendrá una colección (basada en la clase X509Certificate2Collection) de los certificados almacenados.
Después de utilizar el repositorio no hay que olvidar de cerrar el mismo con método Close.
A continuación muestro cómo abrir el repositorio personal y escribir en la consola de debug los nombres y firmas digitales (hash) de cada certificado:
using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Diagnostics; ... X509Store objStore = new X509Store(StoreName.My, StoreLocation.LocalMachine); objStore.Open(OpenFlags.ReadOnly); foreach (X509Certificate2 objCert in objStore.Certificates) Debug.Print(objCert.SubjectName.Name + ": " + objCert.Thumbprint); objStore.Close();
Consultar la información del certificado y sus extensiones
Cada certificado posee una serie de datos (los cuales se describen en el artículo Conceptos de Certificado Digital y Firma Digital y creamos en el artículo Crear certificados de prueba para servidor y cliente). Además de estos datos los certificados poseen extensiones, que definen distintos posibles usos dependiendo de las mismas, y según la extensión poseen distintos datos almacenados. Por ejemplo, en la extensión del tipo “2.5.29.37” (también llamada “Enhanced Key Usage”) se guarda cual va a ser el destino del certificado, por ejemplo para autenticación del cliente.
Estas extensiones están en la colección Extensions del certificado, y para obtener la información almacenada debe castearse según el tipo del mismo. Para saber qué tipo de extensión es la almacenada se puede leer la propiedad Oid, la cual devuelve la clase homónima que representa un identificador de un objeto criptográfico (cryptographic object identifier) y luego, de este objeto, podemos leer la propiedad FriendlyName para obtener el nombre de la extensión.
A continuación hay un código de ejemplo donde se muestra la información de un certificado:
using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Diagnostics; using System.Text; ... X509Certificate2 objCert = ... //Acá tenemos que poner el certificado StringBuilder objSB = new StringBuilder("Detalle del certificado: \n\n"); //Detalle objSB.AppendLine("Persona = " + objCert.Subject); objSB.AppendLine("Emisor = " + objCert.Issuer); objSB.AppendLine("Válido desde = " + objCert.NotBefore.ToString()); objSB.AppendLine("Válido hasta = " + objCert.NotAfter.ToString()); objSB.AppendLine("Tamaño de la clave = " + objCert.PublicKey.Key.KeySize.ToString()); objSB.AppendLine("Número de serie = " + objCert.SerialNumber); objSB.AppendLine("Hash = " + objCert.Thumbprint); //Extensiones objSB.AppendLine("\nExtensiones:\n"); foreach (X509Extension objExt in objCert.Extensions) { objSB.AppendLine(objExt.Oid.FriendlyName + " (" + objExt.Oid.Value + ')'); if (objExt.Oid.FriendlyName == "Key Usage") { X509KeyUsageExtension ext = (X509KeyUsageExtension)objExt; objSB.AppendLine(" " + ext.KeyUsages); } if (objExt.Oid.FriendlyName == "Basic Constraints") { X509BasicConstraintsExtension ext = (X509BasicConstraintsExtension)objExt; objSB.AppendLine(" " + ext.CertificateAuthority); objSB.AppendLine(" " + ext.HasPathLengthConstraint); objSB.AppendLine(" " + ext.PathLengthConstraint); } if (objExt.Oid.FriendlyName == "Subject Key Identifier") { X509SubjectKeyIdentifierExtension ext = (X509SubjectKeyIdentifierExtension)objExt; objSB.AppendLine(" " + ext.SubjectKeyIdentifier); } if (objExt.Oid.FriendlyName == "Enhanced Key Usage") //2.5.29.37 { X509EnhancedKeyUsageExtension ext = (X509EnhancedKeyUsageExtension)objExt; OidCollection objOids = ext.EnhancedKeyUsages; foreach (Oid oid in objOids) objSB.AppendLine(" " + oid.FriendlyName + " (" + oid.Value + ')'); } } Debug.Print(objSB.ToString());
Validar certificados
Una de las tareas comunes que se realizan cuando estamos trabajando con certificados es la validación de los mismos. Esta tarea incluye verificar las fechas de validez, chequear que el certificado no haya sido revocado, etc. Esta validación debe hacerse en todos y cada uno de los certificados de la cadena hasta el certificado raíz.
Para validar la cadena de certificados se puede utilizar la clase X509Chain. Esta clase posee varias propiedades para especificar la forma en la que se quiere hacer la validación, por ejemplo, en la propiedad RevocationFlag se puede establecer si queremos validar sólo el certificado final (X509RevocationFlag.EndCertificateOnly), toda la cadena excepto el raíz (X509RevocationFlag.ExcludeRoot) o la cadena completa (X509RevocationFlag.EntireChain). También puede establecerse la forma en la que se quiere validar las revocaciones a través de la propiedad RevocationMode, pudiendo seleccionar en línea (X509RevocationMode.Online), fuera de línea (X509RevocationMode.Offline) o no chequear las listas de revocados (X509RevocationMode.NoCheck).
Para iniciar la validación, en la instancia de la clase X509Chain, se debe utilizar el método Build pasándole como parámetro el certificado. Luego de esto, en la propiedad ChainStatus, vamos a encontrar la lista de errores en las validaciones o, en caso de estar vacío este vector, significa que el certificado es válido.
Acá les dejo un código de ejemplo donde validamos un certificado y toda la cadena:
using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Diagnostics; using System.Text; ... X509Certificate2 objCert = ... //Acá tenemos que poner el certificado X509Chain objChain = new X509Chain(); //Verifico toda la cadena de revocación objChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; objChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; //Timeout para las listas de revocación objChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30); //Verificar todo objChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; //Se puede cambiar la fecha de verificación //objChain.ChainPolicy.VerificationTime = new DateTime(1999, 1, 1); objChain.Build(objCert); if (objChain.ChainStatus.Length != 0) foreach (X509ChainStatus objChainStatus in objChain.ChainStatus) Debug.Print(objChainStatus.Status.ToString() + " - " + objChainStatus.StatusInformation); else Debug.Print("Ok");
Firma de datos
Una de las tareas más comunes para las cuales se utilizan los certificados es para firmar documentos. Para esto casi siempre se utiliza el estándar de sintaxis para mensajes criptográficos PKCS #7, el cual está definido en el RFC 2315.
En .NET, para crear firmas digitales utilizando PKCS #7, se pueden utilizar las clases que están en el namespace System.Security.Cryptography.Pkcs.
Pienso que lo mejor para explicar cómo hacerte esto es a través de un ejemplo, por lo tanto, a continuación muestro un ejemplo donde creo un documento con su firma digital a partir de un texto:
using System.Security.Cryptography.Pkcs; using System.Diagnostics; using System.Text; ... X509Certificate2 objCert = ... //Acá tenemos que poner el certificado //Creamos el ContentInfo ContentInfo objContent = new ContentInfo(Encoding.ASCII.GetBytes("Scientia Soluciones Informáticas, la mejor consultora de desarrollo")); //Creamos el objeto que representa los datos firmados SignedCms objSignedData = new SignedCms(objContent); //Creamos el "firmante" CmsSigner objSigner = new CmsSigner(objCert); //Firmamos los datos objSignedData.ComputeSignature(objSigner); //Obtenemos el resultado byte[] bytSigned = objSignedData.Encode(); Debug.Print("Documento con firma: " + Convert.ToBase64String(bytSigned));
Otra opción, dependiendo de la forma en la que se quiere utilizar el resultado, es la de crear la firma solamente, sin el documento:
using System.Security.Cryptography.Pkcs; using System.Diagnostics; using System.Text; ... X509Certificate2 objCert = ... //Acá tenemos que poner el certificado //Creamos el ContentInfo ContentInfo objContent = new ContentInfo(Encoding.ASCII.GetBytes("Scientia Soluciones Informáticas, la mejor consultora de desarrollo")); //Creamos el objeto que representa los datos firmados SignedCms objSignedData = new SignedCms(objContent, true); //Creamos el "firmante" CmsSigner objSigner = new CmsSigner(objCert); //Firmamos los datos objSignedData.ComputeSignature(objSigner); //Obtenemos el resultado byte[] bytSigned = objSignedData.Encode(); Debug.Print("Firma digital: " + Convert.ToBase64String(bytSigned));
Verificar firma
Obviamente si creamos un documento con una firma digital es porque, tarde o temprano, vamos a querer verificar esta firma. Para realizar esto utilizamos nuevamente la clase SignedCms, la cual posee el método CheckSignature, y en caso de ser un documento firmado (no la firma “suelta”), en la propiedad ContentInfo.Content va a estar el documento sin firma.
A continuación les dejo los dos ejemplos, la verificación de la firma digital:
using System.Security.Cryptography.Pkcs; using System.Diagnostics; using System.Text; ... byte[] bytFirma = ... //Acá tenemos que poner la firma digital calculada en el ejemplo anterior ContentInfo objContent = new ContentInfo(Encoding.ASCII.GetBytes("Scientia Soluciones Informáticas, la mejor consultora de desarrollo")); SignedCms objDatos = new SignedCms(objContent, true); //Deserealizamos la firma objDatos.Decode(bytFirma); try { //Verificamos si la firma concuerda con los datos objDatos.CheckSignature(true); Debug.Print("Ok - La firma concuerda con los datos"); } catch { Debug.Print("Error - La firma no concuerda con los datos"); }
Y el ejemplo de la verificación de la firma y obtención del documento:
using System.Security.Cryptography.Pkcs; using System.Diagnostics; using System.Text; ... byte[] bytDocFirmado = ... //Acá tenemos que poner el documento firmado obtenido en el ejemplo anterior SignedCms objDatos = new SignedCms(); //Deserializo los bytes PKCS#7 objDatos.Decode(bytDocFirmado); //Verifico la firma y obtengo el documento try { objDatos.CheckSignature(true); Debug.Print("Ok - La firma concuerda con los datos"); Debug.Print(Encoding.ASCII.GetString(objDatos.ContentInfo.Content)); } catch { Debug.Print("Error - La firma no concuerda con los datos"); }
Encriptación y desencriptación utilizando un certificado
Por último voy a mostrar como encriptar y desencriptar documentos utilizando certificados digitales. Para esto, al igual con la firma de documentos, voy a mostrar un ejemplo, que creo es la mejor manera de entenderlo:
using System.Security.Cryptography.Pkcs; using System.Diagnostics; using System.Text; ... X509Certificate2 objCert = ... //Acá tenemos que poner el certificado //Creamos el ContentInfo ContentInfo objContent = new ContentInfo(Encoding.ASCII.GetBytes("Scientia Soluciones Informáticas, la mejor consultora de desarrollo")); //Creamos el objeto que representa los datos firmados EnvelopedCms objEncryptedData = new EnvelopedCms(objContent); //Creamos el destino CmsRecipient objRecipient = new CmsRecipient(objCert); //Encriptamos los datos objEncryptedData.Encrypt(objRecipient); //Datos encriptados byte[] bytResult = objEncryptedData.Encode(); Debug.Pring("Datos encriptados: " + Convert.ToBase64String(bytResult));
Y ahora voy a mostrar como desencriptar los datos obtenidos en el paso anterior. Para esto previamente tenemos que tener instalado el certificado con el que se encriptaron estos datos, junto con su clave privada, en un repositorio sobre el cual el usuario tenga permisos:
using System.Security.Cryptography.Pkcs; using System.Diagnostics; using System.Text; ... byte[] bytDatos = … //Datos encriptados EnvelopedCms objEncryptedData = new EnvelopedCms(); //Leemos los datos encriptados objEncryptedData.Decode(bytDatos); //Desencriptamos los datos objEncryptedData.Decrypt(); //Documento original byte[] bytDoc = objEncryptedData.ContentInfo.Content; //Mostramos el resultado Debug.Print("Datos desencriptados: " + Encoding.ASCII.GetString(bytDoc));
Espero que este resumen pueda servirle a cualquier que necesite trabajar con certificados digitales desde .NET.
Artículos relacionados
Categoria .NET, Certificados Digitales, Seguridad | Etiquetas: Certificado Digital, Firma Digital, PKCS, X.509
Excelente serie. De mucha utilidad
Hola Gustavo:
Nuevamente felicitarte por la serie. Probando el codigo fuente en lo referente a firmar datos obtengo CryptographicException «El conjunto de claves no existe» cuando se invoca al metodo objSignedData.ComputeSignature(objSigner);
Estoy usando certificados de prueba tal cual lo describiste en el inicio de serie.
Puedes indicarme cual es el problema? Muchas gracias.
Santiago: Muchas gracias por las felicitaciones.
Sobre el error, parecería ser porque no encuentra el certificado digital. ¿Guardaste un certificado en la variable objCert? De ser así: ¿El usuario tiene permisos para leer la clave privada del mismo? Ten en cuenta que si lo estás usando desde ASP.NET, el usuario «ASP.NET» debe tener permisos para leer el certificado.
Si sigue fin funcionar te invito a nuestro foro en http://foro.scientia.com.ar para dejar tus consultas, las cuales trataremos de responder a la brevedad.
Espero tus comentarios.
Suerte!
Hola Buenos dias, me pasa exactamente lo mismo. Pero no se cómo darle permisos al usuario ASP.NET.
Me podrian dar una mano con esto? por favor?
Muchas gracias y felicitaciones por el blog. Lo sigo siempre.
Saludos
Gracias por responder. El problema estaba en los permisos.
Saludos
Me alegro. Cualquier otra cosa no dudes en consultarnos.
Saludos.
Primera que nada felicitaciones un muy buen aporte.
Estoy haciendo una aplicacion vb net, para firmar un documento xml o parte de este, y lo unico que me entregan es un archivo en formato .PEM, y no he encontrado como hacerlo..seria una gran ayuda si me das un empujoncito.
Saludos, Jonathan
Hola..felicitaciones me ha ayudado bastante.
Necesito ayuda, estoy haciendo una aplicacion en vb .net y necesito firma un documento xml o parte de este, para ello tengo una string con la rsa key private (ese texto que esta en base 64 entre —begin rsa private key y end rsa private key), mi duda es como firmo este documento teniendo esta llave privada..Un abrazo y ojala me ayudes porfa.
hola felicitaciones por el articulo y el código son bastante token.
estoy realizando la validación del certificado, pero después de correr el codigo me muestra el siguiente mensaje:
«RevocationStatusUnknown – La función de revocación no puede comprobar la revocación para el certificado»
que puede estar pasando… entre los parametros se encuentra objChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; que tiene que ver con la CRL pero en donde le puedo decir la URL en la cual debe buscar la CRL o como se hace esta parte.
muchas gracias de antemano.
saludos…
Leonardo:
Antes que nada, gracias por los elogios.
Sobre tu consulta: la URL de la lista de revocación está dentro del certificado. A simple vista parece ser que no puede obtener la lista, y creo que las posibles causas debe ser alguna de las siguientes:
Cualquier otra consulta no dudes en escribirnos.
Suerte!
Hola Gustavo
he verificado los puntos 1 y 2 accediendo directamente a la URL de la CRL que trae el certificado el cual descarga un archivo .crl, y le modifiqué el tiempo de espera, pero aun asi me sigue presentando el mismo mensaje «RevocationStatusUnknown – La función de revocación no puede comprobar la revocación para el certificado.»
Por otro lado tambien estoy intentando firmar con el ejemplo tuyo, pero todos los certificados que posee mi maquina requieren password, no encontre en el ejemplo como le paso el password, le agradezco si me puede por favor indicar como hacerlo.
saludos
Leonardo, te pido mil disculpas por la demora en la respuesta.
Sobre tu problema, imagino que el problema no está en el certificado que necesitas validar sino en alguno de la cadena de certificación, por ejemplo, el que se utiliza como entidad certificadora de éste.
Espero que puedas resolver el inconveniente, sino te invito a nuestro foro http://foro.scientia.com.ar donde intentaremos ayudarte lo más rápido posible.
Saludos y suerte.
Al tratar de desencriptar los datos, nos un error de «Clave Inválida». el error da en la siguiente linea de código:
objEncryptedData.Decrypt();
Puedo leer certificados, encriptar documentos sin ningun problema.
Por favor si me pueden ayudar,
Saludos,
Jonathan.
Hola Jonathan,
Alguna vez nos pasó algo similar y el problema estaba relacionado con la longitud de los datos que estabamos intentando encriptar, ahora no recuerdo los límites (creo que eran 117 bytes) pero cuando probamos con datos más cortos el problema se resolvió, claro que no es la idea acortar los datos por que esa opción no es posible en casi ninguna ocasión.
Quizás una posible solución (al menos a nosotros nos sirvió) sea utilizar algorítmos asimétricos para encriptar las claves y algorítmos simétricos para encriptar la información, ya que estos no poseen esta restricción.
Espero que esta respuesta te sirva.
Buenas Tardes
Bueno la solución para que el código pida la clave del certificado digital almacenado en el store es simplemente colocar un false
signedCms.ComputeSignature(cmsSigner, false);
pero ahora tengo otro problema, necesito firmar un archivo extremadamente grande y el código que tenemos hasta el momento solo firma hasta 80 MB con archivos por ejemplo de un archivo de 1 GB no lo puede firmar dado que saca un error de memoria.
si alguien sabe de esto por favor les agradeceria me dieran la solución
cuando tenga el código completo lo pìenso subir… por ahora mi correo es jlqr@hotmail.com por si necesitan algo.. no se mucho pero igual ya tengo funcionando un pedazo de código.. y todo gracias a lo que he visto en esta página y en algunas otras…
Hola, queria saber como levantar una clave privada que se encuentra en un archivo.p12 desde .net? desde ya muchas gracias
Hola, Juan! Te pido disculpas por la tardanza, pero estuvimos muy ocupados las últimas semanas.
Sobre el tema del archivo .p12, en este artículo del MSDN http://msdn.microsoft.com/en-us/library/ms867088.aspx habla sobre cómo utilizar los archivos pfx/p12 desde .NET.
Espero que te sea de utilidad.
Saludos.
Hola Fredys, acá respondo la pregunta que hiciste en mi página: para obtener un certificado tienes varias formas, la más común es abrir un repositorio, por ejemplo el del usuario, y buscar por el nombre u otra información. Un ejemplo de lo mencionado es el siguiente:
Otra posibilidad sería abrir el certificado desde un archivo:
o también podrias leer el certificado desde la base de datos u otro lado:
Espero que esta información te sea de utilidad, cualquier otra cosa no dudes en escribir.
Suerte!
Gracias Gustavo por su ayuda y colaboración para quienes se la solicitamos, me ha funcionado perfectamente el asunto, mi objetivo era encriptar una cadena de texto para realizar una validación ante un servicio web, y bien excelente, simplemente que al momento de descencriptar la cadena me arroja error en la linea: objEncryptedDataD.Decrypt(); en la parte de arriba leía que posiblemente era por el tamaño de la cadena, no es mi caso ya que es corta. Dice que no puede encontrar el objeto o la propiedad.
Muchas gracias por su ayuda,
Hola Fredys! Necesitaría consultarte dos cosas para poder ayudarte: la primera es la longitud de lo que quieres encriptar, y la segunda es el mensaje exacto del error.
Saludos.
Hola Gustavo
No se, si todavia revises esto, ya que veo que es de hace 10 años, pero bueno si corro con suerte. Tengo el mismo problema que Fredys, devuelve esto «No puede encontrar el objeto o la propiedad\r\n»
Podrías orientarme a la solución. Esto es debido al certificado?
Hola José.
¿Puede ser que tu certificado no tenga la clave privada?
Saludos.
Hola, muy interesante tu publicación, me gustaría saber como puedo encriptar documentos en pdf con un certificado digital, muchas gracias.
Luis: la verdad es que no sé si se puede encriptar un PDF utilizando un certificado digital, pero por si te interesa acá te dejo un enlace a un proyecto donde se encripta el PDF con una contraseña utilizando iTextSharp: http://www.codeproject.com/KB/cs/iTextSharpPdfTables.aspx.
Suerte!
Hola Gustavo,
Tus articulos son de mucha utilidad.
estoy realizando una aplicacioon con vb net
y la duda que tengo es que me dan una llave privada .key( que correspon de al emisor ) y un certificado .cer
la duda como encriptar la salida del mD5 que ya la tengo usando la llave privada,
en tu articulo encriptas y desecriptas pero no observo donde se utiliza la llave privada , en mi caso seria .key
saludos.
No conozco los archivos .key, con qué aplicación los crean?
Saludos.
Hola Gustavo.
Lo primero es felicitarte por el artículo sobre certificados digitales.
Pese a que no me acaba de salir del todo (por mi falta de nociones), sé que el artículo es bueno y me está ayudando mucho.
Tengo unas cuantas dudas, pero para no cansar de momento solo haré una pregunta.
Trabajo con VS2008 en vb y a la hora de importar el espacio de nombres System.Scurity.Cryptography.Pkcs no me aparece, a que puede ser debido? (lo más parecido es System.Security.Cryptography.PKCS1MaskGenerationMethod), pero entonces el código me marca error. Alguna idea?
Muchas gracias por las felicitaciones.
Sobre tu error: puede ser que tu proyecto no tenga la referencia a System.Security.dll?
Suerte!
Otra vez. MUCHAS GRACIAS y felicidades por el artículo es muy bueno y me está ayudando muchisimo.
Efectivamente era la´referencia (se ve que por defecto no debía venir, pero la agregue y parece que empieza a funcionar).
Hasta ahora ya leo el certificado del usuario, repositorio servidor, y extensiones&info.
La idea es que pueda firmar un pdf alojado en el servidor….continuo en ello a ver si lo consigo…
Alberto, para firmar un PDF te recomiendo que lo hagas utilizando la librería iTextSharp, en este artículo se explica cómo hacerlo: http://itextpdf.sourceforge.net/howtosign.html.
Suerte!
Hola Gustavo, que tal?
De nuevo gracias por el aporte.
He estado trabajando el código de tus ejemplos y pese a que voy avanzando, me he quedado estancado en estos dos:
Validar certificados (me devuelve):
RevocationStatusUnknown – La función de revocación no puede comprobar la revocación para el certificado.
Firma documento (me devuelve):
La clave no existe.
En cuanto a lo de iTextSharp
Mi intención es leer el certificado de cliente y firmar un pdf, así que supongo que el que me interesa es el último punto:
«How to sign with a smartcard using an external signature dictionary with iTextSharp and .NET 2.0»
Lo estoy intentando traducir de CSharp a VB con la ayuda de (http://www.developerfusion.com/tools/convert/csharp-to-vb/).
Pero el código me marca 2 errores, uno en:
sap.PreClose(exc);
– Me dice que exc «Un valor de tipo «System.Collection.Hastable» no se puede convertir en «System.Collections.Generic.Dictionary(Of iTextSharp.text.pdf.PdfName,Integer)»
y otro en:
While (InlineAssignHelper(read, s.Read(buff, 0, 8192))) > 0
sha.TransformBlock(buff, 0, read, buff, 0)
End While
– Me marca como error: «No se ha declarado el nombre InlineAssignHelper»
Si me pudieses orientar te lo agradecería.
Una vez tengo el código, lo puedo dejar en el foro para aquellos que usen VB y lo necesiten.
Saludos.
Hola Gustavo.
Yo sigo intendando firmar pdf’s 😉
De los problemas planteados creo que he solucionado alguno…pongo la solución por si ha alguien le pasa lo mismo.
Con el código de iTextsharp
1-. “Un valor de tipo “System.Collection.Hastable” no se puede convertir en “System.Collections.Generic.Dictionary(Of iTextSharp.text.pdf.PdfName,Integer)”
Donde ponía:
Dim exc As New Hashtable()
Lo he sustituido por:
Dim exc As New Dictionary(Of PdfName, Integer)()
2-. “No se ha declarado el nombre InlineAssignHelper”
Lo he suprimido y dejado así:
While s.Read(buff, 0, 8192) > 0
sha.TransformBlock(buff, 0, read, buff, 0)
End While
En local me firma el pdf, aunque me deja la alerta de que la firma no es valida porque se ha modificado el documento.
Y en el servidor me da el error de:
(si leo el certificado con iis):
«La clave no existe»
o (si leo el certificado con la funcion de itextsharp)
«The current session is not interactive»
Si teneis alguna idea, os lo agradecería.
Alberto: te pido disculpas por la demora. En el siguiente artículo explico como firmar un PDF utilizando un certificado X.509 desde .NET, seguramente te sirva: Cómo firmar un documento PDF desde C# con iTextSharp.
Suerte!
Hola Gustavo buscando como firmar una cadena llegue a tu blog. Mira estoy realizando una aplicacion en c# para sellar una factura el proceso es este:
1- con los datos de la factura se crea una cadena (Ya lo pude hacer)
2- Sacas el digest md5 de la cadena(Ya lo pude hacer)
3- hacienda (la oficina que recauda impuestos en Mexico )te da para pruebas dos archivos uno aaa010101aaa_CSD_01.cer, aaa010101aaa_CSD_01.key y la clave del key privada ‘a0123456789’ los archivos estan en formato DER PKCS8
4- Con la clave privada que se encuentra en el archivo aaa010101aaa_CSD_01.key se debe firma el resultado de la digestion md5 usando el algoritmo RSA esto ultimo es lo que no he se como hacer.
He visto alguno ejemplos em MSDN pero solo ultizan archivos xml para el RSA
no se si se pueda ultizar los archivos DER PKCS8 en .NET
Espero que me puedas ayudar o darme alguna orientacion donde pueda investigar para poder relizarlo.
Nunca necesité usar un PKCS#8, pero probablemente la librería «Bouncy Castle» te sirva, ya que implementa mucha funcionalidad para usar certificados y firmar, encriptar, etc. La dirección del sitio es http://www.bouncycastle.org/csharp.
Aquí también tienes un artículo que seguramente te servirá: Porting Java Public Key Hash to C# .NET.
Suerte!
Gracias por los enlaces ya los checo
Hola, realmente te felicito por este aporte, es utilísimo, necesito hacer un proyecto en C# el cual coja ficheros XML, los comprima en Zip (uno a uno) y luego firme digitalmente de forma Detached (con PKCS7#) usando un CD Personal, creo que esto es con tu ejemplo «Otra opción, dependiendo de la forma en la que se quiere utilizar el resultado, es la de crear la firma solamente, sin el documento:» pero necesito hacerlo con ficheros en lugar que con texto, a medida que vaya avanzando te voy indicando del avance.
Un Saludo y vuelvo a agradecerte por tu aporte
Hola, Rich. Fíjate que en mi ejemplo el texto con convierto a un vector de bytes, ahí deberías poner los bytes de tu archivo en lugar de los del texto.
Acá te paso un ejemplo que debería funcionarte, deberías reemplazar la línea 8 por esto:
Espero que te sirva.
Suerte!
Hola Gustavo, ya comencé con el proyecto que te he comentado, voy a hacer una forma que muestre los CD y que Firme un String de Texto, y que haga l propio con un fichero, al tener algo avanzado te lo dejo saber por si alguien lo necesita.
un Saludo-
Muchas gracias!
Hola Gustavo, he hecho algunos avances, pero no logro generar el fichero .p7s, he logrado ubicar los CD, encriptar un String, pero no sé como hacer para generar el fichero .p7s detached, que es el que se debe enviar junto con el fichero original. te adjunto el código que llevo hasta ahora (de más está decir que estoy incursionando en esto del C# recientemente) y aprovecho para volver a agradecerte por tus aportes, por otro lado, el código me falla en la instruccion «CopyTo»,
Error 1 ‘System.IO.FileStream’ does not contain a definition for ‘CopyTo’ and no extension method ‘CopyTo’ accepting a first argument of type ‘System.IO.FileStream’ could be found (are you missing a using directive or an assembly reference?)
te dejo el Código:
¿Si guardás el contenido de la variable
bytSigned
no te funciona?Saludos.
Hola Gustavo muy bueno tu aporte. la verdad que lamento no haberlo encontrado cuando empeze a lidiar con certificados me hubiese ahorrado un par de dolores de cabeza. aunque todavia tengo uno.
Paso a comentarte cual es el Problema:
Debo firmar una parte de un xml que envio a una entidad, logro encriptarla y firmarla pero la misma me dice que la firma no es valida. El Certificado ya esta validado y llego al web services de ellos por lo que descarto que por el Certificado no llegue ya que recibo un error de que la firma no es valida por parte de ellos.
Estoy intentando hacerlo de esta manera
esto no produce errores ni nada sino que me devuelven como que la clave es invalida. Lo que busco hacer es poder levantar la clave privada de mi certificado para poder firmarla el tema es que no logro levantar dicha clave. Cualquier ayuda me sirve sin mas y agradeciendo por adelantado. te mando saludos
Hola, Sebastián, gracias por los cumplidos.
Sobre tu problema, puede ser que el CA del certificado con el que firmaste tus datos no esté instalado en un repositorio de confianza en el servidor destino? ¿Podés verificar la firma en tu máquina para ver si da algún error?
Suerte!
Hola, primero que nada fantástico articulo.
Estoy teniendo problemas a la hora de desencriptar un masaje mediante certificado, he seguido tus pasos pero obtengo este error cuando intento realizar el decrypt
El mensaje con datos envueltos no contiene el recipiente especificado.
Concretamente en la linea
objEncryptedData.Decrypt();
No entiendo en tu código porque cuando desencriptas no le indicas el certificado pero cuando encriptas si.
un saludo
Buenos dias, muchas gracias por ayudarnos son pocas las personas q comparten su conocimiento.
tengo un problema al generar la firma de un texto en este caso es un string de un conjunto de tag XML.
el codigo es el siguiente VB.NET
Mi problema radica en que el certificado al poseer un largo de llave 1024 como resultado me entrega 172 caracteres.
pero si no cargo la llave del certificado y genero un RSACryptoServiceProvider de 512(por defecto crea de 1024) asi q lo cambio RSACryptoServiceProvider(512). esto me genera mis 88 caracteres finales. pero obiamente no me sirve por que no se genera con las llaves del certificado.
estas son las lineas q ocupo la que esta con comentario es la que genera el RSACryptoServiceProvider con la llave del certificado (pero con 1024)
como puedo generar la firma pero con 512???
gracias de antemano.
Quisiera arreglar el mundo… pero no me dan el codigo fuente!
Excelente información, solo por favor, te lo ruego, cambia tu combinación de colores porque es cansadísimo leer con fondo negro.
Hola. Este sito me ayudo mucho.
Pero tengo que realizar los siguiente.
Localmente me funciona. estoy con vb.net.
pero ahora necesito que el usuario desde una aplicacion web me firme el documento pdf.
No he logrado realziar esto.
Mepodrian ayudar por favor.
Gracias.
Hola, Erika.
El problema con esto que querés hacer es que en el servidor no tenés la clave privada de los certificados del usuario, deberías pensar en hacerlo de alguna otra manera como una aplicación de escritorio o un flash o Silverlight donde el usuario debería elegir su certificado desde un archivo para poder utilizarlo.
Saludos.
Amigo tendras un ejemplo de como Firmar documentos pero con formato CAdES… o conoces algun LINK?
La verdad que nunca lo hice.
Saludos.
He encontrado este post el día de hoy , mientras que en la oficina de gran utilidad envió el enlace a mí mismo y lo más probable es marcar https://www.programandoamedianoche.com cuando hago lo que respecta mejor hogar
Excelente Articulo! Felicidades
yo tengo la misma duda que «jomarmen» pues quiero hacer mis pruebas y no me permite hacer el decifrado solo obtengo la excepcion
ASN1 bad tag value met.
Agradeceria su apoyo Saludos!
Hola! Gracias por el artículo! me sirvió muchísimo para enteder cómo funciona firma digital.
Estoy implementando una aplicación en el cliente para firmar y varios años después me encuentro con el mismo problema que Hernan
El mismo documento lo abro con Acrobat, y el documento verifica.
Para obtener el archivo en bytes utilizo la siguiente información:
Encontraron alguna solución?
Gracias!
Hola! Gracias por el artículo! me sirvió muchísimo para enteder cómo funciona firma digital.
Estoy implementando una aplicación en el cliente para firmar y varios años después me encuentro con el mismo problema que Hernan
Hola majo, para pasar un documento a array de bytes puedes usar:
byte[] bytDocFirmado = File.ReadAllBytes(fullPathArchivoFirmado);
A mí no me da el error de etiqueta ASN1 incorrecto…
Un saludo
Hola Gema!
Gracias por responder.
hice la prueba de utilizar el método «File.ReadAllBytes(fullPathArchivoFirmado);» y se produce la misma excepción.
hice la prueba de luego de obtener el arreglo de bytes, guardarlo con: File.WriteAllBytes(fullPathArchivoFirmado+»prueba»,bytDocFirmado);
y el documento de prueba es exactamente el mismo, incluso lo abro con acrobat reader y sigue verificado. La lectura de los bytes no es el problema.
=/
Por lo que veo es un documento PDF. Para validar la firma en PDFs yo uso itextsharp. Puedes ver un ejemplo de verificar firmar aquí: https://stackoverflow.com/questions/9823240/how-to-retrieve-digital-signature-information-name-date-with-itextsharp
Un saludo
Gracias por la ayuda!..
Te cuento las pruebas con el código que me pasaste que hice:
1ero) traté de verificar un pdf firmado con Acrobat Reader DC: Da la excepción Org.BouncyCastle.SecurityUtilityException: {"Signer 2.16.840.1.101.3.4.2.1WITH1.2.840.113549.1.1.11 not recognised."}
2do) traté de verificar un pdf firmado con itextsharp: y ya no dio la excepción. Este caso si anduvo.
Me preocupa un poco, se supone que puedo recibir documentos firmados con cualquier herramienta. Alguna idea de la excepción que describí?
bueno bueno.. Leyendo «https://stackoverflow.com/questions/22407897/pdf-digital-signature-in-java-and-signature-verification-in-c-sharp-itext» en uno de los comentarios recomienda actualizar la librería de itextSharp. Yo tenía la 4.1.2, así que actualicé a la versión 5.5.11 (https://www.nuget.org/packages/iTextSharp/5.5.11).
Y la verificación funcionó tanto para documentos firmados con itextsharp como el firmado por acrobat reader DC.
tuve que modificar la línea de código: «ArrayList names = af.GetSignatureNames();» por «List names = af.GetSignatureNames();» y «PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField(«C»)» aun tengo que ver como funciona en la nueva versión.
Pero es un paso enorme!
Gracias Chicos por la colaboración.
debes tener en cuenta que para leer debes usar la misma libreria que usaste para escribir, ese puede ser el motivo del error.
Hola Luis,
Para generar la firma estoy utilizando itextSharp: https://www.codeproject.com/articles/14488/e-signing-pdf-documents-with-itextsharp
por las dudas probé verificar un pdf firmado con acrobat reader DC y se produce el mismo error (descarto que sea itextsharp…)
si te sigue dando problemas aqui esta esta libreria que es muy buena la verdad y tiene cositas interesantes, ademas la licencia es libre si la compañia factura menos de $1’000.000 de dolares al año…. podrias probar a ver que tal
https://help.syncfusion.com/file-formats/pdf/working-with-digitalsignature
Gustavo: bajé el ejemplo hecho en c# y funciona perfecto!!
Mi pregunta es es posible hacer lo mismo con java. Tengo que desarrollar la firma de pdf utilizando si o si java.
He estado probando por semanas y no consigo dar con el resultado.
Tengo un certificado emitido por la onti, el cual instalé en mi pc.
Necesitaria firmar de la misma forma que lo estas haciendo en el ejemplo, es decir leer el repositorio de certificados, tomar uno y firmarlo introduciendo el pin o password de la clave privada.
Hasta ahora he conseguido leer el repositorio y tomar el ceritificado sin problemas, pero no he logrado o no comprendo como hacer para firmar con la clave privada solicitando el pin o password de la misma.
Podés ayudarme o darme una pista de adonde investigar?
Muchas gracias!!
Luis.
Nunca necesité hacerlo en Java, pero la librería Bouncy Castle (que es la misma que uso en C#) tiene su versión en Java: http://www.bouncycastle.org/java.html, seguro es un buen punto de partida para buscar cómo usar los certificados en Java.
Suerte!
buenas acabo de enterarme de tu pagina web y la verdad es que me parece excelente no sabia de mas personas interesadas en estos temas, aqui tienes un nuevo lector que seguira visitandote mensualmente.
[…] […]
Hola Gustavo,
muchas gracias por tu artículo.
En estos momentos estamos valorando un proyecto en el que el cliente desea que los usuarios puedan firmar algunos documentos digitalmente.
Esta aplicación será web y estará alojada en un servidor en que el se accederá desde algunos terminales, y desde estos, se debe poder firmar digitalmente.
El problema es que no acabo de entender aún como funciona esto, te explico:
En estos terminales habrá un usuario que atenderá a clientes, realizará las acciones oportunas en la aplicación web, y llegará el momento que se abrirá un documento PDF que necesita ser firmado digitalmente por el cliente. Esto lo hará el usuario en el terminal pero a través del DNI del cliente.
Ahora las preguntas:
Cada vez que se lea un DNI, se generará un certificado en ese terminal ? Si es así y debido a que existirán el de todos los clientes que hayan pasado, se deberá siempre seleccionar en la aplicación el que sea en ese momento no ?
Espero que hayas entendido el planteamiento para que me puedas ayudar un poco en como funciona esto según las necesidades de este proyecto.
Muchisimas gracias por tu artículo y tu ayuda.
GUSTAVO SOY UN ESTUDIANTE DE ULTIMO SEMESTRE DE LA CARRERA DE ING DE SISTEMAS, PARA MI PROYECTO DE GRADO ESTABA PENSANDO HACER UN SISTEMA WEB Q ADMINISTRE LA CORRESPONDENCIA INTERNA DE MI UNIVERSIDAD APLICANDO FIRMAS DIGITALES, LA VERDAD TU APORTE ME ABRIO UN POCO LA MENTE Y ME DIO MASOMENOS UNA IDEA DE COMO HACERLO, QUISIERA SABER SI PODRIAMOS COMUNICARNOS VIA MAIL PARA QUE PUEDA COMENTARTE DE LO Q ESTOY PLANTEANDO Y TAL VEZ ME PUEDAS DAR ALGUNOS CONSEJOS
MUCHAS GRACIAS DE ANTEMANO POR TU TIEMPO
No hay problema, te acabo de enviar un mail a tu cuenta.
Saludos!
Hola Gustavo,
Tengo un problema de encriptacion y buscando por la web me encontre tus grandes aportes sobre el manejo de certificados y quiero ver si me puedes ayudar o iluminarme, mi problema consiste en que mi solucion en el servidor funciona de maravilla, pero para guardar documentos en otra ruta utilice el impersonate en el web config, al generar este cambio ahora tengo problemas de que las llaves no existen, busque informacion y ya le di permisos a los certificados mediante la consola de mmc, pero aun asi me sigue mandando el error, espero puedas ayudarme o indicarme como puedo resolver mi problema, saludos.
An unhandled exception occurred and the process was terminated.
Application ID: /LM/W3SVC/2/ROOT
Process ID: 6292
Exception: System.Security.Cryptography.CryptographicException
Message: Keyset does not exist
StackTrace: at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.SafeKeyHandle._FreeHKey(IntPtr pKeyCtx)
at System.Security.Cryptography.SafeKeyHandle.ReleaseHandle()
at System.Runtime.InteropServices.SafeHandle.InternalFinalize()
at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
Hola, Ubaldo.
¿Es raro lo que te pasa, probaste dándole permisos también al usuario del IIS?
Hola Gustavo,
De hecho si le di los permisos y con esos trabaja de maravilla, la cuestion es que necesito guardar los documentos en un contenedor que no pertenece a ese servidor y cuando utilizo el impersonate para poner el usuario y que pueda guardar, es cuando me da los problemas y ya agregue mediante la consola del mmc el usuario de IIS, el Network Service y hasta el uduario que utilizo en el impersonate, pero me sigue dando el error, vi en algunos blog algo que se realiza mediante la clase cacls.exe, segun para al usuario darle permisos de escritura y ejecucion, pero hasta el momento sigo intentando
Pero hay algo que no entiendo: si el contenedor no pertenece a ese servidor, o sea que está en otro servidor, el usuario con el que hacés la impersonalización es de un dominio?
En efecto Gustavo, el contenedor esta en otro servidor y ambos estan en el mismo segmento de red y el usuario que quiero utilizar para el impersonate es un usuario de dominio
Es raro, disculpá pero no sé que puede hacer.
Por favor, cuando lo resuelvas, comentame cómo hcisite.
Suerte!
Hola Gustavo,
Soy de Ecuador y estoy tratando de firma un documento XML, tengo un avance del tema pero no consigo encontrar algunos valores en el certificado ejemplo «QualifyingProperties», o «Signedproperties». Por favor ayudame con un empujon para encontrar una solucion.
muchas gracias.
Te felicito por tu Blog…
La verdad que nunca necesité hacer eso.
¿Probaste de crear la firma utilizando Bouncy Castle?
Fijate que hay muchísimos ejemplos de esta librería en la red.
Suerte!
Hola Gustavo buena tarde,
tu página me ha servido mucho para aprender y comprender como funciona la criptografía, son excelentes aportes. Pero tengo una duda, si bien se puede cifrar con la llave publica y descrifrar con la privada.. existe el proceso contrario? cifrar con privada y descifrar con publica?.. Bouncy Castle tiene los metodos para hacerlo? espero puedas apoyarme a resolver esta duda.. es que esta libreria es enorme. Saludos 🙂
Hola, Silvia.
La verdad que no conozco una forma de «invertir» las claves, pero igual no entiendo para que podrías necesitar algo así, ya que en ese caso lo que cifres de forma «privada» lo podría ver todo el mundo, pudiéndolo descifrar con la clave pública.
Saludos.
te felicito y muchas gracias por la potencia de los datos !!!
Buenas, tengo una duda, para hacer el desarrollo funciono a la perfección, pero tengo el problema que cuando lo coloco en mi servidor, no lee los certificados del cliente! tenes algún consejo para eso???
Para leer los certificados del cliente utilizo StoreLocation.CurrentUser.
Gracias de antemano.
José, creo que el problema es que, cuando ejecutás tu aplicación en el IIS, el usuario que utiliza es el IUSR_XXX (XXX es el nombre de la máquina), por eso es que no encuentra el certificado.
Te recomiendo instalar el certificado a nivel de máquina, si es que esto no te trae ningún problema de seguridad (como que otro usuario en ese servidor pueda leer el certificado).
Suerte!
Gustavo antes que nada gracias por responder.
No se, si tenes idea si esto se puede usar para leer los certificados instalados en la computadora de cada usuario.
Te explico rápido lo que estoy haciendo.
Un usuario entra al sistema(ya teniendo su firma instalada en su propia pc), yo quiero leer su firma y entonces una vez que la leo proceso que digite su llave privada para poder validarla y ya con eso avanzo en mi proceso.
No se si tenes algún tipo de información o ayuda que me podas brindar, de antemano muchas gracias!!
José, en realidad el usuario ya ingresó una contraseña al logearse, ya que sino no podría usar los certificados de su repositorio.
De todas formas, la clave privada no es un texto que pueda usar, es un array de bytes.
Tal vez lo que podrías hacer es que el usuario ingrese un «password», lo firmes con su certificado y luego verifiqués, a través de la aplicación, si su contraseña es la correcta y si la firma digital corresponde al certificado que tenés guardado con suyo.
Suerte!
ok te entiendo! y de nuevo disculpa que siga con las pregs pero es que tengo esa duda!!
Hay alguna forma, con X509Store, que desde mi aplicación web (asp.net) que se encuentra sobre mi servidor, leer los certificados del usuario (que él tiene instalados en su pc)???
Esa es la base de lo que ocupo!
De nuevo perdona la molestia y gracias por las respuestas!!
Desde el IIS podés solicitar que el usuario ingrese a tu aplicación con un certificado digital, pero no podés leer la clave privada, sólo la pública, sino podrías hacer una suplantación de identidad.
Seguro estos links te pueden ayudar:
Cómo: Instalar certificados de cliente
IIS 7 Walkthrough: One to One Client Certificate Mapping Configuration.
Suerte!
Hola, estoy tratando de consumir un servicio que solicita autenticacion SSL TWO WAY,ya tengo mi certificado en el keystore y lo puedo levantar desde el codigo, como deberia hacer para enviar mi certificado autenticarme en el webservice para poder consumirlo. estoy trabajando con vb2008 en C#.
gracias
Hola, Andrés.
La verdad nunca tuve que hacer esto, pero seguro que estas páginas pueden serte de utilidad: WCF – 2 Way SSL Security using Certificates y Use Mutual SSL Authentication in WCF.
Suerte!
Realice el siguiente ejemplo http://www.codeproject.com/Articles/36683/9-simple-steps-to-enable-X-509-certificates-on-WCF?msg=4691986#xx4691986xx
Todo funciona de manera correcta en ambiente de desarrollo, pero al mover el certificado al servidor de produccion veo el siguiente error.
El conjunto de claves no existe
Descripción: Excepción no controlada al ejecutar la solicitud Web actual. Revise el seguimiento de la pila para obtener más información acerca del error y dónde se originó en el código.
«Detalles de la excepción: System.Security.Cryptography.CryptographicException: El conjunto de claves no existe»
En el servidor he puesto el certificado en el almacen de la computadora pero aun asi no funciona.
Al momento de exportar el certificado he exportado tanto clave publica como clave privada.
Buenas, solo una pequeña reseña para los que utilicen DNI electronico en España.
El que quiera firmar usando DNIe Debe usar el codigo con el modo silent en false.
Concretamente esta linea.
objSignedData.ComputeSignature(objSigner,false);
En otro caso no conseguirar liberar el certificado y no pedirá el PIN de acceso a la tarjeta.
Saludos.
Muchas gracias por el aporte!
Saludos.
Una consulta, en que tipo de aplicacion esta realizado estos ejemplos?? ASPX?? desktop?? consola??
El código que se ve en la página puede ejecutarse en cualquier contexto (asp.net, consola, etc.).
Los fuentes que están al final del artículo para descargarse tienen un ejemplo completo en WinForm.
Saludos.
Gustaba aun puedes ayudar
Buen día Gustavo. Excelente aporte, me ha ayudado mucho y prácticamnete todo el código me ha funcionado. Estoy probándolo en C# Visual Studio 2005 y solo tengo un error en la línea de desencriptación:
//Desencriptamos los datos
objEncryptedData1.Decrypt();
Ahí se genera un error:
Source: System.Security
Message: «Bad Length.\r\n»
La asignación de los datos ecriptados la tengo así:
byte[] bytDatos = bytResult; //Datos encriptados
El certificado está en un eToken PRO72k.
Alguna idea o guía del origen de este error?
Hola, Juan.
La verdad que no tengo idea del problema.
¿Podrás copiar el código completo para verlo y tratar de ayudarte?
Suerte!
Hola Gustavo, realmente muy buenos tus artículos, quisiera hacerte un par de consultas. Yo ya he hecho una aplicación windows para firmar documentos PDF. Las clases de .Net lo resuelven muy bien y se puede usar la clave privada sin problemas.
El inconveniente lo tengo a la hora de hacer una aplicación web, donde el documento se aloja en servidor y el certificado (con su clave privada) está en la PC del cliente.
Y la otra consulta es, cómo hacer más amplia la aplicación web para que el cliente pueda firmar desde cualquier lugar, llevando consigo, por ejemplo, un eToken.
Te agradeceré me puedas encaminar un poco con estos dos temas.
Saludos, Carlos
Hola, Carlos.
Te cuento que nosotros tuvimos el mismo problema para firmar un PDF con el certificado del usuario en una aplicación web.
Para resolverlo, creo yo, tenés dos posibilidades:
1- Pedirle al usuario que «seleccione» un archivo con su certificado, enviarlo al servidor y firmar el documento ahí. Obviamente esta opción no es viable debido a que estás llevando el certificado al servidor y podrías firmar 1000 cosas más, el usuario no creo que quiera correr el riesgo.
2- Hacer una aplicación o applet que corra en el cliente y firmar ahí el documento (esta es la que hicimos nosotros). En nuestro caso desarrollamos una aplicación con Silverlight que obtenía el header del PDF a firmar, lo recibía en el cliente, lo firmaba con el certificado de éste, lo enviaba al servidor, y ahí lo ensamblábamos con el resto del documento. Esto no fue una tarea sencilla, pero pudimos hacer que funcione.
Espero te sirvan las ideas.
Suerte!
Hola Gustavo.
Muchas Gracias por toda la información que has entregado.
Estoy terminando un proyecto de firma Electronica en docs PDF (aplicación web) donde los certificados están instalados en el servidor (no pudimos hacer que funcionara con los certificados instalados en el cliente).
Pero cuando la quisimos poner en el servidor de explotación, no firmaba y enviaba el siguiente error: «No se encuentra el certificado y la clave privada para el descifrado».
Por lo que he leído podría ser problemas de privilegios sobre los certificados. Trate de darle permisos en el servidor con winhttpcertcfg -g -c LOCAL_MACHINE\TrustedPeople -s «certificado» -a IIS_IUSRS, pero ni siquiera esta la carpeta del winhttpcertcfg.
Mis preguntas son: será efectivamente problemas de privilegio y si es asi como puedo darle permisos en el servidor.
Muchas Gracias.
Disculpas por la demora en responder.
¿Pudiste resolver el problema? Si no es así: ¿probaste de hacer que el application pool de tu aplicación web corra con un usuario que tenga permisos para ver los certificados en el servidor? (tal vez podrías usar el mismo usuario con el que instalaste los certificados) Asi podemos ver si es un tema de permisos.
Suerte!
[+] Gracias por el contenido me ha sido de mucha utilidad.
De nada, me alegra que haya sido de utilidad.
Gustavo solo una duda q tipo de licencia tiene esta libreria?? para uso comercial
En este artículo no utilicés librerías externas, sólo las clases que están incluidas en el .NET Framework.
Saludos.
Hola a todos
estoy utilizando visual studio community 2013 y me aparece que en la linea donde uso
using System.Security.Cryptography.Pkcs;
que el tipo o nombre del espacio de nombre «Pkcs» no existe en el espacio de nombres actual System.Security.Cryptography, sera por el tipo de visual studio que utilizo?
Gracias por su ayuda
Solucionado en la parte de proyecto, elije añadir referencia–>Framework–>System Security
Gracias por el dato!
Hola Gustavo,
Pregunta: La validación de firma, el encriptado y desencriptado funciona para un documento firmado con la FIEL?
Saludos
Hola, Karla.
La verdad que desconozco lo que es la FIEL. ¿Es una entidad? Si es así habría que ver qué tipo de certificado te brindan.
Saludos.
Buenos días,
Magnifico post!!
He creado los certificados con makecert en lugar de con xml y solo tengo un problemilla cuando intento desencriptar me aparece el siguiente error:
«Excepción no controlada del tipo ‘System.Security.Cryptography.CryptographicException’ en System.Security.dll
Información adicional: Clave incorrecta.»
¿sabes que puedo hacer para solucionarlo?
Gracias.
Saludos.
Buenos días, Teresa.
La pregunta obvia, pero necesaria, es: ¿Estás segura que estás utilizando la contraseña que utilizaste al crear el certificado?
Saludos.
Gracias!!!
Que tal quisiera realizar una consulta sencilla. En la parte de validar ceertificados como se pone el certificado ??
X509Certificate2 objCert = … //Acá tenemos que poner el certificado
desde visual???? Gracias de antemano
Hola Verónica.
Depende de dónde tengas tu certificado. Por ejemplo, si lo tenés como parte de otro archivo deberías ver cómo tomarlo de ahí, o si lo en un archivo lo podés leer de la siguiente manera:
Y si lo tenés con contraseña podés abrirlo así:
Saludos.
Estimado Gustavo. una pregunta : ¿como puedo hacer para que en lugar de X509Certificate2 objCert = new X509Certificate2(«archivo», «contraseña»);
pueda escojer un certificado instalado previamente en el almacen de certificados
Hola José.
Para mostrar el diálogo del sistema operativo que te permite seleccionar uno o varios certificados podés utilizar la clase X509Certificate2UI.
Sería reemplazar la linea
por esto:
Si necesitás que el usuario seleccione más de un certificado, reemplazá la definición de
objSelected
por esto:Espero te sirva.
¡Suerte!
Hola,
Tienes algún ejemplo de esto mismo, pero firmando un fichero XML
Hola Olga.
En esta página del MSDN tenés un ejemplo: Cómo: Firmar documentos XML con firmas digitales.
Suerte!
Si lo vi, pero no veo donde debo pasar la info del certificado,
Hola Olga.
Deberías obtener el objeto X509Certificate2 con la información de tu certificado, por ejemplo, leerlo desde un archivo utilizando la sobrecarga del construtor creada para eso.
Luego, con el objeto del certificado, deberías utilizar la clave privada del mismo (la podés obtener con la propiedad PrivateKey) en lugar de crear una nueva clave en la siguiente linea:
¡Suerte!
HOLA GUSTAVO: LOGRA HACER EL CERTIFICADO Y PONERLO EN UN PDF EXISTENTE… EL TEMA ES QUE NO SÉ COMO PUEDO HACER PARA ACOMODARLO (POSICIONARLO) EN EL PDF O CAMBIAR EL TAMANO DE LA FIRMA… TENES IDEA COMO SE HACE GRACIAS
Hola.
Hace unos años escribí otro artículo donde explico lo de firmar PDFs desde .NET: Cómo firmar un documento PDF desde C# con iTextSharp, y ahí podés ver que creo la variable
objStamper
que es un objetoPdfSignatureAppearance
, el cual representa la firma «visible» del documento. Probá haciendo modificaciones sobre ese objeto, por ejemplo, el métodoSetVisibleSignature
se utiliza para especificar el tamaño de la firma.Espero te sirva.
Saludos.
Sorry la pregunta pero soy muy nuevo en los certificados y quiero saber que cuando dice » //Acá tenemos que poner el certificado» el objCert.Subject
que me deja como este nombre del certificado (CN=IndexaSignCert) y al momento de hacer X509Certificate2 objCert = … //Acá tenemos que poner el certificado
me manda error.
Gracias
Buenos días Felipe.
Disculpame pero no entiendo la consulta. ¿Podrás pasarnos el código que estás usando para que lo podamos ver?
Saludos.
Hola gustavo muchas gracias por compartir esta informacion me fue muy util, ahora tengo una pregunta si quisiera consultar los certificados agregados al navegador desde una aplicacion mvc, sabes como seria?
y otra vez muchas gracias por el post.
Hola Luis.
Podrías consultar el certificado utilizado en el navegador utilizando esta linea:
Suerte!
ya eso lo probe pero llega vacio, mira te explico la necesidad para que tengas una idea, lo que quiero hacer es que el usuario elija de las firmas que tiene agregadas al navegador con cual desea firmar el documento.
con el archivo fisico ya lo esta haciendo pero ahora en este punto estoy estancado he estado leyendo mucho pero nada no encuentro solucion
El problema es que no vas a poder hacerlo como estás pensando, porque con el navegador podés hacer que el usuario elija un certificado para autenticarse contra el IIS, pero no vas a poder leer la clave privada de éste para firmar un documento.
La única manera de hacerlo es utilizando algo externo, como un applet, plugin del browser, ejecutable o algo parecido. Hace unos años habíamos hecho un componente en Silverlight que leía el certificado y firmaba un PDF del lado del cliente, deberías crear algo así (obviamente no con Silverlight, que ya es obsoleto).
Suerte!
gracias gustavo, mira te dejo este documento que me pareci muy bueno y me parece que es el futuro del manejo de firmas, ademas es de la w3c 🙂
https://www.w3.org/TR/WebCryptoAPI/
GRACIAS!
Muchas gracias por el link, parece bueno, lástima que recién hace un mes que está esta «recomendación» 🙁
Saludos.
Excelente Post, gracias por compartir tu conocimiento.
Hola Gustavo, tengo una duda, como se podria firmar con contraseña pero una imagen o otro documento que no sea PDF.
Gracias
Buenos días Gustavo, saludos a todos con estas líneas; leyendo tu código he podido firmar un documento PDF incluyendo la firma en el mismo haciéndola visible o invisible. El tema que tengo es el siguiente: La necesidad de firmar varios archivos pdf y/o xml, el certificado lo selecciono de mi repositorio, pero tiene una clave privada, al hacerlo con una sólo documento, no hay problema que se pida la clave, pero al ser varios hay alguna forma de almacenar esa clave ingresada una única vez? e ir firmando uno a uno los documentos. Gracias de antemano por la atención. Tus artículos con excelentes y de mucha ayuda.
Buenas tardes!
La contraseña utilizada para abrir un certificado se puede especificar en el constructor de la clase
X509Certificate2
, por lo cual, podrías crear un diálogo para consultarle al usuario la contraseña, y guardarla en una variable para usarla en cada firma que debas hacer.Espero te sirva.
Saludos.
Hola, buenos días.
Estoy intentado coger el certificado digital que hay instalado en el navegador pero no lo consigo, solo consigo mostrar el de windows,
En Firefox no tengo instaldo ningún certificado, sin embargo me muestra el de windows.
Me podéis ayudar.
Gracias
yo intentaba hacer lo mismo, y no se puede esta fue la respuesta por si te interesa, y arriba esta toda la conversacion, como recomendacion lee los comentarios antes de preguntar para que no se repitan las preguntas.
«El problema es que no vas a poder hacerlo como estás pensando, porque con el navegador podés hacer que el usuario elija un certificado para autenticarse contra el IIS, pero no vas a poder leer la clave privada de éste para firmar un documento.
La única manera de hacerlo es utilizando algo externo, como un applet, plugin del browser, ejecutable o algo parecido. Hace unos años habíamos hecho un componente en Silverlight que leía el certificado y firmaba un PDF del lado del cliente, deberías crear algo así (obviamente no con Silverlight, que ya es obsoleto).
Suerte!»
Ya lo he leído, pero yo no quiero leer la clave privada, solo quiero acceder al certificado para autentificarse.
Desde mi equipo accedo a los certificados de Windows sin problema, pero cuando lo ejecuta el servidor no puedo acceder.
Gracias y perdón si me repito
listo, igual creo que si lees desde el cliente lo que puedes consultar son los certificados ssl, mas no los certificados digitales del cliente que tiene en el navegador, hay un proyecto de la w3c para esta necesidad pero aun se esta a la espera de este.
Si encuentras algun modo de hacer que funcione, no dudes en compartirlo
Hola como se podria firmar en formato CAdES?
Buenas tardes, para mandar el texto encriptado a un pdf, como podría hacerle. Quiero que el sello digital se muestre en un documento pero generado desde asp
Hola Gustavo, al momento de correr el código tengo el siguiente problema, al llegar a la línea objSignedData.ComputeSignature(objSigner), emite el error de: la clave no existe. qué podría ser?, considerando que tengo un archivo .crt y un .key ambos en una carpeta y el archivo a firmar en otra.
Hola Felix.
Seguramente el problema esa que el crt no tenga la clave privada. ¿Puede ser?
Saludos.
Buenas tardes primero agradecerles por su aporte de conocimientos y ala vez consultarles si tienes procedimientos para visual basic .net, estaria muy agradecido
Buenas tardes José.
No tenemos los ejemplos en VB.NET, pero son los mismos objetos en los mismos namespaces, sólo cambia un poco la sintaxis.
Saludos.
Como puedo reconocer un ejecutable con firma digital y comprobar que este no ha sido violado. Por favor si me pudieran orientar, donde puedo conseguir información en VB net
Hola Adolfo.
Deberías llamar al método WinVerifyTrust de Wintrust.dll, que está presente desde Windows XP.
En esta página (pinvoke.net: winverifytrust (wintrust)) tenés un ejemplo de cómo utilizarla desde C#, aunque en VB.NET es lo mismo, sólo cambia un poco la sintaxis.
Suerte!
Muy Agradecido Gustavo, gracias por responder.
Buenas tardes hay alguna manera de contactarme con usted? Gustavo Cantero.
Hola Raúl.
Acabo de enviarte un mail.
Saludos.
Hola Gustavo, buenas tardes. Antes que nada excelente artículo.
Una duda, al igual que otro compañero, me sale el error en objSignedData.ComputeSignature(objSigner) error de: la clave no existe. Y revisando tu respuesta anterior, mi archivo .cer no tiene la llave privada, la tengo en otro archivo a parte con extensión .key. (con su respectivo password). Mi pregunta es, como puedo anexarlo a mi archivo .cer? He visto que tal vez pueda hacer una copia con certificate= certificate.CopyWithPrivateKey(key); pero desconozco como pasarle mi .key a dicho método. Te lo agradezco mucho!
Hola Hugo.
Acá dan algunos ejemplos de cómo leer la clave privada desde el archivo .key: How To Read .Key file on C#?.
¡Suerte!
Buenas tardes, Muy interesante el artículo.
Por favor, quisiera tu apoyo con un problema que no he podido resolver hasta ahora,
Tengo un aplicación en VB.net que realiza la firma de archivos xml con el certificado digital, todo ha estado bien por mas de 2 años, pero ahora se ha puesto lento al momento de firmar el xml, justo la demora es en «objChain.Build(objCert)», antes no demoraba y ahora demora mas de 10 segundos en este punto.
No se a que se debe. Espero puedas ayudarme, gracias.