Skip to main content

Módulo Crypto en Node.js (ES Modules)

Introducción al módulo crypto de node.js

Cuando trabajamos con backend utilizando Node.js es frecuente enfrentarse a tareas donde se necesita proteger información, verificar integridad de datos o autenticar mensajes. Para estos casos existe el módulo nativo crypto. Este módulo ofrece primitivas criptográficas seguras, basadas en implementaciones de OpenSSL, que permiten realizar operaciones como generar hashes, cifrar y descifrar datos, crear firmas digitales o derivar claves.

Hashing

El hashing permite obtener un resumen irreversible de un mensaje. Es útil para almacenar contraseñas de forma segura, comparar datos sin revelar su contenido o verificar la integridad de archivos.

Hash SHA-256

// archivo: hash_sha256.js
// en este ejemplo creamos un hash SHA-256 de un texto simple
// los hashes son irreversibles: no permiten obtener el texto original
// este tipo de función se usa, por ejemplo, para almacenar contraseñas de manera segura
// o para verificar que un archivo no ha sido modificado

import { createHash } from "crypto";

async function main() {
// se crea un objeto hash indicando el algoritmo deseado
// SHA-256 es seguro y muy utilizado en sistemas modernos
const hash = createHash("sha256");

// añadimos los datos a hashear
// se puede llamar a update varias veces si el contenido es grande
hash.update("mensaje secreto");

// digest finaliza el cálculo y devuelve el hash en el formato elegido
const resultado = hash.digest("hex");

console.log("hash SHA-256:", resultado);
}

main();

Hash de un archivo completo

// archivo: hash_file.js
// en este ejemplo calculamos el hash SHA-256 de un archivo completo
// esto se utiliza para comprobar que un archivo no ha sido alterado
// si alguien modifica un byte, el hash resultante cambia por completo

import { createHash } from "crypto";
import { createReadStream } from "fs";

async function hashFile(path) {
const hash = createHash("sha256");
const stream = createReadStream(path);

// devolvemos una promesa porque trabajamos con streams asíncronos
return new Promise((resolve, reject) => {
// cada fragmento del archivo se va añadiendo al hash
stream.on("data", (chunk) => hash.update(chunk));

// cuando termine la lectura obtenemos el digest final
stream.on("end", () => resolve(hash.digest("hex")));

// si ocurre un error, rechazamos la promesa
stream.on("error", reject);
});
}

async function main() {
const resultado = await hashFile("./ejemplo.txt");
console.log("hash del archivo:", resultado);
}

main();

SHA-512 y MD5

// archivo: hash_variants.js
// solo cambiando el nombre del algoritmo podemos producir hashes con SHA-512 o MD5
// MD5 ya no es seguro para usos criptográficos, pero puede servir para detectar corrupción accidental

import { createHash } from "crypto";

async function main() {
const texto = "datos";

const sha512 = createHash("sha512").update(texto).digest("hex");
const md5 = createHash("md5").update(texto).digest("hex");

console.log("sha512:", sha512);
console.log("md5:", md5);
}

main();

Hmac

Un HMAC combina un mensaje con una clave secreta. Esto permite verificar autenticidad del mensaje. Si el mensaje cambia, el HMAC cambia. Si alguien no conoce la clave secreta, no podrá generar un HMAC válido.

Generar un HMAC SHA-256

// archivo: hmac.js
// en este ejemplo generamos un HMAC usando SHA-256 y una clave secreta
// esto se usa, por ejemplo, para validar que un mensaje proviene realmente
// de otra parte que comparte la clave secreta (microservicios, APIs internas)

import { createHmac } from "crypto";

async function main() {
const clave = "clave-compartida-segura";
const mensaje = "datos importantes";

// se crea el HMAC con el algoritmo y la clave secreta
const hmac = createHmac("sha256", clave);

// añadimos el mensaje a firmar
hmac.update(mensaje);

// generamos la firma
const firma = hmac.digest("hex");

console.log("mensaje:", mensaje);
console.log("hmac generado:", firma);
}

main();

Cifrado simétrico con aes

El cifrado simétrico utiliza una única clave para cifrar y descifrar. AES es el estándar moderno más utilizado. Se puede usar en varios modos; los más frecuentes son CBC y GCM.

CBC es un modo tradicional que requiere clave e IV y es seguro si se usa correctamente.

