DKIM: rotación de llaves, tamaño correcto y por qué la mayoría tiene la configuración insegura

Cómo configurar DKIM con llave 2048-bit, rotar selector periódicamente y diagnosticar firmas inválidas. Los errores más comunes en setups corporativos y cómo migrar sin downtime.

Por qué DKIM es la pieza más subestimada del stack de email

SPF se rompe ruidosamente cuando hay problemas: PermError, fail visible en Postmaster Tools. DMARC se rompe visiblemente: política activa, reject o quarantine notificados. Pero DKIM se rompe silenciosamente: una firma inválida no genera ningún error visible al remitente, solo erosiona reputación a lo largo de semanas.

Esa invisibilidad hace que DKIM sea donde más errores acumulados encontramos en auditorías. Configuraciones que estuvieron bien hace 5 años, llaves que no se rotaron desde 2019, selectores duplicados entre MTAs, llaves de 1024 bits que nunca se actualizaron. Cada uno aporta un drag pequeño en deliverability que sumado degrada la operación.

Este post cubre el setup técnico correcto, cómo rotar sin downtime, los errores operativos frecuentes y cómo migrar configuraciones legacy.

Cómo funciona DKIM realmente

Al enviar un email, el MTA:

  1. Toma el contenido del header y body
  2. Genera un hash criptográfico (típicamente SHA-256)
  3. Firma ese hash con la llave privada RSA del remitente
  4. Inserta la firma en un header DKIM-Signature del email
  5. Envía el email

Al recibir el email, el MTA receptor:

  1. Lee el DKIM-Signature que indica el selector y el dominio firmante
  2. Consulta el DNS de ese dominio: selector._domainkey.dominio.com
  3. Obtiene la llave pública RSA publicada en ese registro TXT
  4. Recalcula el hash del email
  5. Verifica que la firma del remitente corresponde al hash usando la llave pública
  6. Si verifica: DKIM pass. Si no verifica: DKIM fail.

El header DKIM-Signature típico:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=empresa.com;
  s=2025jan; t=1738531234; h=From:To:Subject:Date:Message-ID;
  bh=hash_del_body=;
  b=firma_codificada_en_base64=

Los parámetros críticos:

  • v: versión del protocolo DKIM (siempre 1)
  • a: algoritmo de firma (rsa-sha256 actual, rsa-sha1 deprecado)
  • c: canonicalización (relaxed/relaxed recomendado)
  • d: dominio firmante
  • s: selector (qué llave pública usar)
  • h: headers incluidos en la firma
  • bh: hash del body
  • b: la firma criptográfica

El registro DNS de la llave pública

Para el selector 2025jan del dominio empresa.com, el receptor consulta:

dig TXT 2025jan._domainkey.empresa.com

Y obtiene algo así:

2025jan._domainkey.empresa.com. 300 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0z3..."

Los parámetros del registro DNS:

  • v: versión (DKIM1, único valor válido)
  • k: tipo de llave (rsa actual; ed25519 emergente)
  • p: la llave pública codificada en base64
  • t: flags opcionales (t=y para testing, t=s para strict subdomain matching)

El error #1: llaves de 1024 bits en producción

NIST SP 800-131A y RFC 8301 establecieron que llaves RSA de 1024 bits están deprecadas desde 2018. La recomendación actual es 2048 bits mínimo. Sin embargo, en auditorías encontramos llaves de 1024 bits en aproximadamente el 40% de operaciones panameñas con dominio antiguo.

Por qué importa:

  • Seguridad: 1024 bits es matemáticamente factible de romper por actores con recursos significativos (gobiernos, empresas con clusters de cómputo). Una vez rota una llave, el atacante puede falsificar firmas DKIM y enviar emails que pasan autenticación de tu dominio.

  • Reputación: Google Postmaster Tools muestra advertencia “weak DKIM signature” para dominios con 1024 bits. Esto aporta señal negativa al cálculo de reputación general del dominio.

  • Compliance: empresas con auditorías SOC 2 Type II o ISO 27001 reciben hallazgos negativos cuando sus proveedores tienen DKIM 1024 bits. Si vendés B2B, esto puede costar contratos.

Cómo migrar a 2048 bits sin downtime

Suponé que tu DKIM actual es:

google._domainkey.empresa.com  →  v=DKIM1; k=rsa; p=MIGfMA0GCSqGSI... (1024 bits)

Migración correcta en 4 pasos:

Paso 1: generar llave nueva

