ConsorciAOC / signador

Documentació del projecte Signador
https://signador.aoc.cat/signador/init
MIT License
15 stars 5 forks source link

Valid - Signador

Servei de Signatura Electrònica basada en certificats digitals

Documentació del projecte Signador

Per a poder utilitzar el servei és necessari donar-se d'alta previament, per a fer-ho és necessari facilitar la següent informació:

Diagrama de flux

Existeixen dues formes d'integrar-se amb el servei, la part inicial és comú en ambdues, només canvia la forma en què l'aplicació recupera la signatura. Ambdues formes es descriuen més avall en aquest mateix document. Per tal de donar context i entendre els dos mètodes de funcionament del servei, a continuació es mostren els diagrames de flux d'operació d'una aplicació contra el Signador per a intentar il·lustrar les crides i el funcionament del mateix per ambdós casos:

Diagrama de flux amb redirect

Diagrama flux signador amb redirect

Diagrama de flux amb callback

Diagrama flux signador amb callback

Ús del servei

Per a utilizar el servei de signatura serà necessari la realització de les següents crides:

1. InitProcess: Servei per iniciar el procés de signatura

Cada operació de signatura requerirà d'un token per tal de poder iniciar el procés. El procés de signatura des del punt de vista de l'aplicació client és un procès asíncron per tant aquest token servirà per lligar després la signatura resultant amb el procés intern que l'ha requerit dins de l'aplicació client. Aquest token també identificarà la signatura a nivell intern del servei de Signador per tal de poder per exemple gestionar els errors si fos el cas, etc.

Per tal d'aconseguir el token s'ha de fer una crida al servei REST ubicat al següent endpoint:

La crida és simplement un GET amb el qual s'han d'enviar obligatòriament les següents capçaleres http (No confondre amb els paràmetres del GET):

La resposta del servei REST tindrà el següent format:

{
    "status": "",
    "token": "",
    "message": ""
}

Els possibles valors dels camps són:

Es comprovarà que la data proporcionada a la capçalera Date estigui dins del rang now -1 hora < Date < now +1 hora

Nota: Donat que es tracta d'una autenticació aquesta crida no suporta el CORS i per tant la crida s'ha de fer des del backend.

1.1. http-header: Authorization - HMAC SHA256

Per a calcular la capçalera d'autorització es fa servir el Message Authentication Code (MAC) basat en una funció de resum criptogràfic (HMAC), en aquest cas com a funció de Hash farem servir SHA256.

