Skip to content

Repository files navigation

FirMapache

FirMapache

Firma digital local para JSON/JWS y PDF/PAdES con soporte PKCS#11 y PKCS#12.

License GPLv3 Rust Tauri v2 PKCS#11 PKCS#12 JWS PDF PAdES

FirMapache es un firmador local escrito en Rust y Tauri. Expone un servicio HTTPS local compatible con flujos POST /sign, permite aprobar firmas desde una ventana de escritorio y soporta tokens fisicos PKCS#11 y tokens virtuales PKCS#12/PFX para pruebas controladas.

FirMapache combina un servicio HTTPS local compatible con POST /sign, una interfaz de escritorio Tauri con bandeja del sistema y un core Rust reutilizable para firmar con tokens criptograficos reales. Soporta JWS compact RS256, firma PDF ETSI.CAdES.detached, cache de tokens/certificados, validacion, diagnostico y flujo visual de aprobacion. El PIN se solicita solo en la UI local o comandos internos de escritorio, no se guarda y no forma parte del payload publico de POST /sign.

Tabla de contenido

Características

  • ✓ Firma JSON/JWS compact con RS256.
  • ✓ Firma PDF con /SubFilter /ETSI.CAdES.detached.
  • ✓ Compatibilidad con tokens fisicos PKCS#11.
  • ✓ Compatibilidad con tokens virtuales PKCS#12/PFX para QA y desarrollo.
  • ✓ Soporte probado con ePass2003 y driver Feitian libcastle.so.
  • ✓ Selección por identidad/certificado de firma.
  • ✓ Servicio HTTPS local en https://localhost:4637/.
  • ✓ Interfaz de escritorio Tauri v2 con bandeja del sistema.
  • ✓ Ventana dedicada para solicitudes de firma.
  • ✓ Firma manual multiarchivo para JSON y PDF.
  • ✓ Empaquetado ZIP automatico cuando se firman varios archivos manualmente.
  • ✓ Autofirma configurable para entornos controlados.
  • ✓ Cache de tokens/certificados y watcher PC/SC.
  • ✓ Validación JWS y diagnóstico estructural PDF.
  • ✓ Diagnóstico exportable sin PIN, claves privadas ni contenido de documentos.

Capturas

Las capturas reales se agregaran cuando se prepare el paquete publico. La estructura ya esta lista en docs/images/.

Pantalla Placeholder
Dashboard Dashboard
Firma manual Firma manual
Solicitud de firma Solicitud de firma
Identidades Identidades
Diagnóstico Diagnóstico

Requisitos

  • Rust estable con Cargo
  • El modulo PKCS#11 correspondiente al token, por ejemplo el driver Feitian ePass2003 o OpenSC.
  • pcscd activo cuando el token o lector requiera acceso PC/SC.
  • OpenSSL de sistema para soporte PKCS#12/PFX de desarrollo (openssl y cabeceras de desarrollo si la distribucion las separa).

PKCS#11, Feitian y OpenSC

PKCS#11 es la interfaz estandar que permite a una aplicacion comunicarse con tokens criptograficos. Algunos dispositivos, como ePass2003, requieren el modulo propietario Feitian (libcastle.so.1.0.0) aunque OpenSC pueda detectar el lector. OpenSC provee una implementacion generica mediante opensc-pkcs11.so. pcscd es el servicio que suele comunicar estos modulos con lectores de tarjetas inteligentes.

El servicio selecciona el modulo en este orden:

  1. La ruta configurada en FIRMAPACHE_PKCS11.
  2. La ruta persistida en ~/.config/firmapache/config.toml.
  3. El driver Feitian ePass2003 en su ruta comun de instalacion.
  4. Rutas comunes de OpenSC en Linux.

Si FIRMAPACHE_PKCS11 esta definida pero no existe, el servicio informa el error en los endpoints PKCS#11 en lugar de usar automaticamente otro modulo. Si una ruta persistida no existe, el servicio registra la situacion y continua con la autodeteccion.

Configuración

Al iniciar, el servicio crea y carga automaticamente:

~/.config/firmapache/config.toml

