Cómo Firmar PDFs Utilizando Certificados Digitales en Node.js, JavaScript y TypeScript
Introducción
En esta guía, veremos cómo firmar PDFs digitalmente utilizando un entorno de Node.js. Cubriremos cómo hacerlo con JavaScript o TypeScript usando una API externa llamada pdfsignify.com, que simplifica el proceso de firma digital y permite personalizar fácilmente la apariencia de las firmas.
Las firmas digitales garantizan la autenticidad e integridad de tus PDFs. Para firmar un PDF, necesitarás un certificado digital en formato
.pfx o .p12, que contiene tu clave privada y certificado. También necesitarás la contraseña asociada al archivo del certificado.Cubriremos desde la configuración del servidor hasta la solicitud API para firmar PDFs, de forma que puedas integrar firmas digitales en tus propias aplicaciones.
Parte 1: Configuración de un Servidor Node.js
En esta parte configuraremos un nuevo servidor Node.js que nos permitirá generar y firmar PDFs. Usaremos
Express para crear el servidor, Axios para hacer solicitudes API y fs para leer archivos desde el sistema.Si ya tienes tu proyecto configurado, puedes saltarte esta parte.
Paso 1: Inicializar el proyecto Node.js
Primero, crea un nuevo directorio para el proyecto y entra dentro:
Copy
mkdir pdf-signing-server
cd pdf-signing-serverDespués, inicializa un nuevo proyecto Node.js:
Copy
npm init -yEsto creará un archivo
package.json.Paso 2: Instalar los paquetes requeridos
Instala las dependencias necesarias:
Copy
npm install express axios form-data- Express: framework web mínimo y flexible para Node.js.
- Axios: cliente HTTP basado en promesas para hacer solicitudes API.
- FormData: permite construir solicitudes multipart/form-data.
- fs: módulo integrado de Node.js para leer archivos del sistema.
Paso 3: Crear el servidor
Crea un archivo llamado
server.js. Si usas TypeScript, puedes crear server.ts.Copy
const express = require("express");
const fs = require("fs");
const axios = require("axios");
const app = express();
const port = 3000;
app.use(express.json());
// Basic route to test the server
app.get("/", (req, res) => {
res.send("PDF Signing Server is up and running!");
});
// Example route for generating and signing a PDF
app.post("/sign-pdf", async (req, res) => {
// PDF generation and signing logic will go here
res.send("PDF signed successfully!");
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});Ejecuta el servidor:
Copy
node server.jsAbre
http://localhost:3000 en el navegador para confirmar que el servidor funciona.Parte 2: Configuración de PDF Signify
Ahora que el servidor Node.js está funcionando, configuraremos PDF Signify para poder firmar PDFs. PDF Signify permite añadir firmas digitales fácilmente a archivos PDF.
Paso 1: Crear una cuenta o iniciar sesión
Visita pdfsignify.com y crea una cuenta o inicia sesión.

Puedes registrarte usando Google, Microsoft o completando el registro manualmente.

Paso 2: Verificar tu correo electrónico
Después de registrarte, tendrás que verificar tu correo electrónico para activar la cuenta.

Abre el correo de verificación y haz clic en el botón o enlace proporcionado.

Si no recibes el correo, revisa spam o solicita otro correo de verificación desde el panel.

Paso 3: Crear un nuevo proyecto en PDF Signify
Una vez verificado el correo y dentro del panel, crea un nuevo proyecto. Esto te permitirá generar claves API, revisar el uso y gestionar tus firmas digitales.
Al crear el proyecto, selecciona un plan. Para pruebas y uso básico puedes usar el plan gratuito, teniendo en cuenta que tendrá límites de solicitudes API.

Después de seleccionar el plan, haz clic en siguiente y asigna un nombre al proyecto.

Paso 4: Explorar el panel de administración
En el panel podrás ver el uso de la API, los límites del plan y las claves necesarias para autenticar tus solicitudes desde Node.js.

Parte 3: Crear Credenciales API
El siguiente paso es crear credenciales API. Estas credenciales son necesarias para autenticar las solicitudes que hará tu servidor Node.js a PDF Signify.
Paso 1: Navegar a la sección de credenciales API
En el menú lateral, entra en API Credentials. Desde allí podrás crear y gestionar tus claves API.

Paso 2: Crear nuevas credenciales API
Haz clic en Create Credentials. Se mostrará un popup con tus nuevas credenciales.
- API Key: se utiliza para autenticar las solicitudes API.
- Secret Key: se utiliza para firmar las solicitudes de forma segura.
La Secret Key solo se mostrará una vez, así que cópiala y guárdala en un lugar seguro. Si la pierdes, tendrás que generar nuevas credenciales.

