Node.js

Node

Tutorial Node: Módulo crypto

Aprende a usar el módulo crypto de Node.js para hashing con SHA256 y generación de UUIDs, con ejemplos prácticos y casos de uso reales.

Aprende Node y certifícate

Hashing con SHA256

El hashing es una técnica fundamental en el desarrollo de aplicaciones que permite convertir datos de cualquier tamaño en una cadena de longitud fija. Node.js incluye el módulo crypto que proporciona funcionalidades criptográficas, incluyendo algoritmos de hash como SHA256.

SHA256 (Secure Hash Algorithm 256-bit) es uno de los algoritmos de hash más utilizados en aplicaciones modernas. Genera un hash de 256 bits (32 bytes) que se representa típicamente como una cadena hexadecimal de 64 caracteres. Este algoritmo es determinista, lo que significa que la misma entrada siempre producirá el mismo hash.

Importación del módulo crypto

Para trabajar con funciones de hash en Node.js, necesitas importar el módulo crypto que forma parte del núcleo de Node.js:

const crypto = require('crypto');

Este módulo no requiere instalación adicional ya que viene integrado con Node.js desde sus primeras versiones.

Creación de hashes básicos

La forma más directa de crear un hash SHA256 es utilizando el método createHash(). Este método acepta el algoritmo de hash como parámetro y devuelve un objeto hash:

const crypto = require('crypto');

// Crear un hash SHA256 de una cadena
const texto = 'Hola mundo';
const hash = crypto.createHash('sha256').update(texto).digest('hex');

console.log(`Texto original: ${texto}`);
console.log(`Hash SHA256: ${hash}`);
// Salida: Hash SHA256: a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3

El proceso de hashing sigue tres pasos principales:

  • 1. Crear el objeto hash con createHash('sha256')
  • 2. Alimentar los datos con update(data)
  • 3. Generar el resultado con digest(format)

Trabajando con diferentes formatos de entrada

El método update() puede procesar diferentes tipos de datos. Puedes trabajar con cadenas de texto, buffers o incluso datos binarios:

const crypto = require('crypto');

// Hash de una cadena con encoding específico
const textoUtf8 = 'Texto con acentos: café, niño, corazón';
const hashTexto = crypto.createHash('sha256')
  .update(textoUtf8, 'utf8')
  .digest('hex');

// Hash de un buffer
const buffer = Buffer.from('Datos en buffer', 'utf8');
const hashBuffer = crypto.createHash('sha256')
  .update(buffer)
  .digest('hex');

console.log('Hash de texto UTF-8:', hashTexto);
console.log('Hash de buffer:', hashBuffer);

Formatos de salida del hash

El método digest() permite especificar el formato de salida del hash. Los formatos más comunes son:

const crypto = require('crypto');
const datos = 'Ejemplo de datos';

// Formato hexadecimal (más común)
const hashHex = crypto.createHash('sha256').update(datos).digest('hex');

// Formato base64
const hashBase64 = crypto.createHash('sha256').update(datos).digest('base64');

// Como buffer (datos binarios)
const hashBuffer = crypto.createHash('sha256').update(datos).digest();

console.log('Hexadecimal:', hashHex);
console.log('Base64:', hashBase64);
console.log('Buffer length:', hashBuffer.length); // 32 bytes

Hashing de archivos

Una aplicación práctica del hashing es verificar la integridad de archivos. Puedes calcular el hash de un archivo para detectar cambios o corrupciones:

const crypto = require('crypto');
const fs = require('fs');

function calcularHashArchivo(rutaArchivo) {
  return new Promise((resolve, reject) => {
    const hash = crypto.createHash('sha256');
    const stream = fs.createReadStream(rutaArchivo);
    
    stream.on('data', (chunk) => {
      hash.update(chunk);
    });
    
    stream.on('end', () => {
      const hashFinal = hash.digest('hex');
      resolve(hashFinal);
    });
    
    stream.on('error', (error) => {
      reject(error);
    });
  });
}

// Uso de la función
calcularHashArchivo('./documento.txt')
  .then(hash => {
    console.log('Hash del archivo:', hash);
  })
  .catch(error => {
    console.error('Error al calcular hash:', error.message);
  });

Hashing incremental

Para datos grandes o cuando recibes información en fragmentos, puedes usar el hashing incremental llamando update() múltiples veces:

const crypto = require('crypto');

const hash = crypto.createHash('sha256');

// Agregar datos en múltiples llamadas
hash.update('Primera parte ');
hash.update('segunda parte ');
hash.update('tercera parte');

const resultado = hash.digest('hex');
console.log('Hash incremental:', resultado);

// Es equivalente a:
const hashDirecto = crypto.createHash('sha256')
  .update('Primera parte segunda parte tercera parte')
  .digest('hex');