En Linux esta ruta se obtiene mediante el directorio de configuracion del usuario. El archivo inicial tiene este formato:

[server]
host = "127.0.0.1"
port = 4637
https = true

[pkcs11]
library_path = "/usr/lib/libcastle.so.1.0.0"

[cors]
allowed_origins = [
  "http://localhost:3000",
  "http://127.0.0.1:3000",
]

[signing]
default_identity_id = ""

[development]
enabled = false
auto_sign = false
default_identity_id = ""
pin_env = "FIRMAPACHE_DEV_PIN"
remember_pin = false
local_pin = ""
fallback_to_modal = true

[[development.pkcs12_tokens]]
id = "dev-token-qa"
label = "Token virtual QA"
path = "/home/user/certs/dev-token.p12"
password_env = "FIRMAPACHE_DEV_P12_PASSWORD"
remember_password = false
local_password = ""

POST /config actualiza solo los campos enviados y persiste el resultado. La ruta PKCS#11 actualizada se usa en las siguientes operaciones. Como el servidor no se reinicia automaticamente, los cambios de server y cors entran en vigor al siguiente inicio.

La variable FIRMAPACHE_PKCS11 mantiene prioridad absoluta sobre el valor guardado en el TOML.

El bloque [signing] guarda solamente la identidad publica predeterminada para firmar. No guarda PIN ni sesiones. Una identidad tiene la forma:

pkcs11:{token_serial}:{slot_id}:{certificate_id}

Si el token no expone serial, se usa un identificador basado en slot_id y certificate_id.

El bloque [development] existe solo para pruebas locales. Permite autofirmar solicitudes POST /sign usando una identidad configurada. La UI permite ingresar el PIN/contraseña y, opcionalmente, recordarlo localmente en este equipo para comodidad de desarrollo. La variable pin_env se mantiene como compatibilidad, pero ya no es el flujo principal.

Tambien puede registrar tokens virtuales .p12 o .pfx para desarrollo. Al importar o crear un token virtual desde la UI, queda registrado automaticamente, las identidades se actualizan y puede marcarse como identidad predeterminada. Si se usa "Recordar contraseña localmente", la contraseña se guarda en config.toml solo para modo desarrollo. No se guarda la clave privada desencriptada.

Con server.https = true, el servicio genera automaticamente un certificado self-signed para localhost y 127.0.0.1 en:

~/.config/firmapache/certs/localhost.crt
~/.config/firmapache/certs/localhost.key

El certificado local no se instala en el trust store del sistema. Por eso curl requiere -k hasta que el certificado se configure como confiable. Al cargar un archivo creado por una version anterior sin server.https, el antiguo puerto por defecto 4856 se migra automaticamente a 4637; puertos personalizados se conservan.

Instalación

AppImage

Descargue el artefacto FirMapache_0.1.0_amd64.AppImage, marque permisos de ejecucion y arranque la aplicacion:

chmod +x FirMapache_0.1.0_amd64.AppImage
./FirMapache_0.1.0_amd64.AppImage

Debian/Ubuntu

Instale el paquete .deb:

sudo apt install ./FirMapache_0.1.0_amd64.deb

Dependencias runtime recomendadas:

sudo apt install pcscd pcsc-tools opensc libpcsclite1 openssl

Para compilar desde fuente en Debian/Ubuntu:

sudo apt install build-essential pkg-config libssl-dev libpcsclite-dev \
  libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf

Arch Linux

Por ahora se recomienda usar el AppImage para instalacion simple. Dependencias runtime recomendadas:

sudo pacman -S --needed pcsc-tools pcsclite ccid opensc openssl

Para compilar desde fuente en Arch Linux:

sudo pacman -S --needed base-devel rustup pkgconf openssl pcsclite ccid opensc \
  webkit2gtk-4.1 libayatana-appindicator

Driver Feitian ePass2003

FirMapache no empaqueta drivers propietarios. Para ePass2003 puede ser necesario instalar el driver Feitian y configurar una de estas rutas:

/usr/lib/libcastle.so.1.0.0
/usr/lib/ePass2003-Linux-x64/redist/libcastle.so.1.0.0
/usr/lib/ePass2003_adsib/redist/libcastle.so.1.0.0

Ejecución desde fuente

cargo run

Por defecto el servidor escucha en https://localhost:4637/.

Para desarrollo sin TLS, establezca https = false en el bloque [server]; en ese modo escucha en http://127.0.0.1:4637/.

curl http://127.0.0.1:4637/
curl http://127.0.0.1:4637/status

Interfaz Tauri

La aplicacion de escritorio inicia el mismo servicio Axum local y comparte su AppState con los comandos Tauri; no duplica operaciones PKCS#11 ni logica de firma. Para levantarla en desarrollo:

cargo install tauri-cli --version "^2" --locked
cargo tauri dev

No ejecute cargo run al mismo tiempo: la aplicacion Tauri ya arranca el servicio local en el puerto configurado.

La ventana permite:

  • Ver estado, version, modo HTTPS, puerto y driver PKCS#11 detectado.
  • Editar host, puerto y modo HTTPS del servidor local.
  • Elegir una biblioteca .so o .so.* y guardarla en config.toml.
  • Consultar tokens y certificados publicos.
  • Actualizar manualmente la cache de tokens/certificados.
  • Firmar manualmente archivos JSON locales como JWS compact.
  • Firmar manualmente archivos PDF con una firma detached ETSI.CAdES.detached.
  • Ver sesiones de firma pendientes, seleccionar identidad de firma y aprobar o rechazar visualmente su flujo.

La aplicacion vive en la bandeja del sistema. Al cerrar la ventana principal, la app se oculta pero el servidor local sigue activo. Desde el tray se puede abrir FirMapache, mostrar sesiones pendientes, reiniciar el servidor embebido o salir completamente.

Configurar servidor local desde la UI

En Configuracion > Servidor local se pueden editar:

  • host, por ejemplo 127.0.0.1 o localhost;
  • port, entre 1024 y 65535;
  • https, activado o desactivado.

La UI muestra la URL activa, por ejemplo:

https://localhost:4637/

Los cambios se guardan en ~/.config/firmapache/config.toml, pero requieren reiniciar el servidor local para aplicarse. Use el boton Reiniciar servidor desde la misma seccion o desde el menu del tray.

0.0.0.0 se acepta para casos controlados, pero muestra una advertencia porque expone el firmador en la red. No se recomienda para uso normal.

Pruebas rapidas:

curl -k https://localhost:4637/status

Si HTTPS esta desactivado:

curl http://127.0.0.1:4637/status

Si cambia el puerto, por ejemplo a 4638, reinicie el servidor y pruebe:

curl -k https://localhost:4638/status

Comportamiento de firma y autofirma

La autofirma viene desactivada por defecto. Cuando esta apagada, el flujo normal no cambia: POST /sign crea una sesion pendiente y abre la ventana Solicitud de firma para que el usuario seleccione identidad e ingrese PIN.

Configuracion de ejemplo:

[development]
enabled = true
auto_sign = true
default_identity_id = "pkcs11:..."
pin_env = "FIRMAPACHE_DEV_PIN"
remember_pin = true
local_pin = "12345678"
fallback_to_modal = true

Con enabled = true y auto_sign = true, POST /sign intenta firmar automaticamente usando development.default_identity_id y el PIN/contraseña configurado para desarrollo. Funciona para format = "jws" y format = "pdf". La variable FIRMAPACHE_DEV_PIN sigue funcionando como compatibilidad si no se recuerda un PIN local.

Si falta identidad, no hay PIN/contraseña disponible o la autofirma falla:

  • con fallback_to_modal = true, FirMapache continua con la ventana de firma interactiva normal;
  • con fallback_to_modal = false, POST /sign responde un error JSON claro.

La UI incluye una seccion Configuracion > Firma con este flujo:

  1. Elegir Autofirma como comportamiento de firma.
  2. Elegir identidad.
  3. Ingresar PIN / contraseña.
  4. Opcionalmente marcar Recordar PIN localmente.
  5. Pulsar Probar autofirma.