GCM añade autenticación integrada para garantizar integridad y evitar modificaciones maliciosas.

Cifrado AES-256-CBC

// archivo: aes_cbc.js
// en este ejemplo ciframos y desciframos un mensaje usando AES-256-CBC
// es fundamental usar claves y vectores de inicialización generados con randomBytes
// nunca se debe reutilizar un IV con la misma clave en CBC

import { randomBytes, createCipheriv, createDecipheriv } from "crypto";

async function main() {
// clave segura de 32 bytes (256 bits)
const key = randomBytes(32);

// iv de 16 bytes para CBC
const iv = randomBytes(16);

const mensaje = "información confidencial";

// cifrado
const cipher = createCipheriv("aes-256-cbc", key, iv);

// update cifra una parte del mensaje
// final completa el proceso
let cifrado = cipher.update(mensaje, "utf8", "hex");
cifrado += cipher.final("hex");

// descifrado
const decipher = createDecipheriv("aes-256-cbc", key, iv);
let descifrado = decipher.update(cifrado, "hex", "utf8");
descifrado += decipher.final("utf8");

console.log("original:", mensaje);
console.log("cifrado:", cifrado);
console.log("descifrado:", descifrado);
}

main();

Cifrado AES-256-GCM con autenticación

// archivo: aes_gcm.js
// AES-GCM ofrece cifrado y además un tag de autenticación
// este tag garantiza que el mensaje no ha sido manipulado
// es el modo recomendado para la mayoría de usos modernos

import { randomBytes, createCipheriv, createDecipheriv } from "crypto";

async function main() {
// clave segura de 32 bytes
const key = randomBytes(32);

// iv recomendado de 12 bytes para GCM
const iv = randomBytes(12);

const mensaje = "mensaje seguro con autenticación integrada";

// cifrado
const cipher = createCipheriv("aes-256-gcm", key, iv);
let cifrado = cipher.update(mensaje, "utf8", "hex");
cifrado += cipher.final("hex");

// tag de autenticación
const tag = cipher.getAuthTag();

// descifrado
const decipher = createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(tag);

let descifrado = decipher.update(cifrado, "hex", "utf8");
descifrado += decipher.final("utf8");

console.log("original:", mensaje);
console.log("cifrado:", cifrado);
console.log("descifrado:", descifrado);
}

main();

Cifrado asimétrico con rsa

El cifrado asimétrico utiliza dos claves distintas: una pública y una privada. La clave pública se puede compartir libremente y sirve para cifrar mensajes. La clave privada se mantiene en secreto y permite descifrarlos. También se usa para crear y verificar firmas digitales.

A diferencia de AES, el cifrado RSA no se emplea normalmente para cifrar grandes cantidades de datos, sino para proteger pequeñas piezas de información como claves simétricas, tokens o secretos breves.

Generar un par de claves rsa

// archivo: generar_rsa.js
// en este ejemplo generamos un par de claves RSA directamente desde Node.js
// se trata de claves PEM estándar, válidas para cifrado y firmas digitales
// la clave pública se puede compartir; la privada debe mantenerse en secreto

import { generateKeyPairSync } from "crypto";

async function main() {
// generateKeyPairSync crea las dos claves según los parámetros indicados
// usamos 2048 bits porque es seguro y ampliamente compatible
const { publicKey, privateKey } = generateKeyPairSync("rsa", {
modulusLength: 2048,
publicKeyEncoding: {
type: "pkcs1",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs1",
format: "pem",
},
});

console.log("clave pública:\n", publicKey);
console.log("clave privada:\n", privateKey);

// en un entorno real guardarías ambas en archivos protegidos
}

main();

Cifrar y descifrar usando rsa

// archivo: rsa_encrypt_decrypt.js
// en este ejemplo ciframos un mensaje con la clave pública
// y lo desciframos con la clave privada
// este proceso es fundamental para asegurar que solo el poseedor de la clave privada
// puede leer la información protegida

import { publicEncrypt, privateDecrypt } from "crypto";
import { readFile } from "fs/promises";

async function main() {
// cargamos las claves previamente generadas
const publicKey = await readFile("./rsa_public.pem", "utf8");
const privateKey = await readFile("./rsa_private.pem", "utf8");

const mensaje = "este texto será cifrado mediante rsa";

// ciframos con la clave pública
// el mensaje debe convertirse a Buffer
const cifrado = publicEncrypt(publicKey, Buffer.from(mensaje, "utf8"));

// descifrado con la clave privada
const descifrado = privateDecrypt(privateKey, cifrado).toString("utf8");

console.log("original:", mensaje);
console.log("cifrado (en hex):", cifrado.toString("hex"));
console.log("descifrado:", descifrado);
}

