Twitter Facebook RSS Feed

Utilizar certificados digitales desde .NET

62
.NETCertificados DigitalesSeguridad

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);

//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

VN:F [1.7.3_972]
Rating: 9.6/10 (24 votos cast)

62 comentarios »

  1. Santiago dice:

    Excelente serie. De mucha utilidad

    VA:F [1.7.3_972]
    Rating: 4.5/5 (2 votos cast)
  2. Santiago dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • 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!

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
  3. Santiago dice:

    Gracias por responder. El problema estaba en los permisos.
    Saludos

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  4. Jonathan dice:

    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

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  5. jonas13 dice:

    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.

    VN:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  6. Leonardo Quintana dice:

    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…

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • 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:

      • La PC donde está corriendo la aplicación no tiene conexión a internet
      • El servidor donde está la lista está caido o ya no existe
      • Demora mucho en obtener la lista. Para esto puedes aumentar el tiempo modificando la propiedad UrlRetrievalTimeout asi: objChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 3, 0);

      Cualquier otra consulta no dudes en escribirnos.
      Suerte!

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
  7. Leonardo Quintana dice:

    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

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • 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.

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
  8. Jonathan dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • Dario Krapp dice:

      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.

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
  9. Leonardo Quintana dice:

    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…

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  10. juan dice:

    Hola, queria saber como levantar una clave privada que se encuentra en un archivo.p12 desde .net? desde ya muchas gracias

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  11. 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 datos
    X509Certificate2 objCert = new X509Certificate2(bytCert);

    Espero que esta información te sea de utilidad, cualquier otra cosa no dudes en escribir.
    Suerte!

    VN:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  12. Fredys Simanca dice:

    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,

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • 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.

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
  13. Luis dice:

    Hola, muy interesante tu publicación, me gustaría saber como puedo encriptar documentos en pdf con un certificado digital, muchas gracias.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  14. Antonio Manrique dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  15. Alberto Pérez dice:

    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?

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  16. Alberto Pérez dice:

    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…

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  17. Alberto Pérez dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  18. Alberto Pérez dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  19. beto hdez dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  20. beto hdez dice:

    Gracias por los enlaces ya los checo

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  21. Rich Rod dice:

    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

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • 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!

      VN:F [1.7.3_972]
      Rating: 5.0/5 (1 voto cast)
  22. Rich Rod dice:

    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-

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  23. Rich Rod dice:

    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());
    }
    }
    }
    }

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  24. Sebastian dice:

    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

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • 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!

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
  25. jomarmen dice:

    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

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  26. reinaldovb dice:

    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!

    VN:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  27. Rolivar Peraza dice:

    Excelente información, solo por favor, te lo ruego, cambia tu combinación de colores porque es cansadísimo leer con fondo negro.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  28. Erika dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • 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.

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
  29. 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

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  30. Hernan dice:

    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!

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  31. Luis dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  32. senavirtual dice:

    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.

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  33. HENRY dice:

    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

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
  34. Ubaldo dice:

    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)

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)
    • Hola, Ubaldo.
      ¿Es raro lo que te pasa, probaste dándole permisos también al usuario del IIS?

      VN:F [1.7.3_972]
      Rating: 0.0/5 (0 votos cast)
      • Ubaldo dice:

        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

        VA:F [1.7.3_972]
        Rating: 0.0/5 (0 votos cast)
        • 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?

          VN:F [1.7.3_972]
          Rating: 0.0/5 (0 votos cast)
          • Ubaldo dice:

            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

            VA:F [1.7.3_972]
            Rating: 0.0/5 (0 votos cast)
  35. Leonel dice:

    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…

    VA:F [1.7.3_972]
    Rating: 0.0/5 (0 votos cast)

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Buscar