console.log('Hash directo:', hashDirecto);
console.log('Son iguales:', resultado === hashDirecto); // true

Casos de uso prácticos

El hashing SHA256 tiene múltiples aplicaciones en el desarrollo de aplicaciones:

Verificación de integridad de datos:

const crypto = require('crypto');

function verificarIntegridad(datos, hashEsperado) {
  const hashCalculado = crypto.createHash('sha256')
    .update(datos)
    .digest('hex');
  
  return hashCalculado === hashEsperado;
}

const mensaje = 'Datos importantes';
const hashOriginal = crypto.createHash('sha256').update(mensaje).digest('hex');

// Verificar más tarde
const esIntegro = verificarIntegridad(mensaje, hashOriginal);
console.log('Los datos están íntegros:', esIntegro);

Generación de identificadores únicos:

const crypto = require('crypto');

function generarIdUnico(datos) {
  const timestamp = Date.now().toString();
  const contenido = datos + timestamp;
  
  return crypto.createHash('sha256')
    .update(contenido)
    .digest('hex')
    .substring(0, 16); // Tomar solo los primeros 16 caracteres
}

const id1 = generarIdUnico('usuario123');
const id2 = generarIdUnico('usuario123');

console.log('ID 1:', id1);
console.log('ID 2:', id2);
console.log('Son diferentes:', id1 !== id2); // true debido al timestamp

El módulo crypto de Node.js proporciona una implementación robusta y eficiente de SHA256, adecuada tanto para aplicaciones simples como para sistemas que requieren alto rendimiento en operaciones criptográficas.

Generación de UUIDs

Los UUID (Universally Unique Identifier) son identificadores únicos de 128 bits que se utilizan para identificar recursos de forma unívoca sin necesidad de una autoridad central. Node.js proporciona el módulo crypto con funcionalidades para generar UUIDs siguiendo diferentes estándares.

Un UUID típico se representa como una cadena de 36 caracteres con el formato xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, donde cada x es un dígito hexadecimal. Esta representación incluye cuatro guiones que dividen el UUID en cinco grupos de caracteres.

Generación de UUIDs aleatorios (v4)

La versión más común de UUID es la versión 4, que se basa en números aleatorios. Node.js incluye el método randomUUID() que genera UUIDs v4 de forma sencilla:

const crypto = require('crypto');

// Generar un UUID v4 aleatorio
const uuid = crypto.randomUUID();
console.log('UUID generado:', uuid);
// Ejemplo de salida: f47ac10b-58cc-4372-a567-0e02b2c3d479

Este método utiliza el generador de números aleatorios criptográficamente seguro del sistema operativo, garantizando que cada UUID generado sea prácticamente único.

Generación de múltiples UUIDs

Para aplicaciones que requieren generar varios identificadores, puedes crear una función que produzca lotes de UUIDs:

const crypto = require('crypto');

function generarLoteUUIDs(cantidad) {
  const uuids = [];
  
  for (let i = 0; i < cantidad; i++) {
    uuids.push(crypto.randomUUID());
  }
  
  return uuids;
}

// Generar 5 UUIDs
const loteIds = generarLoteUUIDs(5);
loteIds.forEach((uuid, index) => {
  console.log(`UUID ${index + 1}: ${uuid}`);
});

Validación de formato UUID

Es importante validar que una cadena tenga el formato correcto de UUID antes de utilizarla en tu aplicación:

const crypto = require('crypto');

function esUUIDValido(cadena) {
  const patronUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return patronUUID.test(cadena);
}

// Probar la validación
const uuidValido = crypto.randomUUID();
const uuidInvalido = 'no-es-un-uuid';

console.log(`${uuidValido} es válido:`, esUUIDValido(uuidValido));
console.log(`${uuidInvalido} es válido:`, esUUIDValido(uuidInvalido));

Casos de uso prácticos

Los UUIDs son especialmente útiles en sistemas distribuidos donde necesitas identificadores únicos sin coordinación central:

Sistema de gestión de usuarios:

const crypto = require('crypto');

class GestorUsuarios {
  constructor() {
    this.usuarios = new Map();
  }
  
  crearUsuario(nombre, email) {
    const id = crypto.randomUUID();
    const usuario = {
      id,
      nombre,
      email,
      fechaCreacion: new Date().toISOString()
    };
    
    this.usuarios.set(id, usuario);
    return usuario;
  }
  
  obtenerUsuario(id) {
    return this.usuarios.get(id);
  }
  
  listarUsuarios() {
    return Array.from(this.usuarios.values());
  }
}

// Uso del gestor
const gestor = new GestorUsuarios();

const usuario1 = gestor.crearUsuario('Ana García', 'ana@email.com');
const usuario2 = gestor.crearUsuario('Carlos López', 'carlos@email.com');