main();

Firmas digitales

Las firmas digitales permiten garantizar que un mensaje fue producido por quien dice producirlo y que no ha sido modificado. En criptografía asimétrica las firmas se crean con la clave privada y se verifican con la pública.

Firmar y verificar datos

// archivo: rsa_firma.js
// en este ejemplo creamos una firma digital usando la clave privada
// y verificamos la firma usando la clave pública
// esto es especialmente útil en APIs, JWT firmados o sistemas donde necesitamos
// validar la autenticidad de datos antes de procesarlos

import { createSign, createVerify } from "crypto";
import { readFile } from "fs/promises";

async function main() {
const privateKey = await readFile("./rsa_private.pem", "utf8");
const publicKey = await readFile("./rsa_public.pem", "utf8");

const datos = "mensaje importante para firmar";

// creamos un objeto Sign para elegir el algoritmo de firma
const sign = createSign("sha256");

// añadimos los datos a firmar
sign.update(datos);
sign.end();

// generamos la firma digital en formato hex
const firma = sign.sign(privateKey, "hex");

// ahora verificamos la firma con la clave pública
const verify = createVerify("sha256");
verify.update(datos);
verify.end();

const esValida = verify.verify(publicKey, firma, "hex");

console.log("firma:", firma);
console.log("¿firma válida?:", esValida);
}

main();

Generación de bytes aleatorios seguros

Node.js ofrece randomBytes, que genera valores con calidad criptográfica. Esto es esencial para crear claves, IVs, tokens, sesiones o cualquier valor sensible.

Generar tokens aleatorios seguros

// archivo: random_bytes.js
// en este ejemplo generamos bytes aleatorios para crear un token seguro
// nunca uses Math.random para seguridad; randomBytes garantiza calidad criptográfica

import { randomBytes } from "crypto";

async function main() {
// generamos 32 bytes aleatorios (256 bits)
const buffer = randomBytes(32);

// lo convertimos a hex para poder guardarlo o enviarlo
const token = buffer.toString("hex");

console.log("token seguro:", token);
}

main();

Derivación de claves: pbkdf2 y scrypt

Las contraseñas no deben usarse directamente como claves. Deben transformarse mediante funciones lentas y seguras que evitan ataques de fuerza bruta. Para eso sirven PBKDF2 y scrypt.

Derivación con pbkdf2

// archivo: pbkdf2.js
// PBKDF2 aplica un algoritmo lento a la contraseña para generar una clave segura
// esto evita que un atacante pueda adivinar contraseñas fácilmente
// se utiliza un 'salt' aleatorio para evitar ataques con tablas precomputadas

import { pbkdf2, randomBytes } from "crypto";
import { promisify } from "util";

const pbkdf2Async = promisify(pbkdf2);

async function main() {
const password = "contraseña_segura";
const salt = randomBytes(16);

// se elige un número alto de iteraciones (mínimo miles)
const iterations = 100000;

// pbkdf2 produce una clave derivada del tamaño deseado
const key = await pbkdf2Async(password, salt, iterations, 32, "sha256");

console.log("salt:", salt.toString("hex"));
console.log("clave derivada:", key.toString("hex"));
}

main();

Derivación con scrypt

// archivo: scrypt.js
// scrypt es más resistente contra hardware especializado y se usa mucho para almacenar contraseñas
// también usa salts y parámetros que lo hacen lento de forma controlada

import { scrypt, randomBytes } from "crypto";
import { promisify } from "util";

const scryptAsync = promisify(scrypt);

async function main() {
const password = "otra_contraseña";
const salt = randomBytes(16);

// derivamos una clave de 32 bytes
const key = await scryptAsync(password, salt, 32);

console.log("salt:", salt.toString("hex"));
console.log("clave derivada:", key.toString("hex"));
}

main();

Certificados y operaciones relacionadas

Node.js también permite trabajar con certificados X.509, claves PEM, firmas, cifrados y otros formatos estándar utilizados en HTTPS y sistemas de autenticación.

Cargar un certificado y extraer información

