Utilizar certificados digitales desde .NET
62Viernes, 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);
//Firmamos 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!
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:
objChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 3, 0);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:
X509Certificate2 objCert;X509Store objStore = new X509Store(StoreLocation.CurrentUser);
X509Certificate2Collection objCol = objStore.Certificates.Find(X509FindType.FindByIssuerName, "Gustavo", true);
if (objCol.Count > 0)
{
objCert = objCol[0];
//Sigo usando el certificado
}
Otra posibilidad sería abrir el certificado desde un archivo:
X509Certificate2 objCert = new X509Certificate2("Archivo.pfx");o también podrias leer el certificado desde la base de datos u otro lado:
byte[] bytCert = //Obtengo el certificado de algún lado, como la base de datosX509Certificate2 objCert = new X509Certificate2(bytCert);
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, 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:
byte[] bytes;
using (FileStream In = File.OpenRead(“c:\archivo.bin”))
using (MemoryStream MS = new MemoryStream())
using (GZipStream Zip = new GZipStream(MS, CompressionMode.Compress, true))
{
In.CopyTo(Zip);
bytes = MS.GetBuffer();
}
ContentInfo objContent = new ContentInfo(bytes);
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:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;
using System.Security.Cryptography.Pkcs;
using System.IO;
using System.IO.Compression;
namespace form
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
X509Store objStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); //StoreLocation.LocalMachine);
objStore.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 objCert in objStore.Certificates)
rtxtCerts.Text = rtxtCerts.Text + objCert.SubjectName.Name + “: ” + objCert.Thumbprint + “\n”;
objStore.Close();
}
private void btnExcrypt_Click(object sender, EventArgs e)
{
try
{
X509Certificate2 objCert = new X509Certificate2(@”C:\Documents and Settings\USERNAME\Datos de programa\Microsoft\SystemCertificates\My\Certificates\THECERTIFICATECODE”);
ContentInfo objContent = new ContentInfo(Encoding.ASCII.GetBytes(txtSource.Text));
SignedCms objSignedData = new SignedCms(objContent, true);
CmsSigner objSigner = new CmsSigner(objCert);
objSignedData.ComputeSignature(objSigner);
byte[] bytSigned = objSignedData.Encode();
rtxtEncrypted.Text = Convert.ToBase64String(bytSigned);
}
catch (Exception ex)
{
MessageBox.Show(“Exception caught ” + ex.ToString());
}
}
private void btnCryptFile_Click(object sender, EventArgs e)
{
try
{
MemoryStream destination = new MemoryStream();
X509Certificate2 objCert = new X509Certificate2(@”C:\Documents and Settings\rerodriguez\Datos de programa\Microsoft\SystemCertificates\My\Certificates\THECERTIFICATECODE”);
using (FileStream fsSource = new
// File 2 Encrypt (pedido en la forma)
FileStream(txtFile2Crypt.Text, FileMode.Open, FileAccess.Read))
using (MemoryStream MS = new MemoryStream())
using (GZipStream Zip = new GZipStream(MS, CompressionMode.Compress, true))
{
// fsSource.CopyTo(Zip); Error no le gusta
byte[] bytes = new byte[fsSource.Length];
int numBytesToRead = (int)fsSource.Length;
int numBytesRead = 0;
while (numBytesToRead > 0)
{
int n = fsSource.Read(bytes, numBytesRead, numBytesToRead);
if (n == 0)
break;
numBytesRead += n;
numBytesToRead -= n;
}
numBytesToRead = bytes.Length;
ContentInfo objContent = new ContentInfo(bytes);
SignedCms objSignedData = new SignedCms(objContent, true);
CmsSigner objSigner = new CmsSigner(objCert);
objSignedData.ComputeSignature(objSigner);
byte[] bytSigned = objSignedData.Encode();
rtxtEncrypted.Text = “Fichero Firmado”; // Convert.ToBase64String(bytSigned);
}
}
catch (Exception ex)
{
MessageBox.Show(“Exception caught here” + ex.ToString());
}
}
}
}
¿Si guardás el contenido de la variable
bytSignedno 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
Dim hasher As New SHA1CryptoServiceProvider()
Dim _Certificado As New X509Certificate2(“certificado”, “pass”)
‘CREO OBTENER LA CLAVE PRIVADA PARA LUEGO CON EL OBJETO rsa poder firmar
Dim rsa As RSACryptoServiceProvider = _Certificado.PrivateKey
Dim msbyt() As Byte
Dim byteconverter As New ASCIIEncoding()
msbyt = byteconverter.GetBytes(“XML a encriptar”)
Dim hshbyt() As Byte
Dim sha As New SHA1CryptoServiceProvider()
hshbyt = sha.ComputeHash(msbyt)
Dim sgnbyt() As Byte
‘FIRMO EL DOCUMENTO
sgnbyt = rsa.SignHash(hshbyt, “SHA1″)
Return Convert.ToBase64String(sgnbyt)
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
Dim _Certificado As New X509Certificate2("C:\certificadodigital.pfx", "1234")
'Dim rsa As RSACryptoServiceProvider = _Certificado.PrivateKey
Dim rsa As New RSACryptoServiceProvider(512)
Dim msbyt() As Byte
Dim byteconverter As New ASCIIEncoding()
msbyt = byteconverter.GetBytes("Texto a Encriptar")
Dim hashbyte() As Byte
Dim sha As New SHA1CryptoServiceProvider()
hashbyte = sha.ComputeHash(msbyt)
Dim sgnbyt() As Byte
'FIRMO EL DOCUMENTO
sgnbyt = rsa.SignHash(hashbyte, "SHA1")
Debug.Print (Convert.ToBase64String(sgnbyt))
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)
'Dim rsa As RSACryptoServiceProvider = _Certificado.PrivateKey
Dim rsa As New RSACryptoServiceProvider(512)
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.
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 http://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!
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.
[...] [...]
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!