Parte 4: Programando la Aplicación Node.js
Ahora que tenemos las credenciales de PDF Signify, integraremos la firma en la aplicación Node.js. Para este ejemplo utilizaremos un PDF ya creado.
Si necesitas generar el PDF antes, puedes usar librerías como
Puppeteer o pdf-lib. Una vez generado, puedes enviar el buffer directamente a PDF Signify sin necesidad de guardarlo y leerlo otra vez.Paso 1: Guardar el PDF ya creado
Coloca el PDF que quieres firmar en la raíz del proyecto, en el mismo directorio donde está
server.js. Renómbralo como filepdf.pdf.Copy
pdf-signing-server/
│
├── server.js
├── package.json
├── node_modules/
└── filepdf.pdfPaso 2: Agregar las dependencias en el código
Añade las dependencias necesarias al inicio del archivo
server.js.Copy
const fs = require("fs"); // File system
const axios = require("axios"); // HTTP requests
const FormData = require("form-data"); // Multipart form dataPaso 3: Crear la ruta de firma
Crea una ruta
GET /sign-pdf para firmar el PDF y devolver el documento firmado al navegador.Copy
app.get("/sign-pdf", async (req, res) => {
console.error("Sign my pdf");
});Paso 4: Configurar el certificado y la contraseña
Para firmar PDFs necesitas un certificado digital válido y su contraseña. El certificado puede estar en formato
.pfx o .p12.Coloca el certificado en la raíz del proyecto y nómbralo como
certificate.pfx o certificate.p12.Copy
pdf-signing-server/
├── server.js
├── package.json
├── node_modules/
├── filepdf.pdf
└── certificate.pfxPaso 5: Leer los archivos necesarios
Dentro de la ruta
/sign-pdf, lee el certificado, el PDF y define la contraseña.Copy
const cert = fs.readFileSync("certificate.pfx");
const pdf = fs.readFileSync("filepdf.pdf");
const password = "YOUR_CERTIFICATE_PASSWORD";Paso 6: Crear el FormData para la solicitud
Para enviar una solicitud
multipart/form-data, crea un objeto FormData y añade el certificado, la contraseña y el PDF.Copy
const formData = new FormData();
formData.append("certificate", cert, {
filename: "certificate.pfx",
contentType: "application/x-pkcs12",
});
formData.append("certificatePassword", password);
formData.append("pdf", pdf, {
filename: "filepdf.pdf",
contentType: "application/pdf",
});Paso 7: Ejecutar la solicitud y manejar el resultado
Ahora envía la solicitud POST a
/api/v1/sign-pdf usando Axios. La respuesta será un PDF en formato arraybuffer.Copy
try {
const response = await axios.post("https://api.pdfsignify.com/api/v1/sign-pdf", formData, {
headers: {
...formData.getHeaders(),
AccessKey: "MY_ACCESS_KEY",
SecretKey: "MY_SECRET_KEY",
},
responseType: "arraybuffer",
});
res.contentType("application/pdf");
return res.send(response.data);
} catch (error) {
let errorResponse = error;
if (error?.response?.data) {
errorResponse = error.response.data.toString();
}
console.error("error", errorResponse);
return res.send(errorResponse);
}Reemplaza
MY_ACCESS_KEY y MY_SECRET_KEY por las claves generadas en PDF Signify.Archivo server.js completo
Copy
const express = require("express");
const fs = require("fs");
const axios = require("axios");
const FormData = require("form-data");
const app = express();
const port = 3000;
app.use(express.json());
app.get("/", (req, res) => {
res.send("PDF Signing Server is up and running!");
});
app.get("/sign-pdf", async (req, res) => {
const cert = fs.readFileSync("certificate.pfx");
const pdf = fs.readFileSync("filepdf.pdf");
const password = "YOUR_CERTIFICATE_PASSWORD";
const formData = new FormData();
formData.append("certificate", cert, {
filename: "certificate.pfx",
contentType: "application/x-pkcs12",
});
formData.append("certificatePassword", password);
formData.append("pdf", pdf, {
filename: "filepdf.pdf",
contentType: "application/pdf",
});
try {
const response = await axios.post("https://api.pdfsignify.com/api/v1/sign-pdf", formData, {
headers: {
...formData.getHeaders(),
AccessKey: "MY_ACCESS_KEY",
SecretKey: "MY_SECRET_KEY",
},
responseType: "arraybuffer",
});
res.contentType("application/pdf");
return res.send(response.data);
} catch (error) {
let errorResponse = error;
if (error?.response?.data) {
errorResponse = error.response.data.toString();
}
console.error("error", errorResponse);
return res.send(errorResponse);
}
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});Paso 8: Reiniciar el servidor y probar la ruta
Reinicia el servidor para aplicar los cambios:
Copy
node server.jsAbre
http://localhost:3000/sign-pdf. Si todo está correcto, recibirás el PDF firmado.Parte 5: Verifica los Resultados
Respuesta de error
- El PDF no está guardado con el nombre correcto.
- El certificado no está guardado con el nombre o extensión correctos.
- La contraseña del certificado es incorrecta.
- Las credenciales de la API no son correctas.
Respuesta de éxito
Si la ruta funciona, verás el PDF firmado. Sin embargo, es posible que las firmas no aparezcan correctamente en el navegador.
Para ver las firmas, descarga el PDF y ábrelo con un visor PDF. En Windows y macOS puedes usar
Adobe Reader. En Linux puedes usar el visor de documentos predeterminado.La firma será visible en las coordenadas (0,0), en la esquina inferior izquierda de la página.