// archivo: leer_certificado.js
// en este ejemplo cargamos un certificado y lo analizamos con el módulo crypto
// esto puede ser útil al depurar conexiones TLS, firmar tokens o validar emisores

import { X509Certificate } from "crypto";
import { readFile } from "fs/promises";

async function main() {
// cargamos un certificado en formato PEM
const pem = await readFile("./certificado.pem", "utf8");

// creamos un objeto X509Certificate para poder inspeccionarlo
const cert = new X509Certificate(pem);

console.log("sujeto:", cert.subject);
console.log("emisor:", cert.issuer);
console.log("válido desde:", cert.validFrom);
console.log("válido hasta:", cert.validTo);
}

main();

Buenas prácticas de seguridad

Este conjunto de reglas resume cómo usar crypto correctamente en proyectos reales.

- usa siempre randomBytes para claves, tokens e ivs
- nunca reutilices el mismo iv en modos como aes-cbc
- prefiere aes-gcm frente a aes-cbc porque incorpora autenticación
- no uses md5 para seguridad, solo para detectar corrupción accidental
- evita almacenar contraseñas en claro; usa pbkdf2 o scrypt con salts
- no generes tus propias primitivas criptográficas; usa las del módulo crypto
- guarda claves privadas en archivos protegidos con permisos restringidos
- no transmitas claves por texto plano; usa canales cifrados
- usa claves RSA de mínimo 2048 bits
- cuando firmes datos, utiliza algoritmos modernos como sha256

Proyecto práctico: cifrado seguro y firma digital de archivos

En esta sección vamos a construir un flujo completo que se usa habitualmente en sistemas reales. El objetivo es tomar un archivo, cifrarlo con AES-256-GCM y además firmar el resultado con una clave privada RSA. De esta manera garantizamos dos propiedades importantes:

  1. confidencialidad del archivo cifrado
  2. autenticidad del remitente mediante la firma

Después crearemos también un script para descifrar y verificar el archivo. De esta manera obtendremos una visión completa de un flujo criptográfico similar al que usaría un sistema de mensajería segura, un sistema de transmisión de documentos o una API que envía datos críticos.

Paso 1: generar claves rsa según las recomendaciones modernas

// archivo: generar_claves.js
// este script genera un par de claves RSA de 2048 bits en formato PEM
// las claves se guardarán en dos archivos en el disco para su uso posterior
// la clave pública se puede compartir libremente, pero la clave privada debe protegerse
// en producción se almacenaría en un almacén seguro o mediante permisos estrictos en el sistema operativo

import { generateKeyPairSync } from "crypto";
import { writeFile } from "fs/promises";

async function main() {
// generamos el par de claves con parámetros estándares
const { publicKey, privateKey } = generateKeyPairSync("rsa", {
modulusLength: 2048,
publicKeyEncoding: { type: "pkcs1", format: "pem" },
privateKeyEncoding: { type: "pkcs1", format: "pem" },
});

// guardamos las claves en archivos
await writeFile("./rsa_public.pem", publicKey, "utf8");
await writeFile("./rsa_private.pem", privateKey, "utf8");

console.log("claves generadas en rsa_public.pem y rsa_private.pem");
}

main();

Paso 2: cifrar un archivo con aes-256-gcm y firmarlo con rsa

// archivo: cifrar_y_firmar.js
// en este script tomamos un archivo de entrada, lo ciframos con AES-256-GCM
// y después firmamos el contenido cifrado con una clave privada RSA
// el resultado final se guarda en un archivo .secure que contiene: iv, tag, datos cifrados y firma
// el diseño es simple y didáctico, pero suficientemente realista para entender un flujo completo

import { randomBytes, createCipheriv, createSign } from "crypto";
import { readFile, writeFile } from "fs/promises";

