Firmar Mensajes

Firmar Mensajes

La mayoría de los mensajes que son enviados deben ser firmados usando JWS (JSON Web Signature). Para que el API sea legible, usamos detached signatures, lo que significa que el contenido a firmar es el cuerpo HTTP de los mensajes que se envían mediante peticiones POST. La firma se envía en una cabecera HTTP Shinkansen-JWS-Signature.

Algoritmo

Para firmar mensajes, usamos el algoritmo PS256 de JWA, que usa RSA (ampliamente usado y con soporte en variedad de plataformas y arquitecturas) en una versión moderna y más segura que lo ofrecido por el algoritmo RS256 de JWA.

👍 En la práctica:

En la mayoría de las librerías JWS deberás pasar el valor "PS256" en el parámetro/cabecera "alg" a la hora de generar la firma.

Certificado(s)

El uso de RSA requiere que cada Participante tenga una llave privada para firmar los mensajes y sus contrapartes tengan las llaves públicas correspondientes. Para facilitar el manejo seguro de estas llaves públicas, las asociamos a un certificado obtenido en un PSC o CA.

Para firmar un mensaje deberás usar la llave privada y enviar el certificado con la llave púbica.

👍 En la práctica:

Deberás incluir el certificado DER encodeado en base64 en el parámetro/cabecera "x5c" de JWS a la hora de generar la firma.

Esto es muy cercano al encoding PEM de certificados que quizás has visto. Son mas o menos así:

-----BEGIN CERTIFICATE-----
MIIGAjCCBOqgAwIBAgIIREUj+lcjuVcwDQYJKoZIhvcNAQELBQAwgbcxHjAcBgkq
hkiG9w0BCQEWD3NvcG9ydGVAaWRvay5jbDEfMB0GA1UEAwwWSURPSyBGSVJNQSBF
TEVDVFJPTklDQTEXMBUGA1UECwwOUlVULTc2NjEwNzE4LTQxIDAeBgNVBAsMF0F1
dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhMRkwFwYDVQQKDBBCUE8gQWR2aXNvcnMgU3BB
MREwDwYDVQQHDAhTYW50aWFnbzELMAkGA1UEBhMCQ0wwHhcNMjAwODEyMjIwNjIx
WhcNMjMwNTI4MTMwNzMwWjB6MSYwJAYDVQQDDB1MRU9OQVJETyBIVU1CRVJUTyBT
T1RPIE1Vw5FPWjEhMB8GCSqGSIb3DQEJARYSbGVvLnNvdG9AZ21haWwuY29tMRMw
EQYDVQQFEwoxNTYyMDMxOC0xMQswCQYDVQQGEwJDTDELMAkGA1UEBwwCUk0wggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDoRiVZ2Ya1JpcaIDJi3xLH8g0v
MF6XzvQONdTYZRnPXui3fk3+LM2YZ9w9xdck38H0i9Qp/aRAKvxmgPIGQuAWZLJy
9HujCCKH1EY8HWEcWCHPu2zy4puJV3jBB/mhkHvKbiBsriCVOFvHCQlcJOytQyOb
AtGbl2dMNzb2w5cavRnPkNaWQGo3BLY1gsoXTsKBGF2rDPmOPipEGcz9QHntz8qP
JLrYD2GMXZIwtjGzHP1+K2eP7NHyoTTApMOaDBkqfRmyXJ84goc6jyHCSuLyQJsl
2+A6nxctyENA9Hh4EAKLUU1E81rz0ovqrAxdYAi8Gg8MnF9cwI8MaLlEG1C7AgMB
AAGjggJMMIICSDAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFPBsM7+sl5NYeqHgzp7s
6N77ZT76MIGYBgNVHSAEgZAwgY0wgYoGCisGAQQBg4weAQQwfDAsBggrBgEFBQcC
ARYgaHR0cHM6Ly9wc2MuaWRvay5jbC9vcGVuL2Nwcy5wZGYwTAYIKwYBBQUHAgIw
QB4+AEMAZQByAHQAaQBmAGkAYwBhAGQAbwAgAHAAYQByAGEAIAB1AHMAbwAgAFQA
cgBpAGIAdQB0AGEAcgBpAG8wggEHBgNVHR8Egf8wgfwwgfmgN6A1hjNodHRwczov
L3BzYy5pZG9rLmNsL29wZW4vSURPSyBGSVJNQSBFTEVDVFJPTklDQS5jcmyigb2k
gbowgbcxHjAcBgkqhkiG9w0BCQEWD3NvcG9ydGVAaWRvay5jbDEfMB0GA1UEAwwW
SURPSyBGSVJNQSBFTEVDVFJPTklDQTEXMBUGA1UECwwOUlVULTc2NjEwNzE4LTQx
IDAeBgNVBAsMF0F1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhMRkwFwYDVQQKDBBCUE8g
QWR2aXNvcnMgU3BBMREwDwYDVQQHDAhTYW50aWFnbzELMAkGA1UEBhMCQ0wwHQYD
VR0OBBYEFFh0yhCCXYJ7OUn2dAPaZUOjfT0iMAsGA1UdDwQEAwIEkDAjBgNVHRIE
HDAaoBgGCCsGAQQBwQECoAwWCjc2NjEwNzE4LTQwIwYDVR0RBBwwGqAYBggrBgEE
AcEBAaAMFgoxNTYyMDMxOC0xMA0GCSqGSIb3DQEBCwUAA4IBAQBxPD7ETW2rK/zH
WEzMM0HKirpqU4Hf8GIErjHHHXukeUjPidNjnWq7aGGAW5sLyYPDhuFSEF/YBSau
9m6fuerwisFFd9n1hjjAQPmKb0MltajPXH9vdIXk4+A7g1W5Stbq1Ezt8E32+Zv9
17l36d33P8wwjkkPkmW14LSm9GwLvwULNkZrocgb4o8oIIzhqQSs/f+qo9rezs6S
WMmbbubDDdOm+HRBao2yjbKilFcMiIA35AJPVUZdSUc/6MzyoA0n0KePXnFQ93HR
S1BRayUnRU+8sNvKVrI7gaSVckYP1xO+747mDzyAYHYAAkE9LI1uA3VwRj5lWuEq
i4eytLag
-----END CERTIFICATE-----