En aquest cas les dades a processar serà el mateix nom del domini tal i com s'ha especificat a l'alta, concatenat amb el caràcter underscore _ i la data proporcionada a la capçalera date (exemple http://ajuntament.cat_28/05/2016 13:21). El secret per a procesar aquesta dada amb l'algoritme HMAC_SHA256 serà la clau que s'ha triat també durant el procés d'alta.

A continuació és mostra un exemple simplificat de com es podria generar la capçalera d'autenticació amb Groovy:

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

def clau = 'changeit'
def algoritme = 'HmacSHA256'
def mac = Mac.getInstance(algoritme)

def domini = 'http://ajuntament.cat'
def date = new Date().format('dd/MM/yyyy HH:mm')
def dades = "${domini}_${date}"

def secretKeySpec = new SecretKeySpec(clau.getBytes(), algoritme)
mac.init(secretKeySpec)
byte[] digest = mac.doFinal(dades.getBytes())
digest.encodeBase64().toString()

De la mateixa forma en Java:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64.Encoder;
import java.text.SimpleDateFormat;
import java.util.Date;

public class BustiaNotificacionsController {

     public static void main(String args[]){
          String domini = "http://ajuntament.cat";
          SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
          String date = sdf.format(new Date());
          String dades = domini + "_" + date;

          String clau = "legalizeit";
          String algoritme = "HmacSHA256";
          Mac mac = Mac.getInstance(algoritme);
          SecretKeySpec secretKeySpec = new SecretKeySpec(clau.getBytes(), algoritme);
          mac.init(secretKeySpec);
          byte[] digest = mac.doFinal(dades.getBytes());
          Encoder encoder = java.util.Base64.getEncoder();
          System.out.println(encoder.encodeToString(digest));
     }
}

Nota: La classe java.util.Base64 existeix a partir de la versió 8 de Java, si es desenvolupa amb una altre versió és pot utilitzat qualsevol altre codificador en Base64 com per exemple el javax.xml.bind.DatatypeConverter que es troba dins de la versió 6 i 7 de Java. O el org.apache.commons.codec.binary.Base64 del Apache Commons Codec, o tants d'altres.

Proveïm aquests codis a tall d'exemple, per veure exemples en altres llenguatges de programació de com calcular el HMAC podeu consultar el següent recurs

Podeu trobar també un exemple complet en Groovy de com invocar el /initProcess per aconseguir el token de l'operació aqui

2. StartSignProcess: Servei per realitzar el procés de signatura de l'applet o de l'apsa segons la configuració

Un cop és diposa del token per a l'operació de signatura, es pot iniciar el procés. Per tal de fer-ho es necessari associar la configuració de signatura que realitzarà l'usuari amb el token d'operació obtingut.

Per a fer-ho, s'ha de realitzar una crida al servei REST al següent endpoint:

En aquesta crida també és necessari afegir la capçalera http Origin amb el nom del domini. Si la crida és fa des de Javascript utilitzant domini registrat els pròpis navegadors per un tema de seguretat ja afegeixen la capçalera a la crida, veure CORS.

2.1. StartSignProcess: Applet de signatura

La crida consisteix en un POST on s'envia un objecte JSON, aquest objecte per a iniciar el procés de signatura amb l'applet té la següent forma. És important remarcar que en funció de si s'informa el camp callbackUrl o redirectUrl canviarà la gestió del flux del usuari i la recuperació de la signatura per part de l'aplicació client.

{
    "callbackUrl": "" o "redirectUrl": "", // S'ha d'informar o un o l'altre
    "token": "",
    "descripcio": "",
    "responseB64": "",
    "applet_cfg":{
        "keystore_type": "",
        "signature_mode": "",
        "doc_type": "",
        "doc_name": "",                 
        "document_to_sign": "",
        "hash_algorithm": "",
        "pkcs11_files": "",
        "multiple": "",
        "pdf_cfg": {
            "pdf_visible_signature": "",
            "pdf_reserved_space": "",
            "pdf_signature_field": "", 
            "pdf_certification_level": "",
            "pdf_reason": "",
            "pdf_location": "",
            "pdf_signature_image": "",
            "pdf_signature_rectangle": "",
            "pdf_signature_page_number": "",
            "pdf_signature_rotation": "",
            "pdf_show_adobe_sign_validation": "".
            "pdf_signature_rendering_mode": ""
        },
        "certs_cfg": {
            "allowed_CAs": "",
            "allowed_OIDs": "",
            "selected_alias": "",
            "selected_CN": "",
            "subject_Text": "",
            "required_nif": "",
            "psis_validation": "",
            "keyUsage": ""
        },
        "xml_cfg": {
            "n_enveloping": "",
            "n_detached": "",
            "uris_to_be_signed": "",
            "includeXMLTimestamp": "",
            "xmlts_tsa_url": "",
            "canonicalizationWithComments": "",
            "protectKeyInfo": ""
        },
        "cms_cfg": {
            "timeStamp_CMS_signature": "",
            "cmsts_tsa_url": ""
        },
        "ades_cfg": {
            "commitment_identifier": "",
            "commitment_description": "",
            "commitment_object_reference": "",
            "signer_role": "",
            "signature_policy": "",
            "signature_policy_hash": "",
            "signature_policy_qualifier": "",
            "signature_policy_hash_algorithm": ""   
        }
    }
}

Al següent apartat és descriu amb més detall l'ús de cadascún d'aquests camps, notis només que la gran part dels mateixos és opcional i no és necessari enviar-los per a poder iniciar el procés correctament.

Podeu trobar també un exemple simple en Groovy de com invocar el /startSignProcess passant una configuració de signatura d'exemple aqui

2.2. StartSignProcess: Applet de PSA (APSA light)

Per al cas d'iniciar el procés per a carregar l'applet de PSA, l' objecte JSON a enviar té la següent forma. És important remarcar que en funció de si s'informa el camp callbackUrl o redirectUrl canviarà la gestió del flux del usuari i la recuperació de la signatura per part de l'aplicació client.

L'Applet de PSA, també disposa de dos modes de funcionament. Un mode per a recuperar un certificat de clau pública que és retornarà en base64, o el mode de signatura de hashos. El camp que indica quina operació és vol realitzar és el camp modeFuncionament.

{
    "callbackUrl": "" o "redirectUrl": "", // S'ha d'informar o un o l'altre
    "token": "",
    "descripcio": "",
    "responseB64": "",
    "applet_apsa_cfg": {
            "keystore_type": "",
            "doc_name": "",
            "modeFuncionament":"",
            "hash_a_xifrar": "",
            "signingCertificate": ""
    }
}

2.3. Camps comuns de la configuració

Descripció dels camps JSON comuns de la configuració:

Nota: És obligatori informar el camp callbackUrl o el redirectUrl, no s'han d'informar els dos.

2.4. Camps de la configuració de l'Applet

Descripció dels camps JSON de la configuració de l'applet:

2.5. Camps de la configuració de l'APSA

Descripció dels camps JSON de la configuració de l'apsa:

En cas que es vulgui signar més d'un document o hash el servei ho permet, posant els diferents documents o hashos separats per ; (al camp hash_a_xifrar) amb els seus respectius noms també separat per ; (al camp doc_name). El número d'elements d'aquests dos camps ha de coincidir. Els noms dels documents no poden coincidir.

2.6. Possibles valors dels camps de configuració:

Els possibles valors acceptats del keystore_type són:

Els possibles valors acceptats del signature_mode són:

Els possibles valors acceptats del doc_type són:

2.7. Filtres de certificats: certs_cfg

L'objecte certs_cfg és opcional i permet especificar filtratges a l'hora de seleccionar el certificat per part de l'usuari. Si el filtre és prou específic (exceptuant el paràmetre keyUsage) per donar només una coincidència; a l'usuari no se li mostrarà el diàleg de selecció de certificats i se seleccionarà de forma automàtica la coincidència, en cas que n'hi hagi més d'un és mostrarà el diàleg de selecció amb els certificats que coincideixin amb el criteri proporcionat:

2.8. Aparença i configuració de sigantures PDF: pdf_cfg

2.9. Paràmetres de signatura XML: xml_cfg

2.10. Paràmetres de signatura CMS: cms_cfg

2.11. Paràmetres de polítiques per als formats avançats de signatura XAdES i CAdES: ades_cfg

3. AddDocumentToSign: Signatura múltiple

Aquesta crida permet afegir documents a processos de signatura de tipus Applet inicialitzats amb el flag multiple = "true" pels quals encara no s'ha efectuat la signatura. El procés de signatura pot contenir més d'un document, especificat durant la inicialització o bé afegit amb "addDocumentToSign". El nou document a afegir haurà de respectar el format indicat al camp doc_type especificat a la inicialització del procés de signatura, que només pot ser per a aquests casos 3(hashDoc), 4 (B64fileContent) o 6 urlFile. Aquest servei pot ser executat múltiples vegades mentre no es realitzi la signatura. La crida consisteix en un PUT on s'envia un objecte JSON amb la següent forma:

{
    "token_id": "",
    "document_to_sign": "",
    "doc_name": ""
}

Descripció dels camps JSON de la configuració del servei de signatura múltiple:

En aquesta crida també és necessari afegir la capçalera http Origin amb el nom del domini. Si la crida és fa des de Javascript utilitzant domini registrat els pròpis navegadors per un tema de seguretat ja afegeixen la capçalera a la crida, veure CORS.

4. StartSignProcess: Resposta

La resposta del servei REST a aquestes crides tindrà el següent format:

{
    "status": "OK/KO",
    "token": "12345",
    "message": ""
}

Els possibles valors dels camps:

5. Signatura per part de l'usuari

Un cop s'ha aconseguit el token i creada la configuració de signatura vinculada al mateix, l'aplicació client ha de redirigir l'usuari a la web del Signador per tal de que aquest pugui acabar realitzant la signatura. Per tal de fer-ho s'ha de realitzar un GET passant com a paràmetre un id amd el valor del token a la següent URL:

Aquesta plana s'encarregarà de la creació de signatura per part de l'usuari a través d'un JNLP o d'una aplicació nativa que properament estarà disponible.

Si l'usuari té instal·lada l'aplicació nativa la signatura és realitzara amb aquest component, cas que no la signatura es farà a través del JNLP

El temps màxim permès per processar la petició és de 5 minuts. Si el client no ha generat la signatura passat aquest temps, la petició es donarà per finalitzada amb error de timeout.

En l'apartat de compatibilitat s'explica les compatibilitat i el funcionament d'aquests dos mètodes per a realitzar la signatura.

6. Recuperar la signatura per part de l'aplicació

Un cop el client hagi realitzat la signatura a través del JNLP, el servei del signador rebrà la signatura i en funció de la configuració retornarà la signatura d'una forma o un altre. Els paràmetres que marquen la configuració del retorn són callbackUrl o redirectUrl, la diferència s'explica a continuació.

6.1 Opció 1: redirectUrl : Redirecció GET

En cas que en el /StartSignProcess s'hagi informat el paràmetre redirectUrl, l'aplicació del signador farà una redirecció a la url informada retornant el flux a l'aplicació client. En la url de redirecció, s'afegira el paràmetre token_id amb el valor del token perquè l'aplicació pugui saber de quina operació és tracta, per exemple https://applicacio/redirect?token_id=bec40de2-510f-4f19-bdfd-2a6595d708b7.

Un cop la aplicació client prengui el control podrà demanar la resposta de l'operació a través del servei REST /getSignature descrit a continuació.

6.1.1 getSignature: Servei REST per consultar el resultat de l'operació

Per tal d'obtenir la resposta de la signatura s'ha de fer una crida al servei REST ubicat a la següent URL:

La crida és simplement un GET passant com a paràmetre un identificador amb el valor del token rebut en la url de redirecció, igual que la resta de crides també ha d'incloure les següents capçaleres:

La resposta d'aquest servei tindrà el següent format.

{
   "status": "OK/KO",
   "token": "id del token",
   "signResult": "resultat de la signatura",
   "type": "XML/CMS/PDF/HASH/TXT/ZIP/CERT",
   "error": "motiu de l'error",
   "urlRedirect" : "http://redirect.cat?token=XXXXXX"
}

Els possibles valors dels camps:

En cas que l'operació sigui de Multisignatura, es a dir que el client faci varies signatures en una mateixa operació, la resposta del servei tindrà una unica resposta amb el token igual que es fa amb signatures simples. La diferència serà que en aquesta cas la resposta serà un document ZIP que contindrà les diferents signatures generades.

L'altre cas singular, és el de l'Applet de PSA en mode CERTIFICAT, en el qual el type tindrà el valor CERT i en el signResult recuperarem el certificat seleccionat per l'usuari en base64.

NOTES:

6.2 Opció 2: callbackUrl : Callback POST

A diferència de l'opció 1, en cas que l'aplicació client hagi informat el paràmetre callbackURL, quan l'usuari hagi finalitzat la signatura el servei respondrà a l'aplicació client utilitzant la URL de callback que s'hagi informat en els paràmetres de configuració i facilitarà la signatura en aquell endpoint via POST. El servei retornarà la resposta amb la signatura generada en cas que hagi anat bé o el motiu de l'error en cas que no.

El format del JSON que enviarem a l'endpoint informat será el següent:

{
   "status": "OK/KO",
   "token": "id del token",
   "signResult": "resultat de la signatura",
   "type": "XML/CMS/PDF/HASH/TXT/ZIP/CERT",
   "error": "motiu de l'error"
}

Els possibles valors dels camps:

Serà necessari per tant per part de l'aplicació client d'implementar un endpoint que accepti rebre un POST amb el contingut del JSON especificat en aquesta punt. Amb la resposta anirà la capçalera http Content-Type: application/json.

En cas que l'operació sigui de Multisignatura, es a dir que el client faci varies signatures en una mateixa operació, l'aplicació rebrà una unica resposta amb el token igual que es fa amb signatures simples. La diferència serà que en aquesta cas la resposta serà un document ZIP que contindrà les diferents signatures generades.

NOTES:

6.3 URL descàrrega

Per alleugerir el pes de la response a getSignature per l'opció 1: redirectUrl, o del POST en el cas de l'opció 2: callbackUrl és possible iniciar el procés indicant en el paràmetre responseB64 amb valor false, d'aquesta forma en la resposta de l'operació es rebrà en el signResult una URL en comptes del resultat en Base64, amb la qual es podrà descarregar la resposta realitzant simplement un GET incloent les següents capçaleres http:

D'aquesta forma és podrà reduïr en els casos necessaris el pes de la resposta i agilitzar la comunicació.

NOTES:

6.4 Conclusions

La primera solució implementada va ser la Opcio 2: callbackUrl : Callback POST, desprès però de veure les necessitats de les aplicacions, la problemàtica que genera aquesta solució (possibles errors de timeout en el POST de resposta, polling ajax de l'aplicació client per tal de mantenir l'estat de l'operació, ...) i el fet de que alguns clients ens han traslladat el seu neguit al respecte s'ha decidit implementar l'altre via: Opcio 1: redirectUrl : Redirecció GET aquesta via és més neta, genera menys trafic i per tant té un millor rendiment, i permet un millor flux de cara a l'usuari per a la gestió de la signatura. Per tant recomanem en la mesura del possible utilitzar la opció del redirectUrl.

7. Demo / Serveis integrats

Podeu veure una Demo d'una integració del servei amb les dues modalitats als següents enllaços:

Opcio 1: redirectUrl : Redirecció GET

Opcio 2: callbackUrl : Callback POST

A banda de la Demo a tall d'exemple també es mostren els enllaços del Signasuite que és un servei de validació/creació de signatures etc. que properament estarà també integrat amb el servei del signador:

8. Recomanacions/Restriccions

9. Compatibilitat

En aquest apartat podreu trobar els enllaços a la informació sobre la pròpia aplicació així com les eines que portaran a terme la signatura en la màquina d'usuari, en aquestes guies s'explica la compatibilitat del servei i de les eines pel que fa a versions de sistemes operatius, navegadors, etc que suporta:

9.1 Servei del signador

9.2 JNLP

9.3 Nativa

Llibreria integradors

Per a facilitar el procés d'integració posem a disposició dels integradors una llibreria feta en Javascript per a poder-se integrar en el servei. Trobareu més detall sobre la mateixa aqui.