async function main() {
// archivo a proteger
const filePath = "./documento.txt";
const fileData = await readFile(filePath);

// generamos clave simétrica y vector de inicialización (iv)
// randomBytes garantiza valores aleatorios seguros
const key = randomBytes(32); // 256 bits
const iv = randomBytes(12); // recomendado para aes-gcm

// ciframos usando AES-256-GCM
const cipher = createCipheriv("aes-256-gcm", key, iv);

// los métodos update y final realizan el cifrado por bloques
const encrypted = Buffer.concat([cipher.update(fileData), cipher.final()]);

// obtenemos el tag de autenticación, imprescindible para descifrar correctamente
const authTag = cipher.getAuthTag();

// cargamos la clave privada para firmar el resultado cifrado
const privateKey = await readFile("./rsa_private.pem", "utf8");

// creamos objeto para firmar con algoritmo SHA-256
const sign = createSign("sha256");

// añadimos al proceso de firma los tres valores esenciales:
// iv, authTag y los datos cifrados. Con ello garantizamos la integridad del paquete completo.
sign.update(iv);
sign.update(authTag);
sign.update(encrypted);
sign.end();

// obtenemos la firma digital en hex
const signature = sign.sign(privateKey, "hex");

// creamos un contenedor simple en formato JSON
// en sistemas reales podrían usarse formatos binarios más eficientes
const output = {
ciphertext: encrypted.toString("hex"),
iv: iv.toString("hex"),
tag: authTag.toString("hex"),
signature,
};

// guardamos como archivo .secure
await writeFile(
"./documento.secure.json",
JSON.stringify(output, null, 2),
"utf8"
);

console.log("archivo cifrado y firmado. almacenado en documento.secure.json");
}

main();

Paso 3: verificar firma y descifrar el archivo

// archivo: verificar_y_descifrar.js
// este script realiza la operación inversa al anterior:
// 1. carga el archivo .secure
// 2. verifica la firma digital con la clave pública
// 3. si es válida, descifra el contenido con AES-256-GCM
// si la firma no es válida, se aborta inmediatamente

import { createDecipheriv, createVerify } from "crypto";
import { readFile, writeFile } from "fs/promises";

async function main() {
// cargamos el contenedor seguro
const secureData = JSON.parse(
await readFile("./documento.secure.json", "utf8")
);

// convertimos los valores hex de nuevo a Buffer
const iv = Buffer.from(secureData.iv, "hex");
const tag = Buffer.from(secureData.tag, "hex");
const encrypted = Buffer.from(secureData.ciphertext, "hex");

// cargamos la clave pública
const publicKey = await readFile("./rsa_public.pem", "utf8");

// verificamos la firma digital
const verify = createVerify("sha256");

// añadimos en exactamente el mismo orden que fueron firmados
verify.update(iv);
verify.update(tag);
verify.update(encrypted);
verify.end();

const firmaValida = verify.verify(publicKey, secureData.signature, "hex");

if (!firmaValida) {
console.log("firma no válida. el archivo puede haber sido manipulado.");
return;
}

console.log("firma válida. procediendo al descifrado.");

// ahora desciframos con AES-256-GCM
// necesitamos disponer de la misma clave simétrica; en un sistema real
// esta clave NO se incluiría en el paquete, sino que se enviaría cifrada con RSA o por un canal seguro
// para este ejemplo didáctico asumimos que el alumno guarda la clave localmente

const key = await readFile("./clave_simetrica.bin"); // este archivo deberá crearlo el alumno
const decipher = createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(tag);

let decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);

// guardamos el archivo descifrado
await writeFile("./documento_descifrado.txt", decrypted);

console.log("archivo descifrado correctamente en documento_descifrado.txt");
}

main();

Cifrado híbrido con aes y rsa

En la práctica real, lo más habitual es combinar cifrado simétrico (AES) y asimétrico (RSA).

La idea básica es:

  • cifrar los datos con AES-256-GCM (rápido y adecuado para grandes volúmenes)
  • cifrar la clave AES con la clave pública RSA (para poder compartirla de forma segura)

Así, el receptor solo necesita su clave privada RSA para recuperar la clave AES, descifrar los datos y comprobar la autenticidad si usamos GCM.

A continuación tienes una implementación didáctica paso a paso.

Script 1: cifrar un archivo con aes-gcm y cifrar la clave simétrica con rsa

// archivo: cifrar_hibrido.js
// este script implementa cifrado híbrido:
// 1. genera una clave simétrica aleatoria para aes-256-gcm
// 2. cifra un archivo completo usando esa clave aes
// 3. cifra la clave aes con la clave pública rsa
// 4. guarda todo en un contenedor json con:
// - ciphertext: datos del archivo cifrado
// - iv: vector de inicialización de aes-gcm
// - tag: etiqueta de autenticación de aes-gcm
// - encryptedKey: clave aes cifrada con rsa
//
// este flujo es muy similar al usado por sistemas modernos:
// los datos se protegen con cifrado simétrico rápido
// y la clave se protege con cifrado asimétrico

