firma-gob
Version:
Firma documentos con plataforma Firma.Gob del gobierno Chile
495 lines (336 loc) • 12.5 kB
Markdown
# Firma Gob
Librería javascript para firmar documentos usando la API de Firma.Gob (Chile)
instalar
```js
// npm
npm install firma-gob
// yarn
yarn add firma-gob
```
Importa la librería en tu código
## FirmaGob
```js
const { FirmaGob, File, PDF } = require("firma-gob");
// o
import { FirmaGob, File, PDF } from "firma-gob";
```
Por defecto la librería inicia con los parámetros de desarrollo (certificado desatendido), por lo que puedes comenzar a usarla para pruebas directamente
```js
// tuarchivo.js
// Ejemplo firma archivos PDF, firma gob recomienda firmar un hash
const { FirmaGob, File } = new FirmaGob();
const gob = new FirmaGob();
const remote = await File.fromRemote("linkToPdf");
gob.addPDF(remote.base64, remote.checksum); // agrega pdf
// o también desde un archivo local
const local = File.fromLocal("pathToPdf");
gob.addPDF(local.base64, local.checksum); // agrega pdf
// firmar documentos y recibir respuesta
const output = await gob.signFiles();
// guarda archivos con la clase File
output.files.forEach((item) => {
file.base64ToDisk(`file_${item.checksum_original}.pdf`, item.content);
});
```
luego ya puedes ejecutar `node tuarchivo.js`
## Firmar un hash
```js
// tuarchivo.js
const { FirmaGob, File } = new FirmaGob();
const gob = new FirmaGob();
const hash = await File.fromRemoteToHash("linkToPdf"); // agrega hash
gob.addHash(hash);
// o también desde un archivo local
const hash = File.fromLocalToHash("pathToPdf");
gob.addHash(hash); // agrega hash
// firmar documentos y recibir respuesta
const output = await gob.signHashes();
// agregar firma a archivo PDF
const hashes = output.hashes; // array de hashes
```
# FirmaGob
Actualmente la clase `FirmaGob` cuenta con los siguientes métodos
### setConfig
modifica los parametros de configuración de la librería.
> Al modificar estos parámetros automáticamente la librería pasa a modo **producción**
- **run** identificador del titular de firma, no debe contener puntos, guión ni tampoco el dígito verificador
- **entity** código asociado a la institución a la cual pertenece el titular
- **api_token** campo no encriptado de tipo string que contiene el código único generado a partir del registro de la aplicación
- **secret** secreto generado por firma.gob al registrar la aplicación
```ts
gob.setConfig(run: string, entity: string, api_token: string, secret: string)
```
### setPurpose (Propósito Desatendido por defecto)
Establece si el certificado es de proposito general o desatendido
- **purpose** código asociado al tipo de certificado a utilizar
Parámetros permitidos:
- Purpose.ATENDIDO (Propósito general)
- Purpose.DESATENDIDO (Desatendido)
```ts
gob.setPurpose(purpose: Purpose)
```
### addJSON
Agrega un archivo JSON a la lista de archivos
- **content** Archivo en base64
- **checksum** SHA256 del archivo
```ts
gob.addJSON(content: string, checksum: string)
```
### addPDF
Agrega un archivo PDF a la lista de archivos
- **content** Archivo en base64
- **checksum** SHA256 del archivo
- **layout** string opcional en caso de desear incrustar elemento al archivo PDF
```ts
gob.addPDF(content: string, checksum: string, layout?: string)
```
### addHash
Agrega un hash a la lista de archivos
- **hash** hash del archivo a firmar
```ts
gob.addHash(hash: string)
```
### addXML
Agrega un archivo PDF a la lista de archivos
- **content** Archivo en base64
- **checksum** SHA256 del archivo
- **references** array de string con la identificación del nodo a firmar en caso de ser un archivo XML ejemplo: [“#nodo1”, “#nodo2”]
- **xmlObjects** array de string con los pie de firma en un archivo XML ejemplo: `["<a></a>","<b/>”]`
```ts
gob.addXML(content: string, checksum: string, references: string[], xmlObjects: string[])
```
### addFiles
Agrega multiples archivos a la lista de archivos a ser firmados
- **files** Lista de archivos a firmar
```ts
gob.addFiles(files: FileProps[])
```
### signFiles
Firma los archivos previamente establecidos
- **otp** Si la firma es de propósito general necesitas enviar el código OTP
- **Respuesta** con documentos firmados o errores, para este método siempre existirá una clave **files** con los archivos firmados
```ts
gob.signFiles(otp?: string)
```
### signHashes
Firma los hashes previamente establecidos con `addHash`
- **otp** Si la firma es de propósito general necesitas enviar el código OTP
- **Respuesta** con documentos firmados o errores, para este método siempre existirá una clave **hashes** con los certificados que deben ser agregados al archivo PDF
```ts
gob.signHashes(otp?: string)
```
## PDF
La clase `PDF` te permite preparar e inyectar firmas digitales en tus documentos de forma sencilla.
Funciona como complemento de `FirmaGob`, recibiendo la firma en `base64` generada por el método de firma con hash y añadiéndola incrementalmente al PDF para mantener válida la primera o las firmas sucesivas.
### Importación
```ts
import { PDF } from "firma-gob";
```
### Tipos y configuración inicial
#### `SignerInfo`
Define los parámetros visuales y de posición del widget de firma:
```ts
export type SignerInfo = {
reason: string; // Motivo que aparecerá en la firma
width: number; // Ancho del área de firma (en pts)
height: number; // Alto del área de firma (en pts)
x: number; // Coordenada X (desde la esquina inferior izquierda)
y: number; // Coordenada Y
};
```
### Métodos principales
Carga un PDF existente desde un buffer en memoria.
```ts
await pdf.loadFromBuffer(pdfBytes);
```
#### `setSigner(signer: SignerInfo): void`
Define la información del firmante (posición, tamaño y motivo).
```ts
pdf.setSigner({
reason: "Revisión Contable",
width: 230,
height: 80,
x: 50,
y: 650,
});
```
#### `addImage(name: string, imageBytes: Uint8Array): Promise<void>`
Inyecta una imagen al PDF y la registra.
```ts
await pdf.addImage("Git", signatureImagen);
```
#### `enableOpacity(): void`
Habilita el uso de estados gráficos (`ExtGState`) para soportar opacidad en los operadores.
```ts
pdf.enableOpacity();
```
#### `setOperators(operatorFn: (regular: FontInfo, bold: FontInfo) => PDFOperator[]): void`
Define la secuencia de operadores gráficos que formarán el contenido visual del widget de firma.
- Recibe dos objetos `FontInfo` el primero con Helvetica Regular y el Segundo con Helvetica Bold. El objeto contiene su nombre y ` PDFFont`para ser usado con `drawTextSegments`
- Debe devolver un array de `PDFOperator` (puede usar helpers como `pushGraphicsState()`, `drawRectangle()`, `drawImage()`, `drawTextSegments()`, `popGraphicsState()`, etc.).
```ts
pdf.setOperators((regular, bold) => [
pushGraphicsState(),
setGraphicsState(PDFName.of("Opacity")),
...drawRectangle({
x: 0,
y: 0,
width: signer.width,
height: signer.height,
color: rgb(0.95, 0.95, 0.95),
borderWidth: 1,
borderColor: rgb(0.9, 0.9, 0.9),
rotate: degrees(0),
xSkew: degrees(0),
ySkew: degrees(0),
}),
popGraphicsState(),
...drawImage("Git", {
x: 10,
y: (signer.height - 40) / 2,
width: 40,
height: 40,
rotate: degrees(0),
xSkew: degrees(0),
ySkew: degrees(0),
}),
setCharacterSpacing(-0.8),
...drawTextSegments(
[
{ segments: [{ texto: "Firmado por", fontName: regular.name }] },
{
segments: [
{
texto: "Guillermo Parraguez",
fontName: bold.name,
font: bold.font,
},
],
},
{ segments: [{ texto: "Coordinador de Transformación Digital" }] },
{
segments: [
{ texto: "Fecha", fontName: bold.name, font: bold.font },
{ texto: new Date().toISOString() },
],
},
{ segments: [{ texto: "Ilustre Municipalidad de Diego de Almagro" }] },
],
{
x: 65,
y: 55,
fontName: regular.name,
font: regular.font,
size: 7,
color: rgb(0, 0, 0),
}
),
]);
```
Nota: `drawTextSegments` es una función que permite generar texto con diferentes fuentes (regular, Bold) en una misma linea, tal como el ejemplo anterior
#### `getPreparedPDF(): Promise<Uint8Array>`
Devuelve el buffer del PDF **preparado** para firmar (contiene AcroForm, campos, widgets y aparición visual). Se usa para calcular el hash antes de la firma.
```ts
const prepared = await pdf.getPreparedPDF();
```
#### `sign(signedHashes: SignatureOutput[]): Uint8Array`
Aplica la firma PKCS#7 (base64) retornada por la API y devuelve el PDF firmado final.
```ts
const signedPDF = pdf.sign(outputFromFirmaGob);
```
### Ejemplo completo de uso
```ts
import { FirmaGob, File, PDF, SignerInfo } from "firma-gob";
const signer: SignerInfo = {
reason: "Revisión Contable",
width: 230,
height: 80,
x: 50,
y: 650,
};
const main = async () => {
const gob = new FirmaGob();
const pdf = new PDF();
const file = new File();
// Leer archivos
const signatureImagen = File.readFile("./signature.png");
const pdfBuffer = File.readFile("./Testing PDF.pdf");
// Preparar PDF
await pdf.loadFromBuffer(pdfBuffer);
pdf.setSigner(signer);
await pdf.addImage("Git", signatureImagen);
pdf.enableOpacity();
pdf.setOperators((regular, bold) => [
/* ...operadores como se mostró arriba... */
]);
// Obtener PDF preparado y firmarlo
const prepared = await pdf.getPreparedPDF();
const hash = File.fromBufferToHash(prepared);
gob.addHash(hash);
const signedOut = await gob.signHashes();
const signedPDF = pdf.sign(signedOut);
// Guardar resultado
File.base64ToDisk("./documento-final-firmado.pdf", signedPDF);
console.log("PDF final guardado en ./documento-final-firmado.pdf");
};
main();
```
# File
La clase `File` te ayudará a manipular tus archivos para ser usados con `FirmaGob`, todos sus métodos son estáticos
### fromLocal
Obtiene los datos de un archivo local y los convierte en base64
- **path** Ruta del archivo en tu disco
- **Respuesta** objeto { base64, checksum }
```ts
File.fromLocal(path: string)
```
### fromLocalToHash
Obtiene los datos de un archivo local y calcula su hash (cheksum)
- **path** Ruta del archivo en tu disco
- **Respuesta** string hash (checksum)
```ts
File.fromLocalToHash(path: string)
```
### fromRemote
Obtiene un archivo pdf desde un servidor remoto y lo convierte a base64
- **url** URL del archivo PDF
- **Respuesta** objeto { base64, checksum }
```ts
File.fromRemote(url: string)
```
### fromRemoteToHash
Obtiene los datos de un archivo desde un servidor remoto y calcula su hash (cheksum)
- **path** Ruta del archivo en tu disco
- **Respuesta** string hash (checksum)
```ts
File.fromRemoteToHash(url: string)
```
### base64ToBuffer
Convierte un archivo en base64 a buffer
- **base64** archivo en base64
- **Respuesta** buffer de archivo
```ts
File.base64ToBuffer(base64: string)
```
### bufferToDisk
Usa el buffer dado y lo almacena en el disco con el nombre especificado
- **filename** nombre del archivo a guardar
- **buffer** buffer de archivo
```ts
File.bufferToDisk(filename: string, buffer: Buffer)
```
### base64ToDisk
Almacena en el disco un archivo en base64
- **filename** nombre del archivo a guardar
- **base64** archivo en base64
```ts
File.base64ToDisk(filename: string, base64: string)
```
# Desarrollo
La librería está escrita con typescript, al modificar el `index.ts` debes ejecutar `tsc` para recompilar el archivo `index.js` (debes tener instalado tsc)
Todos los comentarios y PR son bienvenidos.
La idea es ir complementando la librería para poder manejar con mayor facilidad cada archivo
# Más información
Para obtener más información acerca de esta API pudes descargar los manuales en https://firma.digital.gob.cl/biblioteca/manuales-firmagob/
# Licencia
Este proyecto está liberado bajo la licencia MIT, quiere decir que puedes hacer lo que quieras (incluso comercialmente)