Procedimientos de creación, almacenamiento, y utilización de los componentes criptográficos de los certificados de entidad final Esta especificación ha sido preparada por ANF AC para liberar a terceras partes. NIVEL DE SEGURIDAD DOCUMENTO PÚBLICO Este documento es propiedad de ANF Autoridad de Certificación. Está prohibida su reproducción y difusión sin autorización expresa de ANF Autoridad de Certificación -Copyright © ANF Autoridad de Certificación ÍNDICE 1. Introducción ............................................................................... 3 2. Características técnicas de la librería utilizada ................................. 4 2.1 Arquitectura .................................................................................... 4 2.2 Interface ....................................................................................... 5 2.3. Manejo de los objetos criptográficos .................................................. 6 3. Análisis de los procedimientos ....................................................... 7 3.1 Calidad del material criptográfico ....................................................... 7 3.2 Aleatoriedad .................................................................................... 9 3.3 Generación de claves ...................................................................... 11 3.3.1 Claves RSA ................................................................................. 12 3.3.2 Claves de sesión .......................................................................... 15 3.4 Almacenamiento de material sensible ............................................... 17 3.5 Inicialización y cierre ...................................................................... 17 3.6 Gestión de errores ......................................................................... 18 3.7 Proceso de generación de petición de certificado................................. 19 3.8 Importación de certificados ............................................................. 21 3.9 Proceso de firma ............................................................................ 22 3.10 Proceso de verificación .................................................................. 24 4. Consideraciones adicionales ........................................................ 26 5. Referencias .............................................................................. 27 Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 2 de 27 1. Introducción En primer lugar cabe reseñar que ANF AC, tal y como consta en la Declaración de Practicas de Certificación, genera certificados raíz y certificados de CA intermedios para su actividad como prestador de servicios de certificación. Los certificados raíz solo son utilizados para generar los certificados de CA intermedios, y su uso requiere una autorización expresa de la Junta de Gobierno de ANF Autoridad de Certificación. Un manual completo de procedimientos de utilización de módulos criptográficos requeriría una descripción detallada de los algoritmos criptográficos utilizados por los mismos. En este caso, sin embargo, se ha seguido la máxima, que se suele repetir de forma recurrente en el mundo de la seguridad informática, de utilizar procedimientos e implementaciones de reconocido prestigio y cuyo contenido haya sido sometido a una supervisión pública. Ese planteamiento se ha traducido en la utilización de algoritmos criptográficos abiertos RSA. Estos algoritmos cumplen los planteamientos expuestos anteriormente. Por otro lado, y como se verá posteriormente en el apartado de análisis, los algoritmos criptográficos, los formatos y los protocolos y procedimientos de intercambio, almacenamiento y gestión de claves y certificados siguen los estándares internacionales más extendidos. El enfoque que se va a seguir en este documento de los procedimientos seguidos por ANF AC en el desarrollo de sus dispositivos de certificación raíz e intermedios, hace especial énfasis en asegurar el material sensible, habiendo creado un entorno de programación “estanco”. Dos zonas de programación con equipos de ingeniería sin posibilidad de contacto entre sí. Uno, formado por especialistas en criptografía, dedicado a la elaboración y el mantenimiento de la librería criptográfica y el segundo, construyendo el interfaz de usuario. Se comprobará que se ha respetado ese enfoque y que se hace una utilización eficiente del interfaz, así como que se es consciente del funcionamiento interno de la misma y que la configuración de parámetros que se utiliza es correcta. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 3 de 27 2. Características técnicas de la librería utilizada Como característica fundamental de la librería implementada se puede destacar que ofrece una interfaz de utilización al programador de MUY ALTO NIVEL, lo que resulta de vital importancia para que los riesgos que corre un programador al utilizar la librería se ven muy minimizados, en cuanto a que no dispone de la capacidad de interactuar con los objetos criptográficos a bajo nivel, si no es a través de la interfaz suministrada. Además, en dicho interfaz no se gestiona directamente material criptográfico (lo cual permitiría realizar operaciones externas saltándonos los controles de la misma). En lugar de eso, la única interacción que proporciona se basa en utilizar representaciones de alto nivel, unos “objetos criptográficos” manejados simplemente por medio de un ID (un handler) a partir del cual no es posible obtener externamente la información criptográfica asociada, puesto que ésta la oculta la propia arquitectura de la librería. 2.1 Arquitectura La mayoría de las librerías de programación criptográficas permiten interactuar con el material criptográfico a varios niveles. Sin embargo, en el caso de la librería utilizada por ANF AC, se restringe el acceso tanto a la información sensible como a las propias funciones de tratamiento. Para ello, plantea un sistema de kernel seguro estructurado en capas, de modo que se ocultan las funciones de tratamiento específico asociadas a cada algoritmo de firma, generación de claves, etc. proporcionando una interfaz que únicamente deja disponible ciertas funcionalidades. Así, la capa superior, la que se presenta al programador, se basa en modelar cualquier elemento criptográfico de forma genérica por medio de un conjunto mínimo de objetos, traducidos en tipos de datos. Como se ha comentado, esos objetos se utilizan en base a handlers, con lo que en ningún caso se dispone de una estructura de datos accesible directamente en memoria. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 4 de 27 Figura 1.- Tipos básicos La arquitectura emplea un kernel seguro para implementar los mecanismos de control de acceso al material sensible. Así, dicho kernel es el que proporciona la interfaz entre el exterior (representado a través de los handlers) y las estructuras de datos que maneja internamente la librería. Así se consiguen varios objetivos: Por un lado, aislar los algoritmos del resto de la implementación dificultando una posible manipulación de los mismos. Además permite unificar las funciones de procesamiento de alto nivel, que son independientes del algoritmo, procedimiento, modo de cifrado, etc.… 2.2 Interface El hecho de que las operaciones sobre los objetos criptográficos se realicen de forma independiente del algoritmo o procedimiento concreto que se desea utilizar, hace que el interfaz de utilización sea muy sencillo. Así, a la hora de realizar cualquier operación sobre un objeto criptográfico, basta con establecer si se desea los atributos (como longitud de la clave, algoritmo a utilizar, modo de cifrado, etc.…) e invocar a una función genérica. CRYPT_CONTEXT privKeyContext; cryptCreateContext( &privKeyContext, cryptUser, CRYPT_ALGO_RSA ); cryptSetAttributeString( privKeyContext, CRYPT_CTXINFO_LABEL, label, labelLength ); cryptGenerateKey( privKeyContext ); Figura 2.- Ejemplo básico de generación de claves RSA en un contexto. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 5 de 27 De hecho, la utilización de objetos avanzados como los envelopes o las sessions, hace que se abstraigan incluso las operaciones de cifrado/descifrado, que se gestionan internamente por la librería. La utilización de una API independiente del algoritmo hace que la utilización nuevos algoritmos o variaciones de los mismos sea prácticamente transparente, cuanto a que el programador únicamente debe modificar el ID del algoritmo, atributo asociado al objeto que maneja, manteniendo el resto de la lógica de programa. de en un su Por otro lado, la propia librería realiza comprobaciones continuas que impiden que se utilice un objeto criptográfico para operaciones para las cuales no estaba pensado (como cifrar con claves destinadas sólo a firmar, utilizar certificados inválidos, etc…), e incluso valida que los algoritmos estén funcionando correctamente. Este modo de operación hace que el trabajo del programador esté muy dirigido, restringiendo los grados de libertad de éste. Esto, unido a la ocultación de las estructuras de datos que maneja la librería minimiza la posibilidad de que el programador cometa errores tanto de simple programación, como lo que podríamos llamar “conceptuales” (asociados a un posible desconocimiento de los fundamentos de las operaciones criptográficas a realizar). 2.3. Manejo de los objetos criptográficos La gestión de los objetos criptográficos tiene dos variantes. Por un lado, desde el punto de vista del programador, el manejo se limita a la utilización de los objetos de alto nivel descritos en puntos anteriores. Cuando se invoca a cualquier función de la API de alto nivel, la librería realiza una serie de comprobaciones de permisos de acceso basadas en dos tipos de ACLs, así como comprobaciones de la coherencia. Esto es, además de comprobar que el rol del usuario que desea realizar una operación le permite realizarla, comprueba que la operación no viola el perímetro de seguridad que establece la librería (que no trata por ejemplo de acceder a material privado). Por último comprueba que el tipo y atributos del objeto(s) a utilizar, así como el de la operación a realizar son compatibles y soportan dicha operación. El sistema contempla que se requiera la presencia de varios operadores para que sea posible utilizar los objetos criptográficos. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 6 de 27 3. Análisis de los procedimientos 3.1 Calidad del material criptográfico La calidad del material criptográfico se fundamenta, por un lado, en que la implementación de los algoritmos se ha realizado respetando los estándares más restrictivos, así como implementaciones de referencia (algunos procedimientos están basados por ejemplo en la famosa SSLeay de Eric Young, base también de la actual OpenSSL). Algoritmo Implmentación Blowfish Implementación basada en "Description of a New Variable-Length Key, 64-bit Block Cipher (Blowfish)", Bruce Schneier, "Fast Software Encryption", Lecture Notes in Computer Science No. 809, Springer-Verlag 1994 CAST-128 Modos de operación según la norma ISO/IEC 8372, "Information Technology -Modes of Operation for a 64¬bit Block Cipher Algorithm". DES Implementación basada RFC 2144, "The CAST-128 Encryption Algorithm", Carlisle Adams, May 1997 Triple DES Implementación basada en las normas ANSI X3.92, "American National Standard, Data Encryption Algorithm", 1981; FIPS PUB 462, "Data Encryption Standard", 1994; FIPS PUB 74, "Guidelines for Implementing and Using the NBS Data Encryption Standard", 1981; ISO/IEC 8731:1987, "Banking -Approved Algorithms for Message Authentication - Part 1: Data Encryption Algorithm (DEA)". DiffieHellman Modos de operación basados en ANSI X3.106, "American National Standard, Information Systems - Data Encryption Algorithm Modes of Operation", 1983.; FIPS PUB 81, "DES Modes of Operation", 1980.; ISO/IEC 8372, "Information Technology - Modes of Operation for a 64-bit Block Cipher Algorithm". DSA Implementación basada en ANSI X9.17, "American National Standard, Financial Institution Key Management (Wholesale)", 1985.; ISO/IEC 8732:1987, "Banking -Key Management Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 7 de 27 (Wholesale)". HMAC-MD5 Modos de operación basados en ISO/IEC 8372, "Information Technology - Modes of Operation for a 64-bit Block Cipher Algorithm". HMAC-SHA1 Implementación basada en el estándar PKCS #3, "Diffie-Hellman Key Agreement Standard", 1991. IDEA Implementación basada en las normas ANSI X9.30-1, "American National Standard, Public-Key Cryptography Using Irreversible Algorithms for the Financial Services Industry", 1993.; FIPS PUB 186, "Digital Signature Standard", 1994. MD5 Implementación basada en el estándar RFC 2104, "HMAC: KeyedHashing for Message Authentication", Hugo Krawczyk, Mihir Bellare, and Ran Canetti, February 1997. RSA Implementación basada en el estándar RFC 2104, "HMAC: KeyedHashing for Message Authentication", Hugo Krawczyk, Mihir Bellare, and Ran Canetti, February 1997. RC5 Implementación según las siguientes referencias "Device for the Conversion of a Digital Block and the Use Thereof", James Massey and Xuejia Lai, International Patent PCT/CH91/00117,1991. ; "Device for the Conversion of a Digital Block and Use of Same", James Massey and Xuejia Lai, US Patent #5,214,703, 1993.; "On the Design and Security of Block Ciphers", Xuejia Lai, ETH Series in Information Processing, Vol.1, Hartung-Gorre Verlag, 1992.; ISO/IEC 9979, "Data Cryptographic Tecniques -Procedures for the Registration of Cryptographic Algorithms". SHA1 Modos de operación según SO/IEC 8372, "Information Technology Modes of Operation for a 64-bit Block Cipher Algorithm" Tabla 1.- Recopilación de modelos de implementación de los protocolos. En general la librería, según el autor Peter Guttman, se ha implementado de acuerdo a un nivel 1 del estándar FIPS PUB 140-1, "Security Requirements for Cryptographic Modules", 1993. En cuanto a los rutinas de adquisición de números aleatorios, siguen los principios recogidos en "Randomness Recommendations for Security", RFC 1750, Donald Eastlake, Stephen Crocker, and Jeffrey Schiller, December 1994 y "Cryptographic Random Numbers", IEEE P1363 Appendix E, Draft version 1.0, 11 November 1995. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 8 de 27 Por último, sobre el resultado de las operaciones anteriores de recolección de entropía también se realiza un post-procesamiento con un generador X9.17 A esto, se le une que las configuraciones por defecto de la librería son muy restrictivas, como se analizará en el apartado de generación de claves. Con la aplicación de los métodos mencionados se puede estar seguro de que la generación de los números aleatorios y las medidas de protección que se han tomado en la confección de la librería son adecuadas para la solución de ANF AC. 3.2 Aleatoriedad La propia librería incorpora un sistema seguro de “generación” de datos aleatorios que posteriormente utiliza para crear claves de sesión, pares de claves pública/privada así como en otras operaciones criptográficas. La aleatoriedad se garantiza, además de por el propio algoritmo de generación, por varios mecanismos: • La librería es capaz de utilizar dispositivos hardware de generación de números aleatorio. • Por otro lado, aprovecha las capacidades que pueda proporcionar el sistema operativo a la hora de crear números aleatorios. Por ejemplo, en el caso de sistemas Unix, esta posibilidad se traduce en utilizar el dispositivo /dev/random, que acumula continuamente datos aleatorios del sistema. • Añadido a los procedimientos anteriores, que se utilizan como fuente inicial de números aleatorios, la propia librería va incorporando eventos aleatorios para mejorar la calidad de la fuente aleatoria (como número de ficheros abiertos, estado del sistema, ID de procesador, tamaño de la ventana…). Este proceso se realiza a través de dos funciones slowPoll() y fastPoll() que el programador puede utilizar explícitamente para introducir más o menos aleatoriedad, con mayor o menor coste computacional. Por otro lado, antes de realizar cualquier procedimiento que requiera datos aleatorios de calidad, la propia librería invoca dichas funciones automáticamente (concretamente la más “aleatoria”, slowPoll()), sin intervención del programador. En la Figura 3 se presenta un extracto de la función fastPoll() del archivo fuente misc/rndwin32.c, donde se muestran los eventos que utiliza para incorporar al flujo de datos aleatorios (a modo de semilla continua). • Por último, sobre el resultado de las operaciones anteriores de recolección de entropía se realiza un post-procesamiento mediante un generador X9.17 [5]. En la Figura 4 se observa un extracto del archivo dev/dev_sys.c donde se puede Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 9 de 27 observar la definición de número de ciclos X917_MAX_CYCLES que realiza el generador X9.17 sobre los datos aleatorios con los que se ha alimentado inicialmente el pool de datos aleatorios. /* Check whether the CPU supports extended features like CPUID and RDTSC, and get any info we need related to this. There is an IsProcessorFeaturePresent() function, but all that this provides _asm { ... [código en ensamblador para obtener información] } /* If there's a vendor ID present, check for vendor-specific special features */ if( hasAdvFeatures && !memcmp( vendorID, "CentaurHauls", 12 ) ) { _asm { noRNG: ...} } } /* If there's a hardware RNG present, read data from it. We check that the RNG is still present on each fetch since it could (at least in pack, since chars don't need alignment it would have no effect on the BYTE [] member */ addRandomValue( randomState, GetActiveWindow() ); addRandomValue( randomState, GetCapture() ); addRandomValue( randomState, GetClipboardOwner() ); ... ... addRandomValue( randomState, GetInputState() ); addRandomValue( randomState, GetProcessWindowStation() ); addRandomValue( randomState, GetTickCount() ); handle = GetCurrentThread(); GetThreadTimes( handle, &creationTime, &exitTime, &kernelTime, &userTime ); addRandomData( randomState, &creationTime, sizeof( FILETIME ) ); addRandomData( randomState, &exitTime, sizeof( FILETIME ) ); ... handle = GetCurrentProcess(); GetProcessTimes( handle, &creationTime, &exitTime, &kernelTime, &userTime ); ...*/ if( hasHardwareRNG dispositivo] ) ...[código de lectura de información del Figura 3.- Generación de números aleatorios fastPoll() Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 10 de 27 /* The size in bytes of the randomness pool and the size of the X9.17 post-processor generator pool */ #define RANDOMPOOL_SIZE 256 #define X917_POOLSIZE 8 /* The allocated size of the randomness pool, which allows for the overflow created by the fact that the hash function blocksize isn't any useful multiple of a power of 2 */ #define RANDOMPOOL_ALLOCSIZE ( ( ( RANDOMPOOL_SIZE + 20 -1 ) / 20 ) * 20 ) /* In order to avoid the pool startup problem (where initial pool data may consist of minimally-mixed entropy samples) we require that the pool be mixed at least the following number of times before we can draw data from it. This usually happens automatically because a slow poll adds enough data to cause many mixing iterations, however if this doesn't happen we manually mix it the appropriate number of times to get it up to the correct level */ #define RANDOMPOOL_MIXES 10 /* The number of samples of previous output that we keep for the FIPS 140 continuous tests, and the number of retries we perform if we detect a repeat of a previous output */ #define RANDOMPOOL_SAMPLES 16 #define RANDOMPOOL_RETRIES 5 /* The number of times that we cycle the X9.17 generator before we load new key and state variables. This means that we re-seed for every X917_MAX_BYTES of output produced */ #define X917_MAX_BYTES 8192 #define X917_MAX_BYTES / X917_POOLSIZE ) X917_MAX_CYCLES ( Figura 4.- Postprocesamiento con generador X9.17 3.3 Generación de claves La generación de claves es otro aspecto básico a tener en cuenta a la hora de evaluar el desarrollo de cualquier módulo criptográfico. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 11 de 27 Los aspectos de longitud de clave, modos de operación y demás se configuran por medio de las funciones de gestión de atributos cryptSetAttribute() y cryptSetAttributeString(). Para la generación de claves privadas se utilizan los contextos propios de la librería. Cabe destacar que en el código por seguridad siempre se destruye el contexto tras la utilización de una clave privada. 3.3.1 Claves RSA El sistema de firma digital RSA basa su seguridad en la hipótesis de que el único método efectivo de ataque es la factorización de su módulo que se elige como un número entero grande, y en que esta operación de factorización es computacionalmente imposible a partir de un tamaño de los números elegidos, y dada una determinada potencia de cómputo para el presunto atacante. Así pues, el criptosistema RSA requiere generar su módulo multiplicando dos números primos, de tamaños semejantes y que cumplan ciertas restricciones. Para la generación de esos números primos se utilizan algoritmos probabilísticas que afirman, con cierto grado de certeza, que un determinado número puede ser primo. El procedimiento de generación de claves se basa en la búsqueda de números primos muy grandes (su longitud está relacionada directamente con la longitud del par de claves a generar, por simplificar la mitad de dicha longitud). Por tanto, el factor crítico en la generación de claves RSA es la variabilidad o calidad de los números primos que se buscan durante el proceso de construcción de la clave. Es por tanto de vital importancia que el proceso de búsqueda de dichos números no sea fácilmente reproducible. Para ello los procedimientos que sigue la librería son los recogidos en los siguientes extractos de código, /* Generate an RSA key pair into an encryption context */ int generateRSAkey( CONTEXT_INFO *contextInfoPtr, keySizeBits ) { [INICIALIZACIÓN DE VARIABLES] const int /* Determine how many bits to give to each of p and q */ pBits = ( keySizeBits + 1 ) / 2; qBits = keySizeBits -pBits; pkcInfo->keySizeBits = pBits + qBits; /* Generate the primes p and q and set them up so that the CRT decrypt Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 12 de 27 will work */ BN_set_word( &pkcInfo->rsaParam_e, RSA_PUBLIC_EXPONENT ); status = generatePrime( pkcInfo, p, pBits, RSA_PUBLIC_EXPONENT, contextInfoPtr ); if( cryptStatusOK( status ) ) status = generatePrime( pkcInfo, q, qBits, RSA_PUBLIC_EXPONENT, contextInfoPtr ); if( cryptStatusOK( status ) ) status = fixCRTvalues( pkcInfo, FALSE ); if( cryptStatusError( status ) ) return( status ); /* Compute d = eInv mod (p -1)(q -1), e1 = d mod (p -1), and e2 =d mod(q-1) */ CK( BN_sub_word( p, 1 ) ); CK( BN_sub_word( q, 1 ) ); CK( BN_mul( tmp, p, q, &pkcInfo->bnCTX ) ); CKPTR( BN_mod_inverse( d, &pkcInfo->rsaParam_e, tmp, &pkcInfo->bnCTX ) ); CK( BN_mod( &pkcInfo->rsaParam_exponent1, d, p, &pkcInfo->bnCTX ) ); CK( BN_mod( &pkcInfo->rsaParam_exponent2, d, q, &pkcInfo->bnCTX ) ); CK( BN_add_word( p, 1 ) ); CK( BN_add_word( q, 1 ) ); if( bnStatusError( bnStatus ) ) return( getBnStatus( bnStatus ) ); /* Compute n = pq, and u = qInv mod p */ CK( BN_mul( &pkcInfo>rsaParam_n, p, q, &pkcInfo->bnCTX ) ); CKPTR( BN_mod_inverse( &pkcInfo->rsaParam_u, q, p, &pkcInfo->bnCTX ) ); if( bnStatusError( bnStatus ) ) return( getBnStatus( bnStatus ) ); /* Evaluate the Montgomery forms */ return( getRSAMontgomery( pkcInfo, FALSE ) ); } Figura 5.- Función generateRSAkey () La función más importante para evaluar la calidad de la clave generada es, por tanto, generatePrime(). static int generatePrime( PKC_INFO *pkcInfo, BIGNUM *candidate, const int noBits, const long exponent, const void *callbackArg ) { [INICIALIZACIÓN] /* Start with a cryptographically strong odd random number */ status = generateBignum( candidate, noBits, 0xC0, 0x1 ); ... Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 13 de 27 do { /* Set up the sieve array for the number and pick a random starting point */ initSieve( sieveArray, candidate ); setMessageData( &msgData, &startPoint, sizeof( int ) ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM ); if( cryptStatusError( status ) ) break; startPoint &= SIEVE_SIZE -1; /* Perform a random-probing search for a prime. Poli, poli, di umbuendo */ for( offset = nextEntry( startPoint ); offset != startPoint; offset = nextEntry( offset ) ) { long remainder; /* If this candidate is divisible by anything, continue */ if( sieveArray[ offset ] ) continue; /* Adjust the candidate by the number of nonprimes we've skipped */ if( offset > oldOffset ) CK( BN_add_word( candidate, ( offset -oldOffset ) * 2 ) ); else CK( BN_sub_word( candidate, ( oldOffset -offset ) * 2 ) ); oldOffset = offset; status = primeProbable( pkcInfo, candidate, noChecks, callbackArg ); if( cryptStatusError( status ) ) break; if( !status ) continue; /* It's for use with RSA, check the RSA condition that gcd( p -1, exp ) == 1. Since exp is a small prime, we can do this efficiently by checking that ( p -1 ) mod exp != 0 */ CK( BN_sub_word( candidate, 1 ) ); remainder = BN_mod_word( candidate, exponent ); CK( BN_add_word( candidate, 1 ) ); if( bnStatusOK( bnStatus ) && remainder ) break; /* status = TRUE from above */ } } while( status == FALSE ); /* -ve = error, TRUE = success */ [DEVUELVE RESULTADO] Figura 6.- Función generatePrime () Del código anterior se han eliminado secciones correspondientes a la comprobación del test. En el caso de la librería empleada, el método utilizado es el test Millar-Rabin seguido de un test de Fermat. La elección de los algoritmos es correcta y su Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 14 de 27 implementación es eficaz, pero su seguridad depende del número de veces que se realiza el test de Millar-Rabín antes de aceptar un número como probable primo. En la implementación de la librería este número de iteraciones se elige en función de la longitud del número primo que se quiere localizar, y se ajusta de modo que la probabilidad de no ser realmente primo sea de 2-80; es decir, de un fallo entre 1024 casos. Dado que el Test de Fermat tiene una posibilidad de 10-44 de no reconocer a un número de 512 bits como compuesto cuando lo es, por lo que el uso combinado de ambos algoritmos es muy recomendable. A la vista de estos valores, se puede estar seguro de la calidad, en cuanto a su factorización, de los módulos RSA que genera la librería. La combinación de esto con la colección de algoritmos utilizados para generación de números aleatorios, permite afirmar que la generación de claves RSA por parte de la mencionada librería los hace válidos para ser utilizados en sistemas de Firma Electrónica Avanzada. La elección de longitudes de clave de 2.048 bits para la firma de los certificados raíz e intermedios es correcta actualmente y posiblemente sea así en los próximos cinco o siete años. 3.3.2 Claves de sesión En el caso de querer derivar claves de sesión a partir de cierta información secreta, bien sea ésta una passphrase o material criptográfico intercambiado previamente, el parámetro a controlar es el número de iteraciones que realizar el algoritmo de generación de la clave a partir de dicha información inicial. Por defecto el proceso de generación de claves realizará repetidamente una operación HASH con la función HMAC-SHA1 sobre un SALT de entrada y la información secreta. Ese proceso de hash se repite 16.000 veces para dificultar posibles ataques de adivinación de contraseña. Ese parámetro se controla a partir del atributo del CRYPT_CONTEXT denominado CRYPT_CTXINFO_KEYING_ITERATIONS, de la forma que sigue: cryptSetAttribute( cryptContext, CRYPT_CTXINFO_KEYING_ITERATIONS, 1000 ); Para comprobar que el número de iteraciones por defecto es de 16.000 se muestra un extracto del código correspondiente a la inicialización de la librería, Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 15 de 27 fixedOptionInfo[] = { /* Dummy entry for CRYPT_ATTRIBUTE_NONE */ { CRYPT_ATTRIBUTE_NONE, 0 }, /* cryptlib information (read-only) */ { CRYPT_OPTION_INFO_DESCRIPTION, CRYPT_UNUSED, "cryptlib security toolkit" }, OPTION_STRING, { CRYPT_OPTION_INFO_COPYRIGHT, OPTION_STRING, CRYPT_UNUSED, "Copyright Peter Gutmann, Eric Young, OpenSSL, 1994-2004" }, { CRYPT_OPTION_INFO_MAJORVERSION, CRYPT_UNUSED, NULL, 3 }, OPTION_NUMERIC, { CRYPT_OPTION_INFO_MINORVERSION, CRYPT_UNUSED, NULL, 1 }, OPTION_NUMERIC, { CRYPT_OPTION_INFO_STEPPING, CRYPT_UNUSED, NULL, 0 }, OPTION_NUMERIC, /* Context options, base = 0 */ /* Algorithm = Conventional encryption/hash/MAC options */ { CRYPT_OPTION_ENCR_ALGO, OPTION_NUMERIC, 0, NULL, CRYPT_ALGO_3DES }, { CRYPT_OPTION_ENCR_HASH, OPTION_NUMERIC, 1, NULL, CRYPT_ALGO_SHA }, { CRYPT_OPTION_ENCR_MAC, OPTION_NUMERIC, 2, NULL, CRYPT_ALGO_HMAC_SHA }, /* Algorithm = PKC options */ { CRYPT_OPTION_PKC_ALGO, OPTION_NUMERIC, 3, NULL, CRYPT_ALGO_RSA }, { CRYPT_OPTION_PKC_KEYSIZE, OPTION_NUMERIC, 4, NULL, bitsToBytes( 1024 ) }, /* Algorithm = Signature options */ { CRYPT_OPTION_SIG_ALGO, OPTION_NUMERIC, 5, NULL, CRYPT_ALGO_RSA }, { CRYPT_OPTION_SIG_KEYSIZE, OPTION_NUMERIC, 6, NULL, bitsToBytes( 1024 ) }, /* Algorithm = Key CRYPT_OPTION_KEYING_ALGO, CRYPT_ALGO_SHA }, derivation options OPTION_NUMERIC, 7, */ { NULL, { CRYPT_OPTION_KEYING_ITERATIONS, OPTION_NUMERIC, 8, NULL, 16000 }, /* Certificate options, base = 100 */ Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 16 de 27 { CRYPT_OPTION_CERT_SIGNUNRECOGNISEDATTRIBUTES, OPTION_BOOLEAN, 100, NULL, FALSE }, { CRYPT_OPTION_CERT_VALIDITY, OPTION_NUMERIC, 101, NULL, 365 }, { CRYPT_OPTION_CERT_UPDATEINTERVAL, NULL, 90 }, OPTION_NUMERIC, 102, { CRYPT_OPTION_CERT_COMPLIANCELEVEL, OPTION_NUMERIC, 103, NULL, CRYPT_COMPLIANCELEVEL_STANDARD }, /* CMS options */ { CRYPT_OPTION_CMS_DEFAULTATTRIBUTES, OPTION_BOOLEAN, 104, NULL, TRUE }, Figura 7.- Configuración del número de iteraciones 3.4 Almacenamiento de material sensible El almacenamiento del material sensible se realiza sobre objetos denominados CRYPT_KEYSETs. Para el almacenamiento en disco se utiliza un KEYSET de tipo FILE. La librería no utiliza el formato más común, PKCS#12, por considerarlo poco seguro. Ni siquiera permite la opción de exportar material privado a ese formato. En lugar de eso utiliza PKCS#15, un formato mucho más robusto. 3.5 Inicialización y cierre En la inicialización del software del cliente se procede a realizar las siguientes tareas en orden: 1. Se inicializa la librería mediante la función cryptInit(), que inicializa todos los estados internos de la misma y se realizan una serie de comprobaciones. También carga una configuración por defecto. 2-Se modifican una serie de parámetros de configuración de la librería en lo concerniente a los algoritmos de firma y hash utilizados por defecto como preferidos. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 17 de 27 En concreto se escoge como algoritmo de firma el RSA y la longitud por defecto de clave de 1024 bits. Como algoritmo de hash se utiliza por defecto el SHA2. 3-Importación del certificado raíz de la Autoridad de Certificación. Dicho certificado se encuentra incorporado dentro del código a través de un array binario y se importa como objeto de la librería libcrypt a través de: EVAL(cryptImportCert(certData, length, CRYPT_UNUSED, &cert)); Posteriormente se habilita el atributo de trusted para que la librería confíe en él. Con este procedimiento evitamos el riesgo que puede tener que alguien nos cambie el certificado raíz si lo leyéramos de fichero. 4-Se abre un keyset donde se almacenan las claves privadas generadas por el usuario, así como los certificados asociados. El formato del keyset es un fichero PKCS#15 manejado de forma nativa y transparente por la librería. Con este keystore se trabajará a la largo del programa. Durante el correcto funcionamiento del programa se hacen las llamadas correspondientes a la librería utilizando la interfaz de alto nivel que ofrece la mayor protección frente a fallos o descuidos del programador. El procedimiento de cierre puede producirse por cierre normal del programa (a petición del usuario), por una condición de error o por una señalización externa (generalmente por parte del sistema operativo). En cualquier caso se procede a una salida ordenada del mismo cerrando el keystore y cerrando la librería mediante cryptEnd(). 3.6 Gestión de errores En todo el programa se realiza un control de errores exhaustivo, y muy especialmente en lo relacionado con la interacción con la librería criptográfica. Siempre se comprueba el error y se retorna de la función que esté realizando el procesamiento si se detecta un código de error en una llamada a una función de la librería. Para ellos se encapsulan todas las llamadas a la librería mediante una macro EVAL(), como en el siguiente ejemplo: EVAL(cryptAddPublicKey(keyset, cert)); El tratamiento del error corresponderá a la función de nivel superior, que deberá decidir sobre la gravedad del error y tomar la acción pertinente como comunicárselo al usuario y continuar el programa o simplemente finalizar el programa. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 18 de 27 3.7 Proceso de generación de petición de certificado El proceso de generación de certificado implica la generación de un par de claves privadas. En la implementación actual se utiliza el algoritmo RSA con una longitud de clave de 1024 bits para los certificados de usuario. Los pasos seguidos en la generación de petición al certificado implican utilizar la interfaz de alto nivel de la librería de acuerdo a las recomendaciones del autor. Se utiliza un keyset donde se almacenan los pares de claves de usuario. El keyset empleado es un archivo PKCS#15. Dicho archivo tiene una limitación implícita impuesta por la librería de un máximo de 32 claves, cantidad más que suficiente para un único usuario. El keyset se abre poco después de la inicialización de la librería y se cierra al finalizar el programa normal o anormalmente, tal y como se describe en el punto sobre inicialización y cierre del programa. El siguiente fragmento de código explica el proceso de generación de petición de certificado: int status; /* Generating key-pair with algorithm type specified */ EVAL(cryptCreateContext(keyPair, CRYPT_UNUSED, algoType)); EVAL(cryptSetAttributeString(*keyPair, keyLabel, \ CRYPT_CTXINFO_LABEL, keyLabelLength)); EVAL(cryptGenerateKey(*keyPair)); EVAL(cryptCreateCert(certReq, CRYPT_CERTTYPE_CERTREQUEST)); CRYPT_UNUSED, /* Only public key is effectively added though we pass the key pair */ EVAL(cryptSetAttribute(*certReq, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, *keyPair)); if(!addCertFields(*certReq, certData)) return(FALSE); /* Sign cert */ EVAL(cryptSignCert(*certReq, *keyPair)); /* Check cert. It is self signed so it does not need anything else */ Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 19 de 27 EVAL(cryptCheckCert(*certReq, CRYPT_UNUSED)); Figura 9.- Proceso de generación de la petición de certificado Como puede observarse los pasos son: 1- Crear un contexto para generar el par de claves privadas mediante la función cryptCreateContext(). 2- Generar el par de claves mediante cryptGenerateKey() 3- Crear un objeto certificado del tipo CRYPT_CERTTYPE_REQUEST. 4- Adjuntar la clave (cryptSetAttribute). privada a la petición de certificado como atributo 5- Rellenar los campos de la petición del certificado a partir de la entrada realizada por los operadores autorizados y que chequearán el formato especificado en la CPS de ANF AC . 6- Firmar la petición del certificado con la clave privada recién creada (cryptCertSign). El certificado se exporta a un fichero y posteriormente la clave privada es troceada y almacenada en diferentes partes: una de las partes es cifrada y se almacena en el keystore con passwords proporcionados por los operadores autorizados, el resto de los trozos de la clave se almacena en el keystore de cada dispositivo de firma de cada operador autorizado, y a su vez cada keystore es cifrado con el password privado de cada respectivo operador, de tal forma que para hacer uso de la clave privada es preciso: 1.- que dispositivos, estén presentes todos los operadores con sus respectivos 2.- que los operadores activen sus dispositivos con su respectivo PIN secreto, 3.- que tengan acceso al contenedor donde esta almacenado la parte general de la clave, y que los operadores introduzcan los PIN secreto que permiten su descifrado, y siguiendo el orden establecido para el descifrado por capas. Las comprobaciones sobre el password son las siguientes: Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 20 de 27 1- No se permiten passwords sin combinaciones de números y letras (tiene que haber caracteres de ambos tipos) 2- La longitud mínima es de 8 caracteres 3- Se compara el password con una base de datos o diccionario de passwords “comunes” no autorizados. Una vez comprobado el password se almacena la clave privada en el keystore a través de la función cryptAddPrivateKey(). La librería contiene una clave de alta seguridad, esta clave esta troceada y cada trozo esta dentro de la API criptográfica, y alguno de los trozos se obtiene por aplicación de un algoritmo determinista. La clave seleccionada por el operador es cifrada con esta clave de alta seguridad, y el producto resultante es utilizado como password para cifrar la clave privada. Después se destruye el objeto de contexto que contiene la pareja de claves en memoria, mediante cryptDestroyContext(). De esta forma se garantiza que ni tan siquiera el operador conoce la clave de cifrado, y es preciso utilizar la API licenciada para procesar el sistema. Después de destruir el contexto de cifrado también se borra (sobrescribiendo con otro valor) la zona de memoria donde se almacena el password de usuario para minimizar ataques por acceso a la zona de memoria del proceso aprovechando vulnerabilidades del sistema operativo. Si se trata de un certificado de CA intermedia, la petición de certificado se guarda en formato PKCS#10, y ese certificado de petición es firmado con la clave privada del certificado raíz a cuya jerarquía va a estar sometido. 3.8 Importación de certificados La importación de certificados de CA intermedios se realiza una vez que ha pasado el proceso administrativo de expedición del certificado por parte de la CA. En este proceso se determina el número de operadores que tiene que estar presente para activar la clave privada asociada a ese certificado. La aplicación parte del certificado en un fichero, se admiten los dos tipos de codificación estándar para ficheros de certificado: PEM (base 64) o DER (binario). Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 21 de 27 La aplicación lee y envía el fichero a un buffer realizando la conversión pertinente a formato binario si se encuentra en codificación Base64. Una vez tenemos el certificado en un buffer de datos se importa mediante la función cryptImportCert(). Una vez importado el certificado sin error se adjunta al keystore donde deberá residir la clave privada correspondiente, utilizando la función cryptAddPublicKey(). En caso de no encontrarse una clave privada asociada la función producirá un error y se abortará el proceso. La propia librería no deja asociar a un keystore con claves privadas certificados que no estén asociados a ninguna clave privada. 3.9 Proceso de firma En el procedimiento de firma, la API solicita del operador o del sistema, la localización del archivo a firmar, así como la localización del fichero firmado. A continuación se consulta el keystore para localizar los certificados del usuario. Se le presenta una lista con todos los disponibles. Se le indica al mismo que escoja uno. Se chequea el certificado para verificar que es correcto, no ha caducado y no se encuentra revocado. La lista de certificados revocados se obtiene de un fichero firmado por la CA que puede estar disponible bien localmente o bien remotamente. La opción depende y es responsabilidad del usuario. Posteriormente se accede a la clave privada asociada del certificado, para lo cuál se precisa de los passwords que lo protegen. A continuación se le solicita el password de la clave privada y se accede a la misma a través de la función cryptGetPrivateKey(). Se comprueba el error para verificar si no existe la clave o simplemente el usuario ha especificado una clave incorrecta ofreciéndole varios intentos más. La función devuelve un manejador de objeto CRYPT_CONTEXT, que contiene la clave privada. A continuación se siguen una serie de pasos que pueden esquematizarse con este fragmento de código: CRYPT_ENVELOPE env; Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 22 de 27 /* Signing and hashing algorithms should be set in configuration */ EVAL(cryptCreateEnvelope(&env, CRYPT_FORMAT_CRYPTLIB)); CRYPT_UNUSED, EVAL(cryptSetAttribute(env, CRYPT_ENVINFO_SIGNATURE, signKey)); EVAL(cryptSetAttribute(env, CRYPT_ENVINFO_DATASIZE, dataLength)); EVAL(cryptPushData(env, data, dataLength, &bytesCopied)); EVAL(cryptFlushData(env)); if(bytesCopied < dataLength) { const int initialBytesIn = bytesCopied; EVAL(cryptPushData(env, (BYTE *) data + initialBytesIn, \ dataLength - initialBytesIn, &bytesCopied )); bytesCopied += initialBytesIn; } if(bytesCopied != dataLength) { return ERROR_DATACOPY } EVAL(cryptPopData(env, &bytesCopied)); signedData, signedDataBufferSize, Figura 8.- Proceso de firma digital Pueden observarse una serie de pasos. Se crea un objeto de tipo CRYPT_ENVELOPE que es la interfaz de alto nivel que ofrece la librería para “alterar” datos de forma local. Se aplica para los procedimientos de firma, verificación, cifrado y descifrado de datos. Este objeto conoce el tipo de manipulación que van a sufrir los datos que pasen a través de él por los objetos que se le añadan como atributos. En este caso puede observarse que le pasamos el objeto signKey de tipo CRYPT_CONTEXT, que es la clave privada que acabamos de recuperar del keystore, para maximizar la eficiencia del proceso también le pasamos como atributo la longitud de los datos que le vamos a pasar para firmar. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 23 de 27 De esta forma la librería reserva un buffer adecuado para el procesamiento que se va a llevar a cabo y puede realizar todo el procesamiento de una vez. Después le pasamos los datos del fichero a firmar (ya en forma de buffer) a través de la función cryptPushData(). Como puede observarse se realiza la comprobación de que efectivamente se copian todos los datos a la hora de firmar y no quedan datos fuera. Una vez terminado el paso anterior se recuperan los datos firmados a través de la función cryptPopData(). Posteriormente se exportan estos datos a un fichero. Cabe mencionar en este apartado que el algoritmo que escoge la librería para generar el hash de la firma es el especificado por el parámetro de configuración CRYPT_OPTION_ENCR_HASH que ya nos hemos asegurado al comienzo del programa a fijar a SHA2. El algoritmo para cifrar el hash viene condicionado por el tipo de clave privada que usemos que ya hemos especificado anteriormente que es de tipo RSA. La firma resultante se exporta a un fichero en formato deseado por el operador, en la ruta indicada por el usuario. 3.10 Proceso de verificación El proceso de verificación es análogo al proceso de firma. Lo único indicar en este caso que no es preciso indicar el certificado ya que va incluido en la firma que llega en un fichero en formato deseado por el firmante. En este caso se solicita del operador que indique dónde se encuentra el fichero de firma y opcionalmente la ruta de salida para obtener el documento sin firmar en caso de que lo requiera el operador. El procedimiento es el siguiente: CRYPT_ENVELOPE env; /* Signature contains everything to check it (cert chain, algorithms, etc.) */ EVAL(cryptCreateEnvelope(&env, CRYPT_FORMAT_CRYPTLIB)); Componentes criptográficos entidad final de ANF AC CRYPT_UNUSED, Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 24 de 27 EVAL(cryptSetAttribute(env, signedDataLength)); EVAL(cryptPushData(env, &bytesCopied)); CRYPT_ENVINFO_DATASIZE, signedData, signedDataLength, EVAL(cryptFlushData(env)); if(bytesCopied < signedDataLength) { const int initialBytesIn = bytesCopied; EVAL(cryptPushData( env, (BYTE *) signedData + initialBytesIn, \ signedDataLength - initialBytesIn, &bytesCopied )); bytesCopied += initialBytesIn; } if(bytesCopied != signedDataLength) { return ERROR_DATACOPY } EVAL(cryptPopData(env, (int *)data, dataBufferSize, &bytesCopied)); Figura 12.- Proceso de verificación de firma digital Como puede verse en este caso se crea un objeto CRYPT_ENVELOPE y se le añade el atributo del tamaño de los datos firmados. En este caso el objeto sabe que tiene que verificar la firma cuando se le pasan los datos de firma a través de cryptPushData(), previamente importados del fichero proporcionado por el operador. Cabe destacar que los certificados generados mediante esta librería contienen atributos concretos que los identifican ante otros certificados (ver CPS de ANF AC). Los dispositivos de firma solo pueden utilizar certificados que integran estos atributos y que han sido emitidos por ANF AC. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 25 de 27 4. Consideraciones adicionales Existe una serie de consideraciones adicionales relativas a la seguridad del entorno de firma. La principal de ellas es poder determinar que la librería criptográfica que utilizamos es realmente la original y que no es realmente otra librería que la suplanta para filtrar las comunicaciones con la misma. Uno de los métodos aplicados es el de realizar una operación de hash sobre la propia librería determinando la que realmente está cargada a través de llamadas al sistema, y compararlo con el valor adecuado almacenado dentro del programa. Esta opción sin duda es bastante versátil pero no resuelve algunas de las problemáticas como puede ser la necesidad de generar el hash de forma externa a la librería, abandonando por consiguiente el entorno óptimo para la generación del mismo. La solución adoptada ha sido, si cabe, más sencilla, pero más efectiva. Se trata de enlazar estáticamente la librería al programa. Algo que puede resultar poco eficiente en un principio, pero que no lo es tanto cuando existen otro tipo de requerimientos de mayor importancia, como es este caso, o los recursos disponibles en un entorno operativo medio son más que sobrados para esta solución, como la cantidad de memoria disponible o espacio en HD. Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 26 de 27 5. Referencias [1] Recomendación para la certificación de componentes destinados a la emisión y gestión de certificados. Annabelle Lee, NIST; et. al. Estudio promovido por la NSA. “Certificate Issuing and Management Components Family of Protection Profiles Version 1.0” Disponible en: http://csrc.nist.gov/ pki/documents/CIMC_PP_final-corrections_20010126.pdf [2] Normas europeas de estandarización CWA 14167-1, CWA 14167-2, CWA 14170 [3] Recomendaciones RFC3739, ETSI TS 101 862, ETSI TS 101733. [4] The Design of a Cryptographic Security Architecture. Peter Gutmann. Disponible en http://www.cs.auckland.ac.nz/~pgut001/pubs/usenix99.pdf [5] American National Standards Institute. American National Standard X9.17: Financial Institution Key Management (Wholesale), 1985. [6] ANF Certification Policy Statement. www.anf.es/AC/Documentos Componentes criptográficos entidad final de ANF AC Ref. DT_Creacion_componenetes_criptograficos_entidad_final.pdf Versión: 1.1 OID: 1.3.6.1.4.1.18332.14.1 Página 27 de 27