import { randomBytes, createCipheriv, publicEncrypt } from "crypto";
import { readFile, writeFile } from "fs/promises";

async function main() {
// ruta del archivo original que queremos proteger
const inputPath = "./original.txt";

// cargamos el contenido del archivo a un buffer
const fileData = await readFile(inputPath);

// generamos una clave simétrica segura para aes-256 (32 bytes = 256 bits)
const aesKey = randomBytes(32);

// generamos el iv recomendado para gcm (12 bytes)
const iv = randomBytes(12);

// creamos el cifrador aes-256-gcm con la clave e iv generados
const cipher = createCipheriv("aes-256-gcm", aesKey, iv);

// ciframos el contenido del archivo:
// update procesa una parte de los datos y final termina el proceso
const encryptedFile = Buffer.concat([
cipher.update(fileData),
cipher.final(),
]);

// obtenemos el tag de autenticación de gcm
// este tag garantiza que los datos no han sido modificados
const authTag = cipher.getAuthTag();

// cargamos la clave pública rsa desde disco
// esta clave la había generado previamente el script generar_claves.js
const publicKey = await readFile("./rsa_public.pem", "utf8");

// ciframos la clave aes con la clave pública rsa
// de esta forma puedes enviar la clave cifrada a cualquier receptor
// y solo quien posea la clave privada rsa podrá recuperarla
const encryptedKey = publicEncrypt(publicKey, aesKey);

// construimos un objeto json que contenga toda la información necesaria
// para que el receptor pueda:
// - recuperar la clave aes (usando la clave privada rsa)
// - descifrar el archivo (usando aes-256-gcm)
const packageJson = {
ciphertext: encryptedFile.toString("hex"),
iv: iv.toString("hex"),
tag: authTag.toString("hex"),
encryptedKey: encryptedKey.toString("hex"),
};

// guardamos el paquete en un archivo .secure.json
// en entornos reales podrían usarse formatos binarios, pero json es perfecto para aprender
const outputPath = "./original.secure.json";
await writeFile(outputPath, JSON.stringify(packageJson, null, 2), "utf8");

console.log("cifrado híbrido completado.");
console.log("archivo de entrada:", inputPath);
console.log("paquete seguro generado:", outputPath);
}

main();

Script 2: descifrar el paquete híbrido usando la clave privada rsa

// archivo: descifrar_hibrido.js
// este script realiza la operación inversa al cifrado híbrido:
// 1. carga el paquete json (generado previamente)
// 2. descifra la clave aes con la clave privada rsa
// 3. usa la clave aes recuperada para descifrar los datos con aes-256-gcm
// si el tag de autenticación no coincide, el proceso lanzará un error
// indicando que los datos han sido manipulados o están corruptos

import { privateDecrypt, createDecipheriv } from "crypto";
import { readFile, writeFile } from "fs/promises";

async function main() {
// ruta del paquete seguro que se quiere descifrar
const securePath = "./original.secure.json";

// cargamos el contenedor json
const secureJson = JSON.parse(await readFile(securePath, "utf8"));

// convertimos los valores hex a buffers binarios
const encryptedFile = Buffer.from(secureJson.ciphertext, "hex");
const iv = Buffer.from(secureJson.iv, "hex");
const authTag = Buffer.from(secureJson.tag, "hex");
const encryptedKey = Buffer.from(secureJson.encryptedKey, "hex");

// cargamos la clave privada rsa del receptor
const privateKey = await readFile("./rsa_private.pem", "utf8");

// desciframos la clave aes con la clave privada
// solo quien posea esta clave privada puede completar este paso
const aesKey = privateDecrypt(privateKey, encryptedKey);

// creamos un descifrador aes-256-gcm con la clave recuperada e iv
const decipher = createDecipheriv("aes-256-gcm", aesKey, iv);

// es imprescindible configurar el tag de autenticación
// para que gcm verifique la integridad de los datos
decipher.setAuthTag(authTag);

// desciframos el contenido del archivo
const decryptedFile = Buffer.concat([
decipher.update(encryptedFile),
decipher.final(),
]);

// guardamos el resultado en un nuevo archivo
const outputPath = "./original_descifrado.txt";
await writeFile(outputPath, decryptedFile);

console.log("descifrado híbrido completado.");
console.log("paquete seguro de entrada:", securePath);
console.log("archivo resultante:", outputPath);
}

main();