C/C++/C#

La clase OCSP mostrada a continuación contiene el código para realizar peticiones OCSP. La función OCSP_validation lleva a cabo la validación OCSP de un certificado del DNIe.

 Class OCSP { /* * @fn OCSP_validation lleva a cabo la validación OCSP de un certificado del DNIe * * @return OCSPStatus: devolverá el estado del certificado según * la petición OCSP (Good, Revoked, Unknown o Invalid) * */ public OCSPStatus OCSP_validation(X509Certificate2 clientCertificate, out string respMsg) { String certPath = ""; X509CertificateParser issuerCertParser = new X509CertificateParser(); /*Se determina la CA suboordinada del certificado del DNIe del cliente que se ha conectado*/ if (clientCertificate.Issuer.Contains("CN=AC DNIE 001")) certPath = "..\\..\\Resources\\ACDNIE001-SHA1.crt"; else if (clientCertificate.Issuer.Contains("CN=AC DNIE 002")) certPath = "..\\..\\Resources\\ACDNIE002-SHA1.crt"; else if (clientCertificate.Issuer.Contains("CN=AC DNIE 003")) certPath = "..\\..\\Resources\\ACDNIE003-SHA1.crt"; FileStream fs = new FileStream(certPath, FileMode.Open); Org.BouncyCastle.X509.X509Certificate issuerCert = issuerCertParser.ReadCertificate(fs); fs.Close(); /*Se genera la petición OCSP con el cetificado de la entidad emisora del certificado y el número de serie del certificado cliente*/ OcspReqGenerator ocspReqGen = new OcspReqGenerator(); Org.BouncyCastle.X509.X509Certificate X509ClientCert = new X509CertificateParser().ReadCertificate(clientCertificate.RawData); ocspReqGen.AddRequest(new CertificateID(CertificateID.HashSha1, issuerCert, X509ClientCert.SerialNumber)); OcspReq ocspReq = ocspReqGen.Generate(); byte[] reqData = ocspReq.GetEncoded(); /* Se establece la connexión HTTP con el ocsp del DNIe */ HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create("http://ocsp.dnie.es"); httpReq.Method = "POST"; httpReq.ContentType = "application/ocsp-request"; httpReq.ContentLength = reqData.Length; /* Se escribe la petición */ Stream reqStream = httpReq.GetRequestStream(); reqStream.Write(reqData, 0, reqData.Length); reqStream.Close(); HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse(); /* Se lee la respuesta */ Stream respStream = new BufferedStream(httpResp.GetResponseStream()); OcspResp ocspResponse = new OcspResp(respStream); /*Se verifica el estado del certificado*/ if (ocspResponse.Status == 0) { CertificateID certID = new CertificateID(CertificateID.HashSha1, issuerCert, X509ClientCert.SerialNumber); BasicOcspResp basicResp = (BasicOcspResp) ocspResponse.GetResponseObject(); SingleResp[] singleResps = basicResp.Responses; foreach (SingleResp singResp in singleResps) { CertificateID respCertID = singResp.GetCertID(); if (certID.Equals(respCertID)) { Object status = singResp.GetCertStatus(); if (status == CertificateStatus.Good) { respMsg = "Validación OCSP (http://ocsp.dnie.es): El certificado es válido"; return OCSPStatus.Good; } else if (status is Org.BouncyCastle.Ocsp.RevokedStatus) { respMsg = "Validación OCSP (http://ocsp.dnie.es): El certificado ha sido revocado"; return OCSPStatus.Revoked; } else { respMsg = "Validación OCSP (http://ocsp.dnie.es): El estado del certificado desconocido"; return OCSPStatus.Unknown; } } else respMsg = "El estado del certificado no es válido"; } } respMsg = "Estado de la petición OCSP inválido"; return OCSPStatus.Invalid; } public enum OCSPStatus : int { Good = 0, Revoked = -1, Unknown = -2, Invalid = -3 } } 

A continuación se proporciona el código de la clase CertValidator comprueba que un certificado no haya expirado en función de la fecha del certificado y la actual. Devuelve el estado del certificado.

 class CertValidator { /** * @fn DateValidation comprueba que un certificado no haya expirado en función de la fecha actual * y la fecha de expiración del mismo * * @return CertificateDateStatus: valid si aúno no ha expirado o invalid si ya lo ha hecho **/ public CertificateDateStatus DateValidation(X509Certificate2 cert, out string responseMsg) { /*Se compara la fecha actual con la de expiración y la fecha a partir de la cual *el certificado es válido */ if ((DateTime.Compare(DateTime.Now, cert.NotBefore)) >= 0 && (DateTime.Compare(DateTime.Now, cert.NotAfter) <= 0)) { responseMsg = "El certificado es válido (desde el" + cert.NotBefore + " hasta el " + cert.NotAfter + ")"; return CertificateDateStatus.Valid; } else { responseMsg = "El certificado no es válido (desde el" + cert.NotBefore + " hasta el " + cert.NotAfter + ")"; return CertificateDateStatus.NotValid; } } public enum CertificateDateStatus : int { NotValid = -1, Valid = 0, } } 