Parte 6: Personaliza la Firma
PDF Signify ofrece muchas opciones para personalizar la firma. Puedes configurar imágenes, posición, tamaño, páginas donde aparece, mensaje, fecha, razón, ubicación e información de contacto.
Puedes consultar la documentación completa aquí.
Paso 1: Personalización de la imagen de fondo
Para personalizar la imagen de fondo, puedes usar el parámetro
signatureBackgroundImage. Esto permite establecer un logo o imagen como fondo de la firma.Copy
const backgroundImage = fs.readFileSync("backgroundImage.png");
formData.append("signatureBackgroundImage", backgroundImage, {
filename: "logo.png",
contentType: "image/png",
});Paso 2: Imagen cerca de la firma
También puedes mostrar un logo o imagen cerca de la firma usando el parámetro
signatureImage.Copy
const signatureImage = fs.readFileSync("signatureImage.png");
formData.append("signatureImage", signatureImage, {
filename: "logo.png",
contentType: "image/png",
});Paso 3: Parámetros de la firma
timezone: zona horaria para la fecha de firma.signatureFieldName: nombre único del campo de firma.signatureXPosition: posición X desde el borde izquierdo del PDF.signatureYPosition: posición Y desde la parte inferior del PDF.signatureWidth: ancho de la firma.signatureHeight: alto de la firma.signatureMessage: mensaje visible de la firma.signaturePageAppearance: página o páginas donde aparecerá la firma.signatureDateLabel: etiqueta de la fecha.signatureDateFormat: formato de fecha.signatureReason: motivo de la firma.signatureLocation: ubicación de la firma.signatureContactInfo: información de contacto.signatureShowDistinguishedName: muestra el DN del certificado.
Ejemplo de personalización:
Copy
formData.append("signaturePageAppearance", -1);
formData.append("timezone", "UTC");
formData.append("signatureMessage", "Digitally signed by the user");
formData.append("signatureDateLabel", "");
formData.append("signatureDateFormat", "Y-m-d H:i:s");
formData.append("signatureHeight", 100);
formData.append("signatureWidth", 150);
formData.append("signatureYPosition", 100);
formData.append("signatureXPosition", 180);Paso 4: Metadatos del PDF
Al firmar un PDF, PDF Signify también permite modificar sus metadatos: título, asunto, autor, palabras clave, creador, productor, fecha de creación y fecha de modificación.
Copy
formData.append("pdfMetadataAuthor", "AUTHOR");
formData.append("pdfMetadataKeywords", "keywords,keyword2");
formData.append("pdfMetadataTitle", "MY DOCUMENT");
formData.append("pdfMetadataSubject", "EXAMPLE DOCUMENT");
formData.append("pdfMetadataCreator", "PDFSIGNIFY");
formData.append("pdfMetadataProducer", "PDFSIGNIFY");
formData.append("pdfMetadataCreationDate", new Date().toLocaleString());
formData.append("pdfMetadataModificationDate", new Date().toLocaleString());También puedes usar el endpoint
/api/v1/set-pdf-metadata con los mismos parámetros para cambiar los metadatos sin firmar el documento.Archivo server.js completo con personalización
Copy
const express = require("express");
const fs = require("fs");
const axios = require("axios");
const FormData = require("form-data");
const app = express();
const port = 3000;
app.use(express.json());
app.get("/", (req, res) => {
res.send("PDF Signing Server is up and running!");
});
app.get("/sign-pdf", async (req, res) => {
const cert = fs.readFileSync("certificate.pfx");
const pdf = fs.readFileSync("filepdf.pdf");
const formData = new FormData();
formData.append("certificate", cert, {
filename: "certificate.pfx",
contentType: "application/x-pkcs12",
});
formData.append("certificatePassword", "password");
formData.append("pdf", pdf, {
filename: "filepdf.pdf",
contentType: "application/pdf",
});
const backgroundImage = fs.readFileSync("signatureBackgroundImage.png");
formData.append("signatureBackgroundImage", backgroundImage, {
filename: "logo.png",
contentType: "image/png",
});
// const signatureImage = fs.readFileSync("signatureImage.png");
// formData.append("signatureImage", signatureImage, {
// filename: "logo.png",
// contentType: "image/png",
// });
formData.append("signaturePageAppearance", -1);
formData.append("timezone", "UTC");
formData.append("signatureMessage", "Digitally signed by the user");
formData.append("signatureDateLabel", "");
formData.append("signatureDateFormat", "Y-m-d H:i:s");
formData.append("signatureHeight", 100);
formData.append("signatureWidth", 150);
formData.append("signatureYPosition", 100);
formData.append("signatureXPosition", 180);
formData.append("pdfMetadataAuthor", "AUTHOR");
formData.append("pdfMetadataKeywords", "keywords,keyword2");
formData.append("pdfMetadataTitle", "MY DOCUMENT");
formData.append("pdfMetadataSubject", "EXAMPLE DOCUMENT");
formData.append("pdfMetadataCreator", "PDFSIGNIFY");
formData.append("pdfMetadataProducer", "PDFSIGNIFY");
formData.append("pdfMetadataCreationDate", new Date().toLocaleString());
formData.append("pdfMetadataModificationDate", new Date().toLocaleString());
try {
const response = await axios.post("https://api.pdfsignify.com/api/v1/sign-pdf", formData, {
headers: {
...formData.getHeaders(),
AccessKey: "MY_ACCESS_KEY",
SecretKey: "MY_SECRET_KEY",
},
responseType: "arraybuffer",
});
res.contentType("application/pdf");
return res.send(response.data);
} catch (error) {
let errorResponse = error;
if (error?.response?.data) {
errorResponse = error.response.data.toString();
}
console.error("error", errorResponse);
return res.send(errorResponse);
}
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});Paso 5: Verificar la validez del certificado y la contraseña
PDF Signify también permite verificar si un certificado es válido y si la contraseña coincide. Para hacerlo, utiliza el endpoint
/api/v1/check-certificate-password.Copy
app.get("/check-certificate", async (req, res) => {
const cert = fs.readFileSync("certificate.pfx");
const formData = new FormData();
formData.append("certificate", cert, {
filename: "certificate.pfx",
contentType: "application/x-pkcs12",
});
formData.append("certificatePassword", "password");
try {
const response = await axios.post("https://api.pdfsignify.com/api/v1/check-certificate-password", formData, {
headers: {
...formData.getHeaders(),
AccessKey: "MY_ACCESS_KEY",
SecretKey: "MY_SECRET_KEY",
},
});
return res.send(response.data);
} catch (error) {
let errorResponse = error;
if (error?.response?.data) {
errorResponse = error.response.data;
}
console.error("error", errorResponse);
return res.send(errorResponse);
}
});En este caso, la respuesta estará en formato JSON. Una respuesta correcta puede ser:
Copy
{
"success": true,
"message": "¡La contraseña del certificado es correcta!",
"errors": [],
"data": {},
"auth": true
}Si la contraseña no es válida, la respuesta puede ser:
Copy
{
"success": false,
"message": "¡La contraseña del certificado no es correcta!",
"errors": [
"certificatePassword"
],
"data": {},
"auth": true
}Parte 7: Conclusiones
En conclusión, hemos visto un ejemplo completo de cómo firmar PDFs utilizando Node.js y PDF Signify. También hemos visto cómo personalizar la firma, modificar metadatos del PDF y verificar un certificado con facilidad.
Para más información, puedes visitar estos recursos:
- Documentación de la API: https://api.pdfsignify.com/api/documentation#/
- Ejemplos: https://pdfsignify.com/#examples
- Sitio web: https://pdfsignify.com/
Si tienes más preguntas, puedes contactar con el soporte de PDF Signify por correo electrónico.