console.log('Usuario creado:', usuario1);
console.log('Recuperar usuario:', gestor.obtenerUsuario(usuario1.id));

Generación de tokens de sesión:

const crypto = require('crypto');

class GestorSesiones {
  constructor() {
    this.sesiones = new Map();
  }
  
  crearSesion(usuarioId) {
    const tokenSesion = crypto.randomUUID();
    const sesion = {
      token: tokenSesion,
      usuarioId,
      fechaCreacion: Date.now(),
      activa: true
    };
    
    this.sesiones.set(tokenSesion, sesion);
    return tokenSesion;
  }
  
  validarSesion(token) {
    const sesion = this.sesiones.get(token);
    
    if (!sesion || !sesion.activa) {
      return false;
    }
    
    // Verificar si la sesión ha expirado (24 horas)
    const tiempoExpiracion = 24 * 60 * 60 * 1000; // 24 horas en ms
    const tiempoTranscurrido = Date.now() - sesion.fechaCreacion;
    
    if (tiempoTranscurrido > tiempoExpiracion) {
      sesion.activa = false;
      return false;
    }
    
    return true;
  }
  
  cerrarSesion(token) {
    const sesion = this.sesiones.get(token);
    if (sesion) {
      sesion.activa = false;
    }
  }
}

// Ejemplo de uso
const gestorSesiones = new GestorSesiones();

const token = gestorSesiones.crearSesion('usuario-123');
console.log('Token de sesión:', token);
console.log('Sesión válida:', gestorSesiones.validarSesion(token));

Trabajando con UUIDs en estructuras de datos

Los UUIDs son ideales como claves primarias en estructuras de datos donde necesitas garantizar unicidad:

const crypto = require('crypto');

class RegistroEventos {
  constructor() {
    this.eventos = [];
  }
  
  registrarEvento(tipo, datos) {
    const evento = {
      id: crypto.randomUUID(),
      tipo,
      datos,
      timestamp: Date.now(),
      fechaLegible: new Date().toISOString()
    };
    
    this.eventos.push(evento);
    return evento.id;
  }
  
  buscarEvento(id) {
    return this.eventos.find(evento => evento.id === id);
  }
  
  filtrarPorTipo(tipo) {
    return this.eventos.filter(evento => evento.tipo === tipo);
  }
  
  obtenerEstadisticas() {
    const tipos = {};
    
    this.eventos.forEach(evento => {
      tipos[evento.tipo] = (tipos[evento.tipo] || 0) + 1;
    });
    
    return {
      totalEventos: this.eventos.length,
      eventosPorTipo: tipos
    };
  }
}

// Uso del registro de eventos
const registro = new RegistroEventos();

const idLogin = registro.registrarEvento('login', { usuario: 'ana@email.com' });
const idError = registro.registrarEvento('error', { mensaje: 'Conexión fallida' });
const idLogout = registro.registrarEvento('logout', { usuario: 'ana@email.com' });

console.log('Evento de login:', registro.buscarEvento(idLogin));
console.log('Eventos de tipo error:', registro.filtrarPorTipo('error'));
console.log('Estadísticas:', registro.obtenerEstadisticas());

Conversión y manipulación de UUIDs

Aunque los UUIDs se manejan típicamente como cadenas, puedes necesitar convertirlos a otros formatos para optimización o almacenamiento:

const crypto = require('crypto');

function uuidSinGuiones(uuid) {
  return uuid.replace(/-/g, '');
}

function agregarGuionesUUID(uuidSinGuiones) {
  const patron = /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/i;
  const coincidencia = uuidSinGuiones.match(patron);
  
  if (!coincidencia) {
    throw new Error('Formato de UUID inválido');
  }
  
  return `${coincidencia[1]}-${coincidencia[2]}-${coincidencia[3]}-${coincidencia[4]}-${coincidencia[5]}`;
}

// Ejemplo de conversión
const uuid = crypto.randomUUID();
const uuidCompacto = uuidSinGuiones(uuid);
const uuidRestaurado = agregarGuionesUUID(uuidCompacto);

console.log('UUID original:', uuid);
console.log('UUID compacto:', uuidCompacto);
console.log('UUID restaurado:', uuidRestaurado);
console.log('Son iguales:', uuid === uuidRestaurado);

Los UUIDs proporcionan una solución robusta y escalable para la generación de identificadores únicos en aplicaciones Node.js, especialmente útiles en arquitecturas distribuidas donde la coordinación centralizada no es práctica o deseable.

Aprende Node online

Otras lecciones de Node

Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Node y certifícate

Ejercicios de programación de Node

Evalúa tus conocimientos de esta lección Módulo crypto con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.