Skip to content

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.


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ó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.


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 CUFENo en el cálculo del CUFE
Número de facturaCertificado .p12
Fecha y horaClave pública del cert
Subtotal y totalesFirma 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.


EntidadWeb
Certicámaracerticamara.com
GSE (Gestión de Seguridad Electrónica)gse.com.co
ANDES SCDandesscd.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)

Terminal window
# Emitia-Version: 2026-01-01
curl -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:

  1. Que la contraseña sea correcta.
  2. Que el certificado no esté vencido.
  3. Que el NIT del certificado coincida con el NIT del tenant.
  4. Que el emisor esté en el bundle ONAC.
  5. Que keyUsage incluya digitalSignature.

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…"
}

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 DB
certificates.vault_transit_key + certificates.r2_key

Puntos 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 .p12 quedan irrecuperables automáticamente.

Emitia solo desencripta el .p12 durante la firma, en memoria, y lo zeroiza inmediatamente después.