La firma de código tiene un problema de gestión de claves. El montaje habitual copia un archivo .pfx, o conecta un token USB, en cada agente de compilación que necesita firmar una versión. La clave privada acaba en máquinas que ejecutan scripts de compilación no confiables, no hay registro de qué se firmó ni quién lo firmó, y rotar el certificado implica tocar cada agente. sgcSign 2026.6 responde a esto con un nuevo servidor de firma: un demonio autoalojado que expone la firma a través de una API REST sobre TLS, de modo que tus pipelines y desarrolladores firman de forma remota mientras la clave de firma permanece en un único lugar.
La clave nunca sale del servidor. Mejor aún, el servidor puede actuar como fachada de un token de hardware o de un KMS en la nube, de modo que la clave nunca existe como archivo. Cada petición se autentica con una clave de API, está sujeta a límites de tasa y se escribe en un registro de auditoría a prueba de manipulaciones. Esta publicación cubre qué puede firmar el servidor, cómo configurarlo, un inicio rápido de cinco minutos y las dos formas de invocarlo: curl simple y el cliente de línea de comandos sgcsign incluido.
Qué firma
Un servidor, una API, ocho formatos de firma. La misma forma de endpoint (POST /api/v1/sign/<format>) cubre tanto ejecutables como documentos:
- Authenticode — archivos PE de Windows:
.exe,.dll,.sys,.msi,.cab,.ocx. Firma dual opcional SHA-1 + SHA-256 para versiones antiguas de Windows. - PAdES — documentos PDF, con motivo, ubicación, contacto y nombre del firmante.
- XAdES — XML, enveloped, enveloping o detached, con perfiles de facturación electrónica nacionales como eIDAS, FacturaE, FatturaPA, KSeF, Peppol, VeriFactu y TicketBAI.
- CAdES — PKCS#7 detached (
.p7s) sobre cualquier contenido. - ClickOnce — manifiestos de aplicación y de implementación.
- NuGet — firma de paquetes
.nupkg. - VSIX — paquetes de extensiones de Visual Studio.
- PowerShell — scripts
.ps1/.psm1/.psd1(Authenticode SIP).
Cada formato acepta una URL de sello de tiempo RFC 3161 opcional, de modo que las firmas siguen siendo válidas después de que el certificado de firma caduque. Un endpoint complementario POST /api/v1/verify comprueba una firma existente y devuelve el subject del firmante, la validez y el sello de tiempo.
Proveedores de claves conectables
Un proveedor es un identificador con nombre de una clave de firma. El invocador nombra un proveedor en cada petición; nunca ve la clave. Puedes registrar tantos como necesites, y el mismo servidor puede combinar un PFX local para herramientas internas con un certificado EV respaldado en la nube para versiones públicas. Se admiten nueve tipos de proveedor:
- Almacén de certificados de Windows (
WinCertStore) — selecciona un certificado por huella digital o subject, ideal para certificados EV registrados en un token. - PFX y PEM — archivos de certificado y clave en disco.
- PKCS#11 / HSM (
PKCS11) — tokens de hardware como YubiKey, o un HSM de red. - AWS KMS, Azure Trusted Signing, Google Cloud KMS, HashiCorp Vault y Certum SimplySign — la clave privada reside en el servicio en la nube y nunca llega al servidor.
Los secretos permanecen fuera del archivo de configuración. Cualquier parámetro cuyo nombre termine en _env se lee de una variable de entorno, de modo que la contraseña del PFX, el PIN del token o el secreto en la nube los proporciona el entorno del servicio, sin quedar guardados en disco.
Configuración del servidor
Todo el servidor se gobierna mediante un único archivo JSON, sgcSignServer.conf.json (se incluye un ejemplo documentado como sgcSignServer.conf.sample.json). La forma de nivel superior es un puñado de bloques:
{
"server": {
"listen": "0.0.0.0",
"port": 8443,
"tls": {
"enabled": true,
"cert_file": "certs/server.crt",
"key_file": "certs/server.key"
},
"max_upload_mb": 512,
"firewall": {
"enabled": true,
"whitelist": ["10.0.0.0/8"],
"brute_force": { "enabled": true, "max_attempts": 5, "ban_duration_sec": 900 },
"rate_limit": { "enabled": true, "max_connections_per_ip": 30, "time_window_sec": 60 }
}
},
"storage": { "sqlite_path": "data/sgcsignserver.db" },
"admin": {
"initial_user": "admin",
"initial_password_env": "SGCSIGN_ADMIN_INIT_PW",
"session_timeout_minutes": 60
},
"audit": { "retention_days": 365 },
"providers": [
{
"name": "pfx-build",
"type": "PFX",
"params": { "file": "certs/build.pfx", "password_env": "SGCSIGN_PFX_PW" }
},
{
"name": "ev-release",
"type": "AzureTS",
"params": {
"tenant_id": "00000000-0000-0000-0000-000000000000",
"client_id": "00000000-0000-0000-0000-000000000000",
"client_secret_env": "SGCSIGN_AZURE_TS_SECRET",
"account": "your-account",
"certificate_profile": "your-profile",
"endpoint": "https://eus.codesigning.azure.net/"
}
}
]
}
Los bloques se corresponden directamente con funcionalidades. server establece la dirección de escucha, el puerto (8443 por defecto), el certificado TLS y el firewall integrado (listas de IP permitidas y denegadas, bloqueo por fuerza bruta, limitación de tasa de conexiones, protecciones contra path-traversal y contra contenidos maliciosos). storage apunta a la base de datos SQLite que contiene los usuarios, las claves de API, el registro de auditoría y la cola de webhooks. admin define el primer usuario y cuánto duran las sesiones. audit establece la ventana de retención. providers es la lista anterior.
La contraseña de administrador de arranque nunca se almacena en el archivo. En el primer arranque, cuando la tabla de usuarios está vacía, el servidor lee la variable de entorno indicada por initial_password_env (por defecto SGCSIGN_ADMIN_INIT_PW) y crea la cuenta de administrador. En cada arranque posterior esa variable se ignora, de modo que solo sirve para sembrar el primer inicio de sesión.
Inicio rápido en cinco minutos
Instala el servidor (el asistente de configuración registra el servicio de Windows, o ejecuta sgcSignServer.exe --install), y luego:
1. Crea un certificado de firma de código de prueba y expórtalo como PFX. Para una implementación real importas tu certificado emitido por una CA, o te saltas este paso y apuntas un proveedor a tu token o a tu KMS en la nube.
$cert = New-SelfSignedCertificate -Type CodeSigningCert `
-Subject "CN=sgcSign Quickstart" -KeyAlgorithm RSA -KeyLength 3072 `
-CertStoreLocation Cert:\CurrentUser\My -NotAfter (Get-Date).AddYears(1)
$pw = ConvertTo-SecureString "QuickStartPFX!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -Password $pw `
-FilePath "C:\Program Files\sgcSign Server\certs\quickstart.pfx"
2. Establece los secretos como variables de entorno de máquina para que la cuenta del servicio pueda leerlos:
setx /M SGCSIGN_ADMIN_INIT_PW "ChangeMeNow!"
setx /M SGCSIGN_PFX_PW "QuickStartPFX!"
3. Escribe una configuración mínima con un proveedor PFX (TLS desactivado solo para una prueba en localhost):
{
"server": { "listen": "127.0.0.1", "port": 8443, "tls": { "enabled": false } },
"storage": { "sqlite_path": "data/sgcsignserver.db" },
"admin": { "initial_user": "admin", "initial_password_env": "SGCSIGN_ADMIN_INIT_PW" },
"audit": { "retention_days": 365 },
"providers": [
{ "name": "pfx-quickstart", "type": "PFX",
"params": { "file": "certs/quickstart.pfx", "password_env": "SGCSIGN_PFX_PW" } }
]
}
4. Inicia el servicio y confirma que está escuchando:
sc start sgcSignServer
sc query sgcSignServer
Si se detiene al arrancar, ejecútalo en primer plano con sgcSignServer.exe --console --config <path> para ver el error en vivo (una SGCSIGN_ADMIN_INIT_PW ausente o un PFX que no se puede leer son las causas habituales). El indicador --selftest-providers carga la configuración, inicializa cada proveedor y termina, de modo que puedes validar las claves antes de ponerlo en producción.
5. Inicia sesión y crea una clave de API. Abre http://localhost:8443/admin, inicia sesión como admin con la contraseña de arranque y crea una clave de API. La clave en texto plano se muestra una sola vez en el momento de su creación; cópiala de inmediato. Las claves llevan el prefijo sgcsk_.
6. Firma un ejecutable de prueba con curl:
curl -X POST http://localhost:8443/api/v1/sign/authenticode ^
-H "X-API-Key: sgcsk_..." ^
-F file=@app.exe ^
-F provider=pfx-quickstart ^
-F hash=sha256 ^
--output app-signed.exe
Ese es el ciclo completo: configurar un proveedor, emitir una clave, firmar. A partir de aquí sustituyes el PFX autofirmado por un certificado real, activas TLS y bloqueas el firewall para limitarlo a la subred de tu CI.
Firma sobre REST
Cada petición de firma es un POST con el archivo y unos pocos campos de formulario. La autenticación es una sola cabecera, ya sea X-API-Key: sgcsk_... o Authorization: Bearer sgcsk_.... La respuesta es el artefacto firmado como application/octet-stream, con el subject del firmante y la duración de la firma devueltos en las cabeceras X-Sgcsign-Signer-Subject y X-Sgcsign-Duration-Ms.
Firma de un PDF, con un motivo y una ubicación de firma visibles:
curl -X POST https://sign.acme.local:8443/api/v1/sign/pades \
-H "X-API-Key: sgcsk_..." \
-F file=@invoice.pdf \
-F provider=qualified-eu \
-F tsa_url=http://timestamp.digicert.com \
-F reason=Approved \
-F location="Madrid, Spain" \
-F signer_name="Acme Billing" \
-o invoice-signed.pdf
Firma de una factura XML con un perfil XAdES de eIDAS:
curl -X POST https://sign.acme.local:8443/api/v1/sign/xades \
-H "X-API-Key: sgcsk_..." \
-F file=@invoice.xml \
-F provider=qualified-eu \
-F profile=eidas \
-F xades_type=enveloped \
-o invoice-signed.xml
La verificación de una firma devuelve JSON en lugar de un archivo:
curl -X POST https://sign.acme.local:8443/api/v1/verify \
-H "X-API-Key: sgcsk_..." \
-F file=@app-signed.exe \
-F format=authenticode
{ "valid": true, "status": "valid",
"subject": "CN=Acme Inc., O=Acme, C=US",
"has_timestamp": true, "timestamp_time": "2026-06-15T13:01:17.000Z" }
El cliente de línea de comandos sgcsign
El servidor incluye sgcsign, una pequeña CLI multiformato con cuatro verbos: sign, verify, keys y health. La URL del servidor, la clave de API y el proveedor pueden provenir de indicadores o de las variables de entorno SGCSIGN_SERVER, SGCSIGN_APIKEY y SGCSIGN_PROVIDER, lo que mantiene los secretos fuera de la línea de comandos en CI:
set SGCSIGN_SERVER=https://sign.acme.local:8443
set SGCSIGN_APIKEY=sgcsk_...
sgcsign sign -f authenticode -p pfx-build -o app-signed.exe app.exe ^
--tsa http://timestamp.digicert.com --desc "Acme Installer"
sgcsign verify -f authenticode app-signed.exe
Para Authenticode hay un truco que ahorra ancho de banda. Con el modo de prehash (--prehash, activado por defecto) la CLI calcula el hash del PE localmente y sube solo el hash, unos pocos cientos de bytes en lugar de un binario de varios megabytes. El servidor firma el hash y devuelve un blob PKCS#7 detached, que el cliente incrusta en el archivo. En un agente de CI con ancho de banda limitado que firma un instalador grande, eso convierte una subida de varios megabytes en una diminuta.
Seguridad y operación
El servidor está diseñado para funcionar sin supervisión delante de una clave de firma real, así que la superficie operativa importa tanto como la criptografía:
- Consola de administración en
/admin: panel de control, claves de API, proveedores, usuarios, proyectos y el rastro de auditoría, todo en el navegador. - Claves de API con límites de tasa por clave y cuotas diarias. Una clave que alcanza su límite recibe
429con una cabeceraRetry-After. - Proyectos multiinquilino, de modo que una clave acotada a un equipo no puede usar los proveedores de otro equipo.
- Flujo de aprobación (opcional): un flujo de dos pasos en el que una petición de firma queda retenida hasta que un operador la aprueba, pensado para certificados de versión de alto valor.
- Registro de auditoría a prueba de manipulaciones: cada firma, verificación, inicio de sesión y cambio de configuración se encadena con un hash, de modo que el registro no se puede alterar después.
- Métricas de Prometheus en
/api/v1/metricsy sondas de liveness/readiness en/api/v1/health, listas para tu pila de monitorización. - Webhooks salientes que hacen POST de un evento JSON en cada operación de firma, para integrar la firma en Slack, un SIEM o un panel de versiones.
- Especificación OpenAPI 3.1 en
/api/v1/openapi.jsoncon una Swagger UI interactiva en/api/v1/docs. - TLS 1.2+ con servicio opcional de desafíos ACME / Let's Encrypt, además del firewall integrado para filtrado de IP y protección contra abusos.
Por qué autoalojar
El objetivo del servidor es reducir una flota de agentes de compilación arriesgados, portadores de claves, a un único endpoint reforzado. La clave de firma reside únicamente en el servidor, o en el HSM o KMS en la nube del que el servidor hace de fachada, nunca en las máquinas que ejecutan tus scripts de compilación. El acceso está controlado por tokens de API por clave con cuotas, acotados a proyectos, y cada firma queda registrada en un registro de auditoría que tú controlas. Rotar un certificado, revocar la clave de un agente comprometido o demostrar quién firmó qué se convierten en un único lugar donde mirar en vez de muchos.
Disponibilidad
El sgcSign Server forma parte de sgcSign 2026.6.0. Funciona como servicio de Windows o como aplicación de consola, se configura mediante el único archivo JSON mostrado arriba y se gobierna desde curl, la CLI sgcsign incluida o cualquier cliente HTTP a través de su descripción OpenAPI.
¿Preguntas, comentarios o ayuda para ponerlo en marcha? Ponte en contacto. Recibirás respuesta de las personas que escribieron el código.