En Google Workspace, ir a Apps → Google Workspace → Gmail → Authenticate Email. Crear llave nueva con 2048 bits y selector nuevo (2025dkim). NO desactivar la vieja todavía.

En Postmark, ir a Sender Signatures → DKIM → Generate New Key. Se genera selector nuevo automáticamente.

En PowerMTA propio, generar localmente:

openssl genrsa -out 2025dkim.private.key 2048
openssl rsa -in 2025dkim.private.key -pubout -out 2025dkim.public.key

Paso 2: publicar la llave nueva en DNS

Agregar el TXT record con el selector nuevo, sin tocar el viejo:

google._domainkey.empresa.com       v=DKIM1; k=rsa; p=...(vieja 1024)...
2025dkim._domainkey.empresa.com     v=DKIM1; k=rsa; p=...(nueva 2048)...

Esperar 24-48 horas para propagación DNS.

Paso 3: activar la firma con la llave nueva en el MTA

Cambiar la configuración del MTA para que firme con el selector nuevo. En Google Workspace, “Start Authentication” sobre la llave 2025dkim. En PowerMTA:

<domain empresa.com>
  dkim-sign yes
  dkim-identity 2025dkim
  dkim-private-key /etc/pmta/keys/2025dkim.private.key
</domain>

Verificar enviando un email a un buzón propio y revisando el header DKIM-Signature:

DKIM-Signature: ... s=2025dkim; d=empresa.com; ...

Paso 4: período de grace y eliminación

Mantener AMBAS llaves publicadas en DNS durante 14-30 días. Esto permite que emails antiguos en colas o retries con la firma vieja sigan verificando.

Tras 30 días, monitorear que no haya bounces ni complaints relacionados, y eliminar el registro DNS de google._domainkey.empresa.com.

El error #2: selector duplicado entre proveedores

Caso real visto en auditoría: una empresa tenía Google Workspace y agregó Mailchimp. El integrador configuró Mailchimp con selector default._domainkey por defecto. Resultado:

  • Google firmaba con su llave privada usando selector google._domainkey
  • Mailchimp firmaba con su llave privada usando selector mail._domainkey
  • ESP secundario que migraron meses después usó selector default._domainkey también

Cuando emails del ESP secundario llegaban, el receptor consultaba default._domainkey.empresa.com en DNS, obtenía la llave pública del ESP secundario, pero recibía emails de Google Workspace firmados con otro hash. Firmas inválidas en cascada.

Regla operativa: el selector siempre identifica al proveedor o al periodo de rotación. Nombres recomendados:

  • google._domainkey (Google Workspace)
  • pm._domainkey (Postmark)
  • k1._domainkey, k2._domainkey (Mailchimp usa numeración propia)
  • 2025jan._domainkey, 2025jul._domainkey (rotación semestral propia)

Nunca default._domainkey, mail._domainkey, key1._domainkey u otros genéricos. Generan colisiones cuando se agrega otro proveedor en el futuro.

El error #3: rotación nunca

Tras setup inicial, muchas operaciones dejan el DKIM intacto durante años. La llave privada vive en el MTA, en backups, en consolas de admin, y eventualmente en algún tape de respaldo perdido. Cualquier filtración de cualquiera de esos puntos compromete la llave.

Cuando una llave DKIM se filtra:

  1. Atacante puede falsificar emails con autenticación DKIM válida de tu dominio
  2. Atacante envía phishing que pasa DKIM, pasa DMARC, llega al inbox del destinatario
  3. Destinatario abre el phishing creyendo que es legítimo
  4. Tu dominio recibe complaints masivas, reputación se destruye
  5. Detectar que la causa fue filtración de DKIM es complejo y tardío

La práctica de rotación recomendada:

  • Operaciones pequeñas (<100K envíos/mes): rotación anual
  • Operaciones medianas (100K-1M/mes): rotación semestral
  • Operaciones grandes (>1M/mes): rotación trimestral
  • Operaciones financieras o de salud: rotación trimestral mínimo + auditoría externa

Rotar significa: generar llave nueva, publicar selector nuevo, esperar propagación, activar firma con selector nuevo, mantener selector viejo 30 días en grace, eliminar selector viejo. Total: ~45 días de proceso.

El error #4: testing flag (t=y) olvidado

DKIM permite un flag de testing: t=y en el registro TXT. Significa “este DKIM está en modo prueba, no fallar entrega aunque la firma sea inválida”.

2025jan._domainkey.empresa.com  v=DKIM1; k=rsa; t=y; p=...