Si se recuerda el PIN, queda almacenado localmente en este equipo y claramente marcado como configuracion local de autofirma. No usar esta opcion en equipos compartidos.

Advertencia: no use autofirma con tokens oficiales en entornos no controlados. Permite firmar sin confirmacion visual.

Tokens virtuales P12/PFX de desarrollo

FirMapache puede importar archivos .p12 o .pfx existentes como identidades virtuales de desarrollo (provider = "pkcs12"). Esto no reemplaza a PKCS#11 en produccion: sirve para QA, pruebas automatizadas y ambientes locales donde no se quiere depender de un token fisico.

Tambien puede crear un token virtual nuevo desde la UI. La app genera una clave RSA 2048, un certificado X.509 self-signed con SHA256withRSA, KeyUsage digitalSignature y nonRepudiation, y empaqueta clave privada + certificado en un .p12/.pfx protegido por la contraseña indicada. La clave privada no se escribe fuera del .p12/.pfx.

Configuracion de ejemplo:

[[development.pkcs12_tokens]]
id = "dev-token-qa"
label = "Token virtual QA"
path = "/home/user/certs/dev-token.p12"
password_env = "FIRMAPACHE_DEV_P12_PASSWORD"
remember_password = true
local_password = "clave"

La UI permite importar el archivo indicando la contraseña directamente. El token queda registrado, las identidades se actualizan y puede marcarse como predeterminado. password_env se mantiene como compatibilidad para instalaciones existentes. Las identidades se muestran junto a las fisicas:

[PKCS#12] Token virtual QA
  CN=Certificado Dev QA

Para autofirma, seleccione la identidad pkcs12:... como identidad de autofirma. POST /sign sigue recibiendo el mismo payload puro; no se agrega PIN, ruta, contraseña ni identity_id al contrato publico.

En firma manual, si selecciona una identidad P12, el campo de PIN se trata como PIN / contraseña P12 para esa firma. La contraseña no se guarda.

Crear token virtual desde la app:

  1. En Identidades > Tokens virtuales P12/PFX, complete ID, etiqueta, CN, organizacion, pais, vigencia y contraseña.
  2. Pulse Crear token virtual.
  3. FirMapache abre el dialogo para guardar el archivo .p12 o .pfx.
  4. FirMapache guarda el archivo y lo registra automaticamente en development.pkcs12_tokens.
  5. La identidad queda disponible inmediatamente y la app pregunta si desea usarla como predeterminada.

Limitaciones de seguridad:

  • no usar como modo produccion;
  • el PIN/contraseña recordado queda en almacenamiento local simple de desarrollo;
  • no guardar credenciales en equipos compartidos;
  • no exportar contraseñas en diagnostico;
  • no guardar claves privadas desencriptadas en disco;
  • no mantener la clave privada cargada mas tiempo del necesario para firmar.

Cuando llega una solicitud compatible, FirMapache abre una ventana dedicada Solicitud de firma sobre el escritorio. Esa ventana pide certificado y PIN, muestra estados de carga como Firmando... no retire el token, y deshabilita los botones mientras se completa la operacion. El PIN solo existe durante esa aprobacion y no se almacena.

Cache de tokens y certificados

FirMapache carga tokens y certificados en segundo plano al iniciar Tauri para que la ventana de firma abra rapido y no repita lecturas PKCS#11 innecesarias. La cache guarda solamente metadata publica:

  • tokens detectados;
  • certificados publicos;
  • DER publico del certificado en Base64;
  • identidades de firma normalizadas;
  • hora de carga y driver PKCS#11 usado.

No se cachea PIN, sesiones PKCS#11 logueadas, claves privadas ni contenido de archivos. El PIN se pide solo al firmar y se limpia despues de usarlo.

La cache se invalida cuando cambia la configuracion del driver PKCS#11, cuando se pulsa Actualizar tokens/certificados o cuando falla una lectura durante la recarga. La UI muestra cantidad de tokens, cantidad de certificados, hora de ultima carga y driver cacheado.

Los endpoints GET /tokens y GET /certificates, el modal de firma y la firma manual prefieren la cache. Si el certificado seleccionado no esta en cache, el core hace una recarga controlada como fallback antes de fallar.

Cada certificado utilizable se muestra como una identidad de firma agrupada por token. Esto evita depender visualmente solo del slot_id cuando hay varios tokens o varios certificados conectados.

La UI permite marcar una identidad con Usar como predeterminado. Si no hay predeterminada y existe una sola identidad disponible, FirMapache la selecciona automaticamente. Si la identidad guardada ya no esta conectada, se muestra como no disponible y la UI advierte:

El token o certificado seleccionado ya no está disponible. Actualice tokens/certificados.

Para invalidar o actualizar la cache, pulse Actualizar tokens/certificados. Tambien se invalida al cambiar el driver PKCS#11 o cuando una lectura detecta que el token seleccionado ya no esta disponible.

Firma manual

La seccion Firma manual permite seleccionar uno o varios archivos locales sin depender de una web externa. FirMapache detecta automaticamente el tipo de archivo y el formato de salida:

  • JSON: genera JWS compact.
  • PDF: valida header %PDF- y marcador %%EOF, genera una firma PDF con /Filter /Adobe.PPKLite, /SubFilter /ETSI.CAdES.detached, /ByteRange y CMS/CAdES detached SHA-256.
  • Otros formatos: se muestran como no soportados y se omiten al firmar.
  1. Abrir FirMapache con cargo tauri dev.
  2. Esperar o pulsar Actualizar tokens/certificados si el token se conecto despues de abrir la app.
  3. En Firma manual, pulsar Seleccionar archivos.
  4. Revisar la lista: JSON → JWS, PDF → PDF/PAdES o No soportado.
  5. Quitar archivos individuales si hace falta o usar Limpiar para reiniciar la lista.
  6. Seleccionar identidad de firma, escribir el PIN y pulsar Firmar N archivos.
  7. Elegir una carpeta destino.

Si se firma un solo archivo, FirMapache guarda el archivo firmado directamente:

solicitud.json -> solicitud_firmado.jws
factura.pdf    -> factura_firmado.pdf

Si se firman varios archivos, FirMapache genera un ZIP unico:

firmados_dd-mm-yyyy-hh:mm:ss.zip

Dentro del ZIP, los archivos se renombran con el sufijo _firmado:

solicitud_firmado.jws
factura_firmado.pdf
contrato_firmado.pdf
reporte_firmado.jws

Si existe una colision de nombres, FirMapache agrega un contador como factura_firmado(1).pdf sin preguntar. Si un archivo falla, registra el error, continua con los demas y muestra un resumen final.

El archivo guardado contiene el JWS compact en texto:

header.payload.signature

Para PDF, el archivo guardado conserva el documento original con una firma digital invisible. Puede inspeccionarse con:

pdfsig archivo-firmado.pdf

La compatibilidad esperada es:

Signature Type: ETSI.CAdES.detached
Signing Hash Algorithm: SHA-256

Limitaciones actuales de PDF: no se implementa TSA, LTV, OCSP ni CRL. La firma es detached y usa el certificado seleccionado desde el token PKCS#11. El PIN no se guarda ni se registra.

Validación y diagnóstico

La seccion Validacion y diagnostico permite revisar archivos firmados y el estado local del firmador sin exponer informacion sensible.

Validacion JWS:

  • acepta JWS compact directo o Base64(JWS compact);
  • separa header.payload.signature;
  • muestra alg, presencia de x5c, subject del certificado si puede parsearse y tamano del payload;
  • verifica la firma RS256 usando el certificado x5c.

Validacion PDF:

  • detecta /ByteRange, /Contents, /Filter /Adobe.PPKLite, /SubFilter /ETSI.CAdES.detached, /M, /Name, /Reason, /Location y /ContactInfo;
  • muestra diagnostico estructural;
  • para validacion criptografica PDF completa, por ahora recomienda:
pdfsig archivo.pdf

Diagnostico del sistema:

  • version de la app;
  • configuracion no sensible del servidor;
  • ruta de driver PKCS#11 configurada y detectada;
  • disponibilidad basica de PC/SC;
  • tokens publicos detectados;
  • certificados publicos resumidos y expiracion;
  • identidades de firma disponibles;
  • identidad predeterminada configurada;
  • certificados expirados y certificados que vencen en menos de 30 dias;
  • ultimo error PKCS#11 conocido durante el diagnostico, si existe.

El boton Exportar diagnostico guarda un .json sin PIN, claves privadas, firmas completas, archivos firmados completos ni contenido de documentos.

API

Consumo desde NextJS

El servicio habilita CORS solamente para aplicaciones web servidas desde:

  • http://localhost:3000
  • http://127.0.0.1:3000

No se habilita el origen comodin (*). Las solicitudes desde otros orígenes no reciben autorización CORS por defecto.

Ejemplo de consulta desde un componente o acción cliente de NextJS:

const response = await fetch("https://localhost:4637/certificates", {
  method: "GET",
  headers: {
    "Content-Type": "application/json",
  },
});

if (!response.ok) {
  throw new Error("No se pudieron cargar los certificados");
}

const certificates = await response.json();

Para POST /sign/hash, el navegador puede enviar JSON desde esos mismos orígenes; el PIN debe existir solo en la solicitud iniciada por el usuario y no debe almacenarse en el frontend.

Desarrollo

Verificar

cargo check
curl -k https://localhost:4637/
curl -k https://localhost:4637/status
curl -k https://localhost:4637/version
curl -k https://localhost:4637/config
curl -k https://localhost:4637/pkcs11/library
curl -k https://localhost:4637/tokens
curl -k https://localhost:4637/certificates

Para probar explicitamente un ePass2003 con el driver propietario:

export FIRMAPACHE_PKCS11=/usr/lib/ePass2003-Linux-x64/redist/libcastle.so.1.0.0
cargo run

curl -k https://localhost:4637/pkcs11/library
curl -k https://localhost:4637/tokens
curl -k https://localhost:4637/certificates

Respuestas esperadas:

{"status":"ok","service":"firmapache"}
{"name":"FirMapache","version":"0.1.0","build_date":"2026-06-05T00:00:00Z","git_commit":"abcdef123456","release_channel":"stable"}

Si el driver Feitian se selecciona automaticamente:

{"found":true,"path":"/usr/lib/ePass2003-Linux-x64/redist/libcastle.so.1.0.0","source":"auto"}

Si se selecciona usando la variable de entorno, source es "env". Si se selecciona desde config.toml, source es "config".

El listado de slots incluye datos publicos del token cuando esta presente:

[{"slot_id":1,"token_present":true,"label":"ePass2003","manufacturer":"Feitian Technologies Co., Ltd","model":"ePass2003","serial_number":"..."}]

Los certificados publicos encontrados se devuelven con su identificador, certificado DER en base64 y metadatos X.509:

[{"slot_id":1,"id":"01","label":"Certificado de firma","certificate_der_base64":"MIIC...","subject":"CN=...","issuer":"CN=...","serial_number":"...","not_before":"2024-...","not_after":"2026-..."}]

API de configuracion

Consultar la configuracion activa:

curl -k https://localhost:4637/config

Actualizar, por ejemplo, solamente el driver PKCS#11:

curl -k -X POST https://localhost:4637/config \
  -H "Content-Type: application/json" \
  -d '{
    "pkcs11": {
      "library_path": "/usr/lib/libcastle.so.1.0.0"
    }
  }'