class CertManager
{
/**
* @fn getDNIeCerts accede al contenedor personal de certificados
* 
* @return X509Certificate2: es el certificado seleccionado por el usuario, 
* En el caso del DNIe será el certificado de Autenticación o el de Firma
**/
public X509Certificate2 getDNIeCerts()
{
//Se obtiene el contenedor Personal de certificados
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
//Se abre el contenedor con permisos de lectura
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
/*Se filtran los certificados para obtener únicamente los certificados del 
* DNIe (cualquiera de las tres Autoridades de Certificación Subordinadas*/
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByIssuerName, "AC DNIE 001", true);
int numcerts = fcollection.Count;
if (numcerts == 0)
{
fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByIssuerName, "AC DNIE 002", true);
numcerts = fcollection.Count;
if (numcerts == 0)
fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByIssuerName, "AC DNIE 003", true);
}

X509Certificate2Collection scollection = null;
/*Si se han encontrado los certificados se muestra una ventana gráfica que permitirá al usuario 
* seleccionar uno de los dos certificados del DNIe*/
if (fcollection.Count != 0)
scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Visualizador de Certificados DNIe", "Seleccione uno de los certificados de su DNIe", X509SelectionFlag.MultiSelection);
else 
return null;

//Se recupera el certificado seleccionado
X509Certificate2 certificate = scollection[0];

//Se cierra el contenedor
store.Close();
return certificate;
}
}

El siguiente código busca dentro del DNIe los objetos del tipo clave privada, devolviendo un puntero a la clave para poder utilizarla.

 /*En este caso, al trabajar con DNIe, sólo contiene 2 claves, por lo tanto, se crean dos manejadores (o handles)*/ CK_OBJECT_HANDLE hObject[2] = {CK_INVALID_HANDLE, CK_INVALID_HANDLE}; CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY; /*El tipo de objeto que se quiere buscar*/ CK_ULONG ulCount2; /*Otro long*/ /*Se define un template que es el que define el atributo que debe hacer coincidir (match)*/ CK_ATTRIBUTE template1[] = { { CKA_CLASS, &objClass, sizeof (objClass) } }; /*ulTemplateCount es el número de atributos que deben coincidir (matching), en este caso 1.*/ CK_ULONG ulTemplateCount = sizeof (template1) / sizeof (template1[0]); rv = C_FindObjectsInit(hSession, template1, ulTemplateCount); if(rv!=CKR_OK) return rv; /*Si hay error se devuelve*/ rv = C_FindObjects(hSession, hObject, 2, &ulCount2); if(rv!=CKR_OK) return rv; /*Si hay error se devuelve*/ rv = C_FindObjectsFinal(hSession); if(rv!=CKR_OK) return rv; /*Si hay error se devuelve*/ 

El siguiente código para abrir sesión con la tarjeta y la presentación del pin a la tarjeta para desbloquear los objetos.

 slotID = SlotList[0]; //se selecciona el primer token rv = C_OpenSession(slotID,CKF_SERIAL_SESSION|CKF_RW_SESSION,NULL_PTR,(CK_NOTIFY)NULL_PTR,&hSession); //se inicia session if (rv != CKR_OK) return rv; /*Si hay error se devuelve*/ char * Pin = "pin_a_presentar"; /* Se guarda el PIN en una cadena*/ rv = C_Login(hSession,CKU_USER, (unsigned char *) Pin,strlen(Pin)); if (rv != CKR_OK) return rv; /*Si hay error se devuelve*/ 