Útil durante setup inicial. Catastrófico si se queda en producción: el dominio nunca aplica DKIM real, queda vulnerable a DMARC bypass y a degradación de reputación.

En auditorías, encontramos t=y olvidado en aproximadamente el 8% de configuraciones. Siempre verificar tras setup que el registro NO contenga t=y.

Diagnóstico: cómo saber si DKIM está funcionando

Tres métodos confiables:

Método 1: enviar a buzón propio y revisar headers

Mandar un email a un Gmail/Outlook personal. Abrir el email, “View original” o “Show original”. Buscar:

Authentication-Results: mx.google.com;
  dkim=pass [email protected] header.s=2025jan

dkim=pass confirma funcionamiento. dkim=fail o dkim=temperror indica problema.

Método 2: Postmaster Tools

En Postmaster Tools, pestaña Authentication. La gráfica de DKIM debería mostrar 98-99% pass rate consistentemente. Caídas indican problema activo.

Método 3: dmarcian XML reports

Si tenés DMARC configurado con rua=mailto:[email protected], recibís reports XML diarios de Gmail/Yahoo. Estos contienen el detalle por mensaje de qué autenticación pasó. Útil para detectar problemas en flujos específicos.

Configuración avanzada: ed25519

El algoritmo emergente para DKIM es Ed25519 (curva elíptica), que produce firmas mucho más pequeñas que RSA. El registro TXT cabe en una sola línea (vs 2-3 líneas con RSA 2048):

ed._domainkey.empresa.com  v=DKIM1; k=ed25519; p=PHE...

Ventajas:

  • Firmas más rápidas de verificar (menos load en receptores)
  • Records DNS más cortos (menos riesgo de truncamiento)
  • Misma o mejor seguridad que RSA 2048

Limitación actual: no todos los MTAs lo soportan. Google Workspace todavía solo ofrece RSA. PowerMTA y KumoMTA tienen soporte experimental. Para 2025, RSA 2048 sigue siendo la opción operativa estándar.

Tres casos reales de DKIM con problemas

Caso 1: la migración de Google Workspace que no terminó

Empresa que migró de un proveedor de email antiguo (cPanel mail) a Google Workspace hace 3 años. El proveedor anterior usaba selector mail._domainkey. Google Workspace usa google._domainkey.

Durante la migración, alguien publicó el DKIM de Google pero olvidó eliminar el mail._domainkey del proveedor anterior. El SPF se actualizó correctamente. DMARC empezó en p=none.

Resultado: 100% de los emails enviados desde Google Workspace estaban firmados con google._domainkey (correcto). Pero el registro mail._domainkey seguía publicado con la llave del proveedor antiguo.

Síntoma observado 18 meses después: empezaron a llegar reports a [email protected] mostrando spoofing intentos desde IPs externas usando mail._domainkey. Un atacante había detectado que el selector legacy estaba publicado pero no se usaba, y empezó a usarlo para spoofear el dominio.

Diagnóstico: aunque el atacante no tenía la llave privada, podía intentar firmas y para algunos receptores con configuración permisiva pasaba como autenticado. Específicamente, algunos receptores corporativos europeos verificaban DKIM pero no la chain completa de DMARC.

Solución: eliminación inmediata del registro mail._domainkey, monitoreo de reports DMARC durante 60 días para confirmar que el spoofing se detenía.

Lección: los selectores legacy son superficie de ataque. Eliminar al cabo del período de grace, no dejarlos publicados “por si acaso”.

Caso 2: la rotación que rompió Mailchimp

Operación que rotó DKIM correctamente en Google Workspace: generó llave nueva con selector 2024aug, publicó en DNS, esperó 48h, activó firma en Workspace, mantuvo el selector viejo en DNS otros 30 días, eliminó después.

Pero la misma operación también enviaba marketing desde Mailchimp. Mailchimp usa su propio selector (k1._domainkey) y nadie tocó esa configuración. El DKIM de marketing siguió funcionando bien.

Sin embargo, había un tercer proveedor: una aplicación interna que enviaba emails de notificación (alertas, recordatorios) directamente desde un script que firmaba con la llave PRIVADA antigua. Cuando el equipo eliminó el selector antiguo del DNS, los emails de notificación dejaron de validar DKIM (firmados con llave cuya pública ya no estaba publicada).

Síntoma observado: alertas críticas operativas empezaron a llegar a spam. Tardaron 12 días en diagnosticar porque las alertas son raras (10-15/día) y nadie las monitoreaba como deliverability.

