Certificados digitales
El certificado .p12 firma el XML de la factura con XAdES-EPES. Este documento
explica la escalera de certificados y un malentendido frecuente que hace perder
horas de debugging.
La escalera de tres niveles
Section titled “La escalera de tres niveles”Nivel 1 — test (sandbox) Emitia usa un certificado mock interno La firma XML es válida técnicamente La DIAN NO ve este documento (sandbox, no envía)
Nivel 2 — habilitacion Requieres tu propio certificado .p12 emitido por CA ONAC La DIAN recibe y valida el documento El certificado debe estar en la cadena de confianza ONAC
Nivel 3 — production Mismo certificado que habilitacion (o uno nuevo, también ONAC) La DIAN valida en el ambiente de producciónEl malentendido más común
Section titled “El malentendido más común”“En sandbox mi factura se firma y el XML se ve bien. En habilitación, la DIAN me rechaza con código de firma inválida.”
Por qué ocurre: el certificado de prueba (autofirmado, o emitido por una CA propia) firma el XML correctamente a nivel técnico. La firma es válida como operación criptográfica. Pero la DIAN exige que el certificado esté emitido por una CA acreditada por la ONAC (Organismo Nacional de Acreditación de Colombia). Si el emisor del certificado no está en el bundle de confianza que la DIAN acepta, el documento es rechazado aunque la firma sea matemáticamente correcta.
La solución: para habilitacion y production, necesitas un certificado real
emitido por una CA como Certicámara, GSE, o ANDES SCD, todas acreditadas ONAC.
El CUFE es independiente del certificado
Section titled “El CUFE es independiente del certificado”Este es el segundo malentendido frecuente:
“¿Tengo que re-calcular el CUFE si cambio de certificado?”
No. El CUFE (Código Único de Factura Electrónica) se calcula con un algoritmo
SHA-384 sobre los datos de la factura y la clave técnica de la resolución (ClTec).
El certificado no entra en el cálculo del CUFE.
| En el cálculo del CUFE | No en el cálculo del CUFE |
|---|---|
| Número de factura | Certificado .p12 |
| Fecha y hora | Clave pública del cert |
| Subtotal y totales | Firma XAdES |
| NIT del emisor | |
| Documento del adquirente | |
Clave técnica (ClTec) | |
| Tipo de ambiente (1 o 2) |
Esto significa que puedes rotar el certificado sin que cambie el CUFE de ninguna factura existente ni futura.
CAs acreditadas ONAC (selección)
Section titled “CAs acreditadas ONAC (selección)”| Entidad | Web |
|---|---|
| Certicámara | certicamara.com |
| GSE (Gestión de Seguridad Electrónica) | gse.com.co |
| ANDES SCD | andesscd.com.co |
El certificado debe tener:
keyUsage: digitalSignature- Vigencia mínima para tu plan de operación (recomendado: 2-3 años)
- NIT del certificado = NIT del tenant en Emitia (Emitia lo valida al subir)
Cómo subir tu certificado
Section titled “Cómo subir tu certificado”# Emitia-Version: 2026-01-01curl -X POST https://api.emitia.co/v1/certificates \ -H "Authorization: Bearer sk_live_TU_CLAVE" \ -H "Content-Type: application/json" \ -H "Emitia-Version: 2026-01-01" \ -d '{ "filename": "miempresa.p12", "content_base64": "MIIK...", "password": "contraseña_del_p12" }'Emitia valida:
- Que la contraseña sea correcta.
- Que el certificado no esté vencido.
- Que el NIT del certificado coincida con el NIT del tenant.
- Que el emisor esté en el bundle ONAC.
- Que
keyUsageincluyadigitalSignature.
La respuesta incluye solo metadatos — nunca el contenido del .p12:
{ "id": "cert_01HX…", "subject": "CN=Mi Empresa SAS, O=Mi Empresa SAS, C=CO", "issuer": "CN=Certicámara S.A., O=Certicámara S.A., C=CO", "valid_from": "2026-01-01", "valid_to": "2029-01-01", "fingerprint": "sha256:abc123…"}Custodia del certificado: OpenBao Transit
Section titled “Custodia del certificado: OpenBao Transit”Emitia nunca guarda el .p12 en texto plano. El proceso es:
Tu .p12 (subido vía API) ↓OpenBao Transit (encrypt-as-a-service, self-hosted) ↓ ciphertext: "vault:v1:abc..."R2 Storage (Cloudflare) ↓ referencia en DBcertificates.vault_transit_key + certificates.r2_keyPuntos clave:
- La llave maestra nunca sale de OpenBao.
- Una transit key por tenant (
emitia-tenant-{id}). - Cada uso queda en el audit log de OpenBao.
- La rotación del certificado no requiere re-cifrar el anterior.
- Si revocan el acceso al tenant, sus
.p12quedan irrecuperables automáticamente.
Emitia solo desencripta el .p12 durante la firma, en memoria, y lo zeroiza
inmediatamente después.
Próximos pasos
Section titled “Próximos pasos”- Conceptos: Ambientes — cuándo usar cada ambiente
- Inicio rápido — primera factura en sandbox sin cert
- Seguridad (internal) — para el equipo de Emitia, ver
docs/SECURITY.md