A continuación se muestra un fragmento de código que obtiene un array con los lectores conectados al PC donde se ejecuta la aplicación.

 /* Se obtiene el listado de slots con puntero a NULL*/ rv = C_GetSlotList(CK_FALSE, NULL_PTR, &ulCount); if (rv == CKR_OK) { /* Se reserva memoria para ulCount*/ SlotList = (CK_SLOT_ID_PTR) malloc(ulCount * sizeof(CK_SLOT_ID)); rv = C_GetSlotList(CK_FALSE, SlotList, &ulCount); if (rv != CKR_OK) return rv; /*Si hay error se devuelve*/ 

El siguiente fragmento de código declara e inicializa las variables necesarias para realizar operaciones con PKCS#11.

 CK_RV rv; /*Se define un valor de retorno*/ CK_SLOT_ID slotID;/*Se define el slotID que se utilizará*/ CK_SLOT_ID_PTR SlotList;/*Se define un puntero a una lista de Slots */ CK_ULONG ulCount;/*Se define un Long donde se guardará el número de slots */ CK_SESSION_HANDLE hSession; /*Se define una sesión*/ Una vez definidas todas las variables se inicializa el módulo. Para ello se debe llamar a la función C_Initialize(): rv = C_Initialize(NULL_PTR); if(rv!=CKR_OK) //se comprueba si se ha inicializado correctamente el módulo return rv; //en caso contrario se devuelve error 

29/09/11 17:09

Firma de un fichero


RSACryptoServiceProvider Firmador = new RSACryptoServiceProvider();

FileStream file = new FileStream(args[0], FileMode.Open, FileAccess.Read);
BinaryReader reader = new BinaryReader(file);
byte[] data = reader.ReadBytes((int)file.Length);

byte[] signature = Firmador.SignData(data);

Console.WriteLine("FIRMA: " + Convert.ToBase64String(signature));
reader.Close();
file.Close();

El método SCardTransmit permite el envío de comandos APDU a la tarjeta, conociendo estos comandos específicos para cada tarjeta podemos realizar operaciones a nivel bajo con la misma.

 *Se selecciona el directorio PKCS#15 5015*/ if( SCardTransmit( hCard, SCARD_PCI_T0,(const unsigned char *) "\x00\xA4\x00\x00\x02\x50\x15", 7, NULL, szBuffer, &dwLength ) != SCARD_S_SUCCESS ) { SCardReleaseContext(hSC); return -1; } /*Si devuelve algo distinto de 61 significa que ha habido un error*/ if(szbuffer[0]!=61) return -1; /*Se selecciona el Certificate DF del DNIe 6004*/ if( SCardTransmit( hCard, SCARD_PCI_T0,(const unsigned char *) "\x00\xA4\x00\x00\x02\x60\x04", 7, NULL, szBuffer, &dwLength ) != SCARD_S_SUCCESS ) { SCardReleaseContext(hSC); return -1; } /*Si devuelve algo distinto de 61 significa que ha habido un error*/ if(szbuffer[0]!=61) { SCardReleaseContext(hSC); return -1; } /*Se leen FF bytes del archivo*/ if( SCardTransmit( hCard, SCARD_PCI_T0,(const unsigned char *) "\x00\xb0\x00\x00\xff", 5, NULL, szBuffer, &dwLength ) != SCARD_S_SUCCESS ) { SCardReleaseContext(hSC); return -1; } 

El siguiente fragmento de código comprueba que la tarjeta insertada en el lector es un DNI electrónico. El DNIe genera una misma respuesta siempre que recibe una señal de reset. Conociendo esa respuesta (ATR, Answer To Reset) del DNIe se compara con la respuesta generada por una tarjeta inteligente, si coincide es que es un DNI electrónico.

BOOL isDNIe( SCARDHANDLE hCard )
 {
 unsigned char * bAtr = NULL;
 DWORD dwAtrLen = 0;
 
 if( SCardStatus( hCard, NULL, NULL, NULL, NULL, bAtr, &dwAtrLen) != SCARD_S_SUCCESS ) {
 SCardReleaseContext(hSC);
 return FALSE;
 }
 if(dwAtrLen != 20)
 return FALSE;
 
 bAtr = (unsigned char *) malloc (sizeof(unsigned char *) * dwAtrLen);
 memset(bAtr,0,sizeof( unsigned char *) * dwAtrLen);
 if( SCardStatus( hCard, NULL, NULL, NULL, NULL, bAtr, &dwAtrLen) != SCARD_S_SUCCESS ) {
 SCardReleaseContext(hSC);
 return FALSE;
 }
 
 if( !(bAtr[0]==0x3B && bAtr[1]==0x7F && bAtr[3]==0x00 && bAtr[4]==0x00 && bAtr[5]==0x00 && bAtr[6]==0x6A && bAtr[7]==0x44 && bAtr[8]==0x4E && bAtr[9]==0x49 && bAtr[10]==0x65 && bAtr[18]==0x90 && bAtr[19]==0x00 ) )
 return FALSE;
 
 return TRUE;
 }