Solución: re-publicar selector viejo en DNS, planificar rotación del script interno con grace period, eliminar selector viejo solo después de validar el script con la llave nueva.

Lección: la rotación de DKIM requiere inventario de TODOS los servicios que firman con esa llave. Olvidar uno rompe deliverability silenciosamente.

Caso 3: el DKIM truncado por TXT limit

Empresa que configuró DKIM 2048 bits correctamente. La llave pública en formato base64 tiene ~390 caracteres.

DNS TXT records tienen un límite por string de 255 caracteres. Para records más largos, hay que dividirlos en strings concatenados:

"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEF" "AAOCAQ8AMIIBCgKCAQEAvA8s..."

El equipo de IT publicó el TXT sin dividir, copiando la llave en un solo string. El servidor DNS truncó silenciosamente a 255 caracteres. Resultado: la llave pública publicada estaba incompleta.

Receptor: intentaba verificar firma con llave incompleta, fallaba siempre. DKIM pass rate: 0% en Postmaster Tools.

Síntoma observado: la operación pensaba que DKIM estaba OK porque pasaba syntax checks. Tools como dig mostraban la llave (truncada, pero sintaxis válida). Solo Postmaster Tools mostraba el problema.

Solución: republicar el TXT con la llave dividida en strings de máximo 255 caracteres entre comillas separadas.

Lección: validar DKIM con Postmaster Tools post-publicación, no solo con dig. La sintaxis válida no garantiza que el receptor pueda verificar la firma.

DKIM y BIMI: la relación obligatoria

Para que BIMI funcione (logo verificado en Gmail), DMARC debe estar en p=quarantine o p=reject. Para subir DMARC a p=quarantine, SPF y DKIM deben pasar al 95%+ consistentemente.

DKIM mal configurado es típicamente el cuello de botella para activar BIMI. La operación intenta p=quarantine y empiezan a llegar reports de aggregate mostrando 30% de fails. Bajan DMARC de vuelta a p=none y postergan BIMI indefinidamente.

El orden correcto de implementación:

  1. SPF correcto, validado en MXToolbox
  2. DKIM 2048 bits, sin selectores legacy
  3. DMARC en p=none con rua=mailto:[email protected] durante 2-4 semanas
  4. Analizar aggregate reports: identificar y arreglar todos los flujos que no autentican
  5. Subir DMARC a p=quarantine con pct=10 (afecta 10% de fails)
  6. Aumentar pct gradualmente: 25, 50, 75, 100 a lo largo de 8 semanas
  7. Solo entonces, activar BIMI

Saltar pasos rompe deliverability legítima. Operaciones que activan BIMI sin auditar DKIM previamente terminan con emails legítimos en cuarentena.

Configuración paso a paso: DKIM con KumoMTA

Si operás infraestructura propia con KumoMTA, la configuración DKIM se hace en Lua:

-- Generar llave (una vez, offline):
-- openssl genrsa -out /etc/kumomta/keys/2025jan.pem 2048
-- openssl rsa -in /etc/kumomta/keys/2025jan.pem -pubout -out /etc/kumomta/keys/2025jan.pub

-- Configurar la firma en el handler de mensaje saliente:
kumo.on('smtp_server_message_received', function(msg)
  local from_domain = msg:from_header().domain
  if from_domain == 'empresa.com' then
    msg:dkim_sign({
      domain = 'empresa.com',
      selector = '2025jan',
      headers = {'From', 'To', 'Subject', 'Date', 'Message-ID', 'Reply-To'},
      key = '/etc/kumomta/keys/2025jan.pem',
      canon = 'relaxed/relaxed',
    })
  end
end)

Para rotación, se mantienen dos selectores activos simultáneamente:

-- Durante período de transición (días 1-30 post-rotación):
-- Firmar TODO con el selector nuevo
msg:dkim_sign({
  domain = 'empresa.com',
  selector = '2025jul',  -- nuevo
  key = '/etc/kumomta/keys/2025jul.pem',
  -- ...
})
-- El selector viejo (2025jan) sigue publicado en DNS para que emails 
-- antiguos en colas todavía verifiquen

Tras 30 días sin cola pendiente con la llave vieja, se elimina el registro DNS del selector viejo.

Validador interactivo de DKIM

Diagnóstico rápido de DKIM

Marca los puntos que cumple tu setup actual. El validador identifica gaps.

Marca las casillas que aplican a tu setup.

Resumen operativo