La configuracion no contiene PIN, certificados privados, sesiones ni datos sensibles del token.

Firma compatible sincrona

El endpoint POST /sign recibe el payload compatible de archivo y formato. Actualmente soporta:

  • format = "jws": genera JWS compact RS256.
  • format = "pdf": genera PDF firmado con ETSI.CAdES.detached.

La solicitud HTTP queda abierta mientras espera autorizacion local. La interfaz Tauri detecta la sesion pendiente y abre la ventana independiente Solicitud de firma para aprobar o rechazar la operacion. La ventana solo muestra nombres y tamanos aproximados de los archivos; no muestra su contenido completo.

Al aprobar, el usuario selecciona un certificado, escribe el PIN del token y el core genera la firma correspondiente al formato solicitado. Para JWS genera:

BASE64URL(header).BASE64URL(payload).BASE64URL(signature)

El campo x5c del header contiene el certificado DER en Base64 estandar. El JWS compact resultante se devuelve codificado nuevamente como Base64 estandar en response.files[].base64.

Para PDF, response.files[].base64 contiene el PDF firmado completo en Base64 estandar y el nombre del archivo se conserva igual al recibido.

Levantar la aplicacion:

cargo tauri dev

Puede cerrar la ventana principal despues de iniciar: FirMapache queda en segundo plano en el tray y el servicio https://localhost:4637/ sigue respondiendo.

