Firmas Digitales muy Fácil con Firefox
Introducción
Los certificados digitales son cada vez más populares y su
utilidad no se limita sólo a identificar al usuario sino que
también permite que éste “firme”
electrónicamente datos con garantías de no repudio y
reconocimiento legal en muchos paises. Particularmente útil
para que el usuario firme contratos u ordene productos/servicios, de
consentimiento con validez legal, etc.
Para implementar certificados digitales en el navegador Microsoft
Internet Explorer contamos con cuantiosa documentación y
tutoriales sobre el tema, pero en el caso de Firefox estamos más
escasos así que aquí va nuestro humilde aporte.
En la página web
Firefox implementa el objeto JavaScript 1.2
“window.crypto” que encapsula la operatorio de firma y
simplifica notablemente la etapa en cliente. Una vez en servidor,
veremos cómo validar la firma y extraer información de
la misma.
A fines de simplificar el ejemplo, copiamos una página muy
breve con el JavaScript incrustado:
|
Como vemos, el formulario CompruebaFirmaDigital
tiene un campo original
donde pondremos el contenido a firmar.
Luego presionaremos el botón Firmar
y Firefox nos mostrará un pop-up donde seleccionamos el
certificado a utilizar:
En esta ventana podemos comprobar el texto a firmar, seleccionar
el certificado (podríamos tener varios instalados) e
introducir la clave del repositorio*. Al presionar OK, se generará
en firmado
la firma correspondiente.
El botón Enviar
realizará el post al servlet que veremos en la siguiente
sección.
*Si no hemos definido ninguna clave de repositorio o “master
password”, deberíamos ir a Tools / Options / Security (o
Herramientas / Opciones / Seguridad):
Y entonces seleccionar “Use a master password” y
establecer la nueva clave pinchando en el botón “Change
Master Password”:
Es interesante observar la barra “Password quality meter”
que calcula la dificultad de nuestra password en base a reglas como:
contener letras en mayúsculas y minúsculas, números,
caracteres de puntuación, etc.
En el servidor
Vamos a crear un servlet donde:
-
Obtendremos el contenido original y el contenido firmado
-
Verificaremos que el firmado coincida con el original
-
Obtendremos más información desde el
certificado
Y vamos directamente al código:
import import import import public private @Override try // out.println(«[VERIFY // out.println(«[EXTRACTING }
|
Al enviar un mensaje firmado, se ejecutará el servlet y
obtendremos una página como la que copio a continuación:
[VERIFY OK] [P7 CONTENS] Content Info Sequence Content type: 1.2.840.113549.1.7.1 Content: null PKCS7 :: version: 01 PKCS7 :: digest AlgorithmIds: SHA PKCS7 :: certificates: 0. [ [ Version: V3 Subject: CN=NOMBRE ANTONIUCCI . JAVIER - NIF X2998155J, OU=500051386, OU=FNMT Clase 2 CA, O=FNMT, C=ES Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Sun RSA public key, 1024 bits modulus: 131575675323991855481882695090604255354283802995994130325140287343442527550523287 735330662450495312300738878218939717859247210783036875426139715204374930718984052460866831 380022824796002705409063853029223667641707 5659210682004122052836707771024886312323336689 83461209287086466595754860589074641524008749629753 public exponent: 65537 Validity: [From: Thu Jun 09 18:19:39 CEST 2005, To: Mon Jun 09 18:19:39 CEST 2008] Issuer: OU=FNMT Clase 2 CA, O=FNMT, C=ES SerialNumber: [ 3c82ccbe] Certificate Extensions: 11 [1]: ObjectId: 2.5.29.17 Criticality=false SubjectAlternativeName [ [OID.1.3.6.1.4.1.5734.1.1=JAVIER, OID.1.3.6.1.4.1.5734.1.2=ANTONIUCCI, OID.1.3.6.1.4.1.5734.1.3=., OID.1.3.6.1.4.1.5734.1.4=X2998155J]] [2]: ObjectId: 2.5.29.35 Criticality=false AuthorityKeyIdentifier [ KeyIdentifier [ 0000: 40 9A 76 44 97 74 07 C4 AC 14 CB 1E 8D 4F 3A 45 @.vD.t.......O:E 0010: 7C 30 D7 61 .0.a ] ] [3]: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 80 24 65 E9 F7 0E 54 3C 1A AD 49 69 CD 21 10 58 .$e...T<..Ii.!.X 0010: 0C 4D 04 17 .M.. ] ] [4]: ObjectId: 1.3.6.1.5.5.7.1.3 Criticality=false Extension unknown: DER encoded OCTET string = 0000: 04 23 30 21 30 08 06 06 04 00 8E 46 01 01 30 15 .#0!0......F..0. 0010: 06 06 04 00 8E 46 01 02 30 0B 13 03 45 55 52 02 .....F..0...EUR. 0020: 01 64 02 01 00 .d... [5]: ObjectId: 1.3.6.1.4.1.5734.1.33 Criticality=false Extension unknown: DER encoded OCTET string = 0000: 04 10 16 0E 50 45 52 53 4F 4E 41 20 46 49 53 49 ....PERSONA FISI 0010: 43 41 CA [6]: ObjectId: 2.5.29.16 Criticality=false PrivateKeyUsage: [ From: Thu Jun 09 18:19:39 CEST 2005, To: Mon Jun 09 18:19:39 CEST 2008] [7]: ObjectId: 2.5.29.32 Criticality=false CertificatePolicies [ [CertificatePolicyId: [1.3.6.1.4.1.5734.3.5] [PolicyQualifierInfo: [ qualifierID: 1.3.6.1.5.5.7.2.1 qualifier: 0000: 16 28 68 74 74 70 3A 2F 2F 77 77 77 2E 63 65 72 .(http://www.cer 0010: 74 2E 66 6E 6D 74 2E 65 73 2F 63 6F 6E 76 65 6E t.fnmt.es/conven 0020: 69 6F 2F 64 70 63 2E 70 64 66 io/dpc.pdf ], PolicyQualifierInfo: [ qualifierID: 1.3.6.1.5.5.7.2.2 qualifier: 0000: 30 81 CB 1A 81 C8 43 65 72 74 69 66 69 63 61 64 0.....Certificad 0010: 6F 20 52 65 63 6F 6E 6F 63 69 64 6F 20 65 78 70 o Reconocido exp 0020: 65 64 69 64 6F 20 73 65 67 FA 6E 20 6C 65 67 69 edido seg.n legi 0030: 73 6C 61 63 69 F3 6E 20 76 69 67 65 6E 74 65 2E slaci.n vigente. 0040: 55 73 6F 20 6C 69 6D 69 74 61 64 6F 20 61 20 6C Uso limitado a l 0050: 61 20 43 6F 6D 75 6E 69 64 61 64 20 45 6C 65 63 a Comunidad Elec 0060: 74 72 F3 6E 69 63 61 20 70 6F 72 20 76 61 6C 6F tr.nica por valo 0070: 72 20 6D E1 78 69 6D 6F 20 64 65 20 31 30 30 20 r m.ximo de 100 0080: 65 20 73 61 6C 76 6F 20 65 78 63 65 70 63 69 6F e salvo excepcio 0090: 6E 65 73 20 65 6E 20 44 50 43 2E 43 6F 6E 74 61 nes en DPC.Conta 00A0: 63 74 6F 20 46 4E 4D 54 3A 43 2F 4A 6F 72 67 65 cto FNMT:C/Jorge 00B0: 20 4A 75 61 6E 20 31 30 36 2D 32 38 30 30 39 2D Juan 106-28009- 00C0: 4D 61 64 72 69 64 2D 45 73 70 61 F1 61 2E Madrid-Espa.a. ]] ] ] [8]: ObjectId: 2.16.840.1.113730.1.1 Criticality=false NetscapeCertType [ SSL client S/MIME ] [9]: ObjectId: 2.5.29.19 Criticality=false BasicConstraints:[ CA:false PathLen: undefined ] [10]: ObjectId: 2.5.29.31 Criticality=false CRLDistributionPoints [ [DistributionPoint: [CN=CRL2049, OU=FNMT Clase 2 CA, O=FNMT, C=ES] ]] [11]: ObjectId: 2.5.29.15 Criticality=false KeyUsage [ DigitalSignature Key_Encipherment ] ] Algorithm: [SHA1withRSA] Signature: 0000: 7F F2 DF B5 7B 2E 36 12 89 AD 3E FB C6 48 A6 63 ......6...>..H.c 0010: 5E CC E6 92 C6 14 86 93 AD 5D 09 F3 3D 04 CC 37 ^........]..=..7 0020: 29 DE 97 22 32 EA FE 23 B1 1C 46 B3 60 A4 23 9C ).."2..#..F.`.#. 0030: 08 CB 6F B8 8A 74 92 FF 9A C5 5A 8F 87 8D 17 6C ..o..t....Z....l 0040: 2E 93 9A 3F 47 C1 77 38 24 00 0B 51 6F 26 29 3C ...?G.w8$..Qo&)< 0050: B0 CF EE 72 AE CE F9 45 72 BA 3D A6 57 ED DF C8 ...r...Er.=.W... 0060: 79 4F 1D B8 EF 4F 5B 98 95 3E 49 D3 7A FD 2D 9B yO...O[..>I.z.-. 0070: 85 F6 95 9F 0F A5 D8 FE EA 48 D3 A3 E7 0F FB 76 .........H.....v ] PKCS7 :: signer infos: 0. Signer Info for (issuer): OU=FNMT Clase 2 CA, O=FNMT, C=ES version: 01 certificateSerialNumber: 3c82ccbe digestAlgorithmId: SHA authenticatedAttributes: PKCS9 Attributes: [ [ContentType: 1.2.840.113549.1.7.1]; [MessageDigest: 0000: 63 94 C1 CE 6B 7D AD 3A 34 04 68 01 53 A5 96 BF c...k..:4.h.S... 0010: 1E F3 96 12 .... ]; [SigningTime: Wed Jun 20 12:17:33 CEST 2007] ] (end PKCS9 Attributes) digestEncryptionAlgorithmId: RSA encryptedDigest: 0000: 1A FD 86 01 B6 B6 85 27 87 FB AE 6C C0 78 FE 3C .......'...l.x.< 0010: 04 74 38 F2 F6 9B D2 29 46 47 EA 9F 36 E7 64 8D .t8....)FG..6.d. 0020: BB A2 A3 B5 0C A5 01 60 19 1C 30 09 3D EA BC 0C .......`..0.=... 0030: 63 4A 8A 2E BC C8 42 35 E1 3A D7 06 18 EB 38 3A cJ....B5.:....8: 0040: 89 B8 F8 BC 1E AC 0A 46 CE B5 1A 3D 6B F5 2C 2C .......F...=k.,, 0050: 8A FD B8 05 9D 8B 79 64 73 00 26 CE 65 38 97 01 ......yds.&.e8.. 0060: F9 3B 01 A4 11 58 FC B7 0E 27 7A D5 F6 7A 4F 69 .;...X...'z..zOi 0070: ED B2 20 3F 40 8A CA C1 CE FF 55 67 4E 3E F8 6B .. ?@.....UgN>.k [EXTRACTING DATA] No data found
|
Consideraciones
Recordemos que lo que firma el usuario es un texto, así que
si lo que queremos que firme es, por ejemplo, una solicitud de compra
de productos deberemos traducir el formulario o “carrito de la
compra” a un texto tipo “Don
Juan Perez, con DNI XXX se compromete a comprar el 30 de Junio de
2007 los siguientes productos: 2 Cartuchos de Tinta HP6546 y 1 Papel
500x90g por un valor total de 67 euros (IVA incluido) pagaderos en
efectivo y que serán enviados a C/Castellana, 1 Bajo C. En
Madrid, a los 20 dias del mes de junio de 2007.”
¿Dónde encuentro más info?
Mozilla Development Center: JavaScript crypto
http://developer.mozilla.org/en/docs/JavaScript_crypto
Introducing MS CAPICOM
http://msdn2.microsoft.com/en-us/library/ms995332.aspx
Conclusiones
Con la popularización de los certificados digitales (más
con llegada de los DNI electrónicos en España)
Muy buena herramienta, muy útil, muy estable y en constante
evolución.
En Autentia tenemos mucha experiencia en desarrollo Web y podemos
ayudarte a construir u optimizar tus portales o aplicaciones web. No
dudes en ponerte en contacto con nosotros.
Hola Javier, excelente tu articulo. Lo estuve estudiando para encontrar un vinculo entre lo que escribiste y lo que yo necesito porque realmente estoy perdido, pero creo que no es lo mismo. Yo estoy tratando de generar un certificado PKCS#10 desde javascript, es decir desde el lado del cliente. Con capicom de microsoft puedo si el usuario tiene IE pero si tiene FireFox utilizando parece complicado he tratado de hacerlo con Keygen tag o crypto.generateCRMFRequest() y no lo he logrado. Tienes algo que pueda ayudarme?
Hola Javier, muy bueno su articulo, estoy tratando de firmar un documento PDF con uno de los certificados de Mozilla. Como puedo hacerlo. Saludos y garcias de antemano
Hola!!!! gracias por tu artículo…… No domino mucho la creación de servlet, pero creo que lo he conseguido desde NetBeans, creando un proyecto nuevo Java Web y en web pages añadiendo el index.html y en source packages creando el servlet CompruebaFirmaDigital.java
Me he vuelto loco porque el crypto.signText parece ser que no funciona bien con la última versión de firefox, así que he instalado una antigua la 38.05 y le he instalado la extensión signTextJS 0.7.8…
Hasta ahí todo perfecto, pero cuando le doy a firmar me salta la ventanita que pones aquí con el texto de prueba, puedo seleccionar los certificados que tengo instalados en el navegador sin problemas, pero cuando le doy a firmar, me salta: «Su navegador no ha generado una firma válida» y no tengo ni idea de porqué es…… Cuando le doy a enviar, me salta: [ERROR VALIDATING SIGNATURE]…. pero me imagino que eso será normal, puesto que he obtenido el error anterior a la hora de firmar….. Un salido y gracias!!!!
Hola, soy yo de nuevo…. resulta que estaba probando con certificados generamos por mí y se ve que estan mál creados. He probado con uno de la fnmt y lo ha tomado sin problemas….
Bien… Ya tengo el texto codificado….. pero… ahora como lo decodificaría?????? me estoy volviendo loco y no encuentro ninguna orden para tal fin….. tiene que ser una tontería, pero…….