Tres acciones para esta semana:

  1. Auditar el tamaño de la llave DKIM actual. Si es 1024 bits, programar migración a 2048 en las próximas 2 semanas.

  2. Verificar que no haya selectores genéricos (default, mail, key1) que vayan a generar colisiones futuras. Migrar a nombres específicos por proveedor.

  3. Documentar el procedimiento de rotación con calendario anual o semestral según volumen. Si nunca se ha rotado, programar la primera rotación para los próximos 60 días.

DKIM es la base sobre la que DMARC y BIMI funcionan. Una autenticación DKIM débil o desactualizada compromete todas las capas superiores aunque estén bien configuradas individualmente.

Preguntas frecuentes sobre este tema

¿Qué pasa si dejo mi DKIM en 1024 bits? ¿Los emails seguirán llegando?
Por ahora sí, todos los receptores principales aceptan firmas DKIM de 1024 bits. Pero hay tres efectos negativos acumulados: (1) Google Postmaster Tools marca el dominio con advertencia en su pestaña Authentication, lo que aporta señal negativa a la reputación general; (2) auditores de seguridad de empresas grandes (especialmente B2B) marcan el remitente como configuración legacy en sus reportes de vendor risk; (3) NIST y RFC 8301 ya recomendaron oficialmente migrar a 2048 bits desde 2018, lo que significa que la deprecación gradual es inevitable. Migrar a 2048 toma 30-60 minutos y no tiene riesgo operativo si se hace correctamente.
¿Cuántas llaves DKIM debería tener publicadas simultáneamente en mi DNS?
Lo correcto es 2-3 llaves activas simultáneamente, identificadas por selectores diferentes (ej: 2025a._domainkey, 2025b._domainkey, 2024legacy._domainkey). Una sola llave es vulnerable: si se compromete, no hay rotación posible sin downtime. Más de 3 confunde el monitoreo y aumenta el riesgo de operar con llaves olvidadas. La operación típica: selector activo de firma + selector de rotación pendiente + selector legacy en grace period mientras se valida que el cambio no rompió nada. Tras 14-30 días de grace, el legacy se borra.
¿El cliente verá algo si rotamos DKIM mal y las firmas fallan?
Probablemente no verá nada inmediato. Las firmas DKIM inválidas no rebotan el email; el receptor evalúa DMARC, y si DKIM falla pero SPF pasa, el correo se entrega normalmente. El problema es invisible hasta que se acumula: tras 1-2 semanas con DKIM fallando, la reputación del dominio empieza a bajar, lo que se manifiesta en open rates declinantes y eventualmente en envíos a spam. La detección más temprana es revisar Postmaster Tools 24-48 horas después de cambios en DKIM; si la métrica de DKIM pass rate cae por debajo del 95%, hay problema activo.
¿Es seguro publicar DKIM si tengo correo personal en el mismo dominio?
DKIM es completamente seguro de publicar; solo expone la llave pública, no la privada. La llave privada vive solo en el MTA que firma los emails. Cualquiera puede ver tu DKIM público haciendo dig TXT selector._domainkey.dominio.com y no puede usar esa información para suplantar tu dominio. El único riesgo de publicar DKIM mal: usar el mismo selector en múltiples MTAs sin sincronizar las llaves privadas, lo que genera firmas inválidas alternadas según qué MTA envió cada email. Por eso los selectores se nombran por proveedor: pm._domainkey para Postmark, k1._domainkey para Mailchimp, google._domainkey para Google Workspace.
¿Por qué algunos proveedores usan DKIM 'simple' y otros 'relaxed'?
Son canonicalizaciones (formas de normalizar el mensaje antes de firmarlo). 'simple' es estricto: cualquier modificación al header o body del email invalida la firma. 'relaxed' es tolerante: permite cambios menores como espacios extra, line breaks normalizados, conversión de mayúsculas en headers. La sintaxis es 'c=header/body' donde header es la canonicalización del header y body del body. La práctica recomendada actual es 'c=relaxed/relaxed' porque muchos MTAs intermedios modifican mínimamente los emails sin intención maliciosa (ej: ajustar line breaks, normalizar headers), y 'simple' falla con esos cambios benignos. Solo usar 'simple' cuando hay requisito explícito de auditoría que lo exija.

¿Le suena conocido el problema?

Una conversación de quince minutos por WhatsApp. Si el caso del artículo se parece al suyo, le decimos qué corresponde primero, sin presentación de la firma.

Botón flotante de WhatsApp