Terminal 1:

curl -k -X POST https://localhost:4637/sign \
  -H "Content-Type: application/json" \
  --data-raw '{
    "archivo": [
      {
        "base64": "data:application/json;base64,eyJob2xhIjoibXVuZG8ifQ==",
        "name": "solicitud.json"
      }
    ],
    "format": "jws",
    "language": "es"
  }'

La terminal queda esperando. En la aplicacion Tauri aparece la ventana Solicitud de firma:

  1. Seleccione una identidad de firma.
  2. Escriba el PIN del token.
  3. Pulse Firmar JWS o Firmar PDF, segun el formato.

La terminal responde:

{
  "files": [
    {
      "base64": "BASE64_DEL_JWS_COMPACT",
      "name": "solicitud.json"
    }
  ]
}

Para inspeccionar el JWS devuelto:

echo "BASE64_DEL_JWS_COMPACT" | base64 -d

Debe verse una cadena con tres partes separadas por puntos:

header.payload.signature

Ejemplo PDF:

curl -k -X POST https://localhost:4637/sign \
  -H "Content-Type: application/json" \
  --data-raw '{
    "archivo": [
      {
        "base64": "data:application/pdf;base64,BASE64_PDF",
        "name": "documento.pdf"
      }
    ],
    "format": "pdf",
    "language": "es"
  }'

La app abre Solicitud de firma; el usuario selecciona certificado, ingresa PIN y aprueba. Para guardar la respuesta como PDF:

echo "BASE64_RESPUESTA" | base64 -d > firmado.pdf
pdfsig firmado.pdf

Debe mostrar Signature Type: ETSI.CAdES.detached.

Repita la solicitud y pulse Rechazar en la ventana. El request POST /sign pendiente responde:

{"error":"User cancelled signing operation"}

El panel de sesiones tambien ofrece botones Aprobar y Rechazar. Cerrar la ventana de firma no resuelve la solicitud: permanece pendiente y puede abrirse nuevamente desde el panel o desde el menu del tray. Una misma sesion no genera ventanas automaticas duplicadas.

Si falta certificado o PIN, la UI no permite aprobar. Si el login PKCS#11 falla, se muestra el error y no se reintenta automaticamente.

Los endpoints /sign/sessions/{id}/approve y /reject se conservan como herramientas internas de desarrollo.

Si una solicitud no se resuelve en cinco minutos, responde con HTTP 408:

{"error":"Signing request expired"}

El campo base64 de entrada tambien puede enviarse sin el prefijo data:; la salida siempre contiene Base64 estandar limpio.

Firma de hash

El endpoint POST /sign/hash acepta un hash codificado en base64 y firma sus bytes con el mecanismo RSA_PKCS. El flujo recomendado selecciona el certificado cuya clave privada debe utilizarse.

  1. Listar los certificados publicos del token y copiar el campo id elegido:
curl -k https://localhost:4637/certificates
  1. Generar un hash SHA-256 base64 de prueba:
HASH=$(echo -n "hola" | openssl dgst -sha256 -binary | base64)
  1. Firmar indicando el certificate_id. Reemplace los valores de ejemplo localmente; el PIN no se registra ni se conserva por el servicio:
curl -k -X POST https://localhost:4637/sign/hash \
  -H "Content-Type: application/json" \
  -d '{
    "slot_id": 1,
    "certificate_id": "PEGAR_ID_DEL_CERTIFICADO",
    "pin": "CAMBIAR_POR_PIN_REAL",
    "hash_base64": "PEGAR_HASH_BASE64",
    "mechanism": "RSA_PKCS"
  }'

Respuesta:

{"slot_id":1,"signature_base64":"...","algorithm":"RSA_PKCS","certificate_id":"PEGAR_ID_DEL_CERTIFICADO"}

Advertencia de seguridad: el token puede bloquearse tras intentos de PIN incorrectos. firmapache realiza un solo intento de login por solicitud y no reintenta automaticamente cuando la autenticacion falla.

Si se omite certificate_id, el servicio conserva el modo compatible anterior y selecciona una clave privada disponible, registrando una advertencia. No se recomienda omitirlo si el token contiene mas de un certificado.

Verificacion local

El endpoint POST /verify/hash verifica una firma RSA_PKCS usando solo el certificado publico devuelto por /certificates. No requiere PIN y no accede a la clave privada ni al token.

curl -k -X POST https://localhost:4637/verify/hash \
  -H "Content-Type: application/json" \
  -d '{
    "certificate_der_base64": "BASE64_CERT_DER_OBTENIDO_DE_CERTIFICATES",
    "hash_base64": "BASE64_DEL_HASH",
    "signature_base64": "BASE64_DE_LA_FIRMA",
    "mechanism": "RSA_PKCS"
  }'

Respuesta cuando la firma corresponde al hash y certificado:

{"valid":true,"algorithm":"RSA_PKCS"}

Una firma no válida responde exitosamente con "valid": false.

Si no se encuentra la biblioteca PKCS#11, /tokens responde con HTTP 500:

{"error":"PKCS#11 library not found"}

Estructura

  • src-tauri: aplicacion de escritorio y comandos que consumen el core.
  • ui: interfaz HTML/CSS/JavaScript minima para administracion local.
  • src/server: rutas y handlers HTTP.
  • src/config: configuracion del servicio local.
  • src/core: operaciones reutilizables de PKCS#11, criptografia y firma.
  • src/error: errores convertibles a respuestas HTTP.
  • src/models: modelos JSON de la API.
  • src/utils: utilidades compartidas futuras.

Seguridad

  • El PIN no forma parte del contrato publico de POST /sign.
  • El PIN solo se solicita en la UI local o en comandos internos de escritorio.
  • La app no guarda claves privadas desencriptadas.
  • La cache guarda metadata publica de tokens/certificados, no PIN ni sesiones PKCS#11 logueadas.
  • El diagnostico exportado no incluye PIN, claves privadas, firmas completas ni contenido de documentos.
  • Los tokens virtuales PKCS#12/PFX son para QA, pruebas automatizadas y ambientes controlados. No se recomiendan como reemplazo de tokens fisicos en produccion.
  • El certificado HTTPS local es self-signed y no se instala automaticamente en el trust store del sistema.

Para reportar vulnerabilidades, vea SECURITY.md.

Contribuir

Las contribuciones son bienvenidas. Antes de enviar cambios:

cargo fmt --all
cargo check
cargo test
cargo check --manifest-path src-tauri/Cargo.toml
node --check ui/app.js

Vea CONTRIBUTING.md para el flujo de desarrollo, reporte de bugs y propuesta de cambios.

Release v0.1.0

La primera version publica estable esta documentada en docs/releases/v0.1.0.md.

Comandos sugeridos para publicar el tag:

git tag -a v0.1.0 -m "FirMapache v0.1.0"
git push origin v0.1.0

GitHub Topics sugeridos

Para publicar el repositorio, se sugieren estos topics:

tauri
rust
digital-signature
pkcs11
pkcs12
jws
pades
pdf-signature
linux
pcsc
smart-card

Licencia

FirMapache se distribuye bajo licencia GPL-3.0.

Vea LICENSE para mas informacion.

About

Desktop digital signing application with PKCS#11 tokens, PKCS#12 certificates, JSON/JWS and PDF/PAdES support.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Contributors

Languages