Eso ya está base64-encodeado, pero con saltos de línea cada 64 caracteres y con delimitadores. Este mismo certificado en una cabecera JWS se ve así:

Otros parámetros JWS

Para mayor eficiencia (y claridad), evitamos encodear en base64 el mensaje JSON a firmar. Esto implica usar la extensión b64 de JWS, que es parte del RFC 7797.

👍 En la práctica:

En la mayoría de las librerías JWS deberás pasar el valor false en el parámetro "b64" a la hora de generar la firma. Es posible que también debas pasar explícitamente el parámetro "crit" con valor ["b64"].

Generar la firma

Si la librería que usas no tiene soporte nativo para generar detached signatures, deberás generar una representación JSON del JWS (con los campos protected, payload, y signature). Luego deberás concatenar el valor de protected con ".." y luego signature (el payload no se incluye).

Ejemplos

En la práctica esto es bastante mecánico usando librerías existentes. Puedes ver el ejemplo mas abajo en Python como referencia.

O aún mejor, puede resultar en muy pocas líneas usando librerías/wrappers que hemos creado, como el ejemplo Python que usa python-shinkansen más abajo.

{/* prettier-ignore-start */}

Pruebas

Para probar, te recomendamos usar nuestra librería de referencia en Python. Para eso te la puedes bajar via:

Las pruebas requieren que cuentes con un certificado y una llave privada. Si no tienes aún tu certificado real o de pruebas, puedes generar archivos para pruebas locales de esta forma (asumiendo que tienes openssl instalado):

Te pedirá una password para proteger el archivo de la llave privada. Aunque sea un certificado para jugar, te recomendamos protegerlo con una passephrase para nunca perder la costumbre de manejar estas cosas con mucha precaución.

También te pedirá indicar información para el certificado. No lo dejes en blanco, o fallará la creación del certificado.

Después de este proceso, tendrás dos archivos generados:

  • test-key.pem: Llave privada RSA de pruebas

  • test-cert.pem: Certificado auto-firmado para probar

Ahora podemos usar la librería de referencia (recuerda tenerla instalada via pip install python-shinkansen) para hacer algunas pruebas simples:

Eso va a generar un archivo jws.txt.

Ahora verifiquemos la firma:

Si no te aparece ningún error, entonces significa que la firma está correcta. Veamos que pasa si modificamos el contenido:

Debieras observar un error que termina con:

Si pruebas con otras permutaciones (ej: cambiando el certificado usado para generar el JWS y el usado para validarlo, o modificando jws.txt) también obtendrás errores.

📘 Las firmas con PS256 son probabilístcas

Eso significa que si vuelves a firmar el mismo payload.txt con la misma llave y certificado... ¡obtendrás una firma distinta! Por ende no puedes comparar dos firmas para ver si todo está ok. Lo que se debe hacer es verificar la firma.

Ahora que cuentas con estas herramientas puedes :

  • Generar tus propias firmas JWS y chequear si se verifican correctamente con python3 -m shinkansen.jws verify ...

  • Generar una firma con python3 -m shinkansen.jws sign ... y chequear si la puedes verificar correctamente con tu código.

👍 Todo esto sólo es necesario si debes o quieres escribir tu propio código

¿Quizás nuestras librerías no soportan las tecnologías que estás usando? Avísanos y trataremos de escribir una que te sirva.

Última actualización