En corto

1
2
PUB=$(openssl rsa -in dkim.key -pubout -outform DER 2>/dev/null | base64 | tr -d '\n')
echo "v=DKIM1; k=rsa; p=$PUB" | fold -w 255 | sed 's/.*/"&"/' | paste -sd' ' -

Sale el valor listo para pegar en el proveedor de DNS, dos cadenas entre comillas separadas por un espacio:

1
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvs7EROOYh35C/1s7M//pIaRvu1XWXzdexgBR2dO7eq1oDw+X08BNJUVUEte3z7os5CNeP7YfZrAqyDJvA59wMK1qul6oHyy7z5P1aB4zNczvO7SZ20HogWjHjzYJ0qwmSUUtjlKvECQzoTCbS/F0gB9rdUZN02MJopDs8YWcohy8oqNLnrflHdxWm6jAb3sSa" "EQReu6iRKKPiXTGUISSetY+iXLo2j/2c5g5ppiIYkUTq58asamMNiCzB7BeF4WNkjz2P+9IJmmtkkJ0M/U4oThUMvBBtQ/XUV4i2N4PGB359G5d/PIMiCj4go2axA4p8ul7Qafbtv0fyPwOIGSojwIDAQAB"

Las dos van en el mismo registro TXT. Quien verifica DKIM las concatena sin espacios. Si el texto te lo dio Google Workspace o Microsoft 365 y no tienes la llave privada, es lo mismo sin el openssl: echo "<valor>" | fold -w 255 | sed 's/.*/"&"/' | paste -sd' ' -.

El problema

Un registro TXT se compone de una o más character-strings y cada una tiene un tope de 255 bytes (RFC 1035). El registro entero puede ser mucho más largo; lo que no puede es tener una sola cadena de 410 caracteres.

Llave RSALargo de p=Con v=DKIM1; k=rsa; p=¿Cabe en una cadena?
1024 bits216234Sí
2048 bits392410No: hacen falta dos
4096 bits736754No: tres

Google Workspace y Microsoft 365 generan llaves de 2048 por defecto, y con razón: 1024 está desaconsejado desde la RFC 8301. Así que hoy casi cualquier DKIM nuevo tropieza con esto, y cada proveedor reacciona distinto:

ProveedorQué pasa con 410 caracteres seguidos
Route 53Rechaza con CharacterStringTooLong, en consola y CLI
Google Cloud DNSRechaza el valor; tienes que escribir tú las cadenas entre comillas
CloudflareLo acepta y lo parte solo en trozos de 255
BIND y la mayoría de servidores propiosEl archivo de zona no carga hasta que uses varias cadenas

La RFC 6376 (§3.6.2.2) obliga a los verificadores a concatenar las cadenas antes de usarlas, así que partir la llave no es un truco: es el formato previsto.

Por proveedor

BIND / archivo de zona

1
2
selector._domainkey  300  IN  TXT  ( "v=DKIM1; k=rsa; p=MIIBIjANBgkq...primera parte"
                                     "segunda parte...IDAQAB" )

Route 53

En la consola, en Value pega las dos cadenas entre comillas separadas por un espacio o un salto de línea. Por CLI el valor es una sola string con las comillas escapadas:

1
2
3
4
5
aws route53 change-resource-record-sets --hosted-zone-id Z0123456789EXAMPLE --change-batch '{
  "Changes": [{"Action": "UPSERT", "ResourceRecordSet": {
    "Name": "selector._domainkey.example.com", "Type": "TXT", "TTL": 300,
    "ResourceRecords": [{"Value": "\"v=DKIM1; k=rsa; p=...primera parte\" \"segunda parte...\""}]
  }}]}'

Google Cloud DNS

En la consola, mismo formato en el campo de datos. Con gcloud:

1
2
3
gcloud dns record-sets create selector._domainkey.example.com. --zone=example-zone \
  --type=TXT --ttl=300 \
  --rrdatas='"v=DKIM1; k=rsa; p=...primera parte" "segunda parte..."'

Con Terraform (google_dns_record_set), un solo elemento en rrdatas con las comillas escapadas:

1
rrdatas = ["\"v=DKIM1; k=rsa; p=...primera parte\" \"segunda parte...\""]

Cloudflare

Pega el valor completo en Content; el panel lo parte al guardar. Al consultarlo con dig verás dos cadenas aunque tú metiste una: es normal.

Verificar

1
dig +short TXT selector._domainkey.example.com

Lo esperado es "v=DKIM1; k=rsa; p=MIIB..." "...IDAQAB": dos cadenas, en un solo registro. Después comprueba que la llave decodifica a 2048 bits, lo que confirma que no se perdió ningún carácter en el corte:

1
2
3
4
dig +short TXT selector._domainkey.example.com \
  | tr -d '" \n' | sed -E 's/.*p=([A-Za-z0-9+/=]+).*/\1/' \
  | base64 -d | openssl pkey -pubin -inform DER -noout -text | head -1
# Public-Key: (2048 bit)

Si base64 protesta, se coló un espacio dentro de la llave o faltan caracteres. Cierra con un correo de prueba y Authentication-Results: ... dkim=pass en la cabecera; para mirarlo con más detalle, el DKIM Inspector de dmarcian.

Trampas

  • El corte puede ir en cualquier sitio, incluso a media base64, pero sin meter espacios dentro de la llave. El espacio entre las dos cadenas separa cadenas, no forma parte del dato.
  • No dupliques comillas. Si el panel ya las pone por ti y pegas "...", dig devuelve "\"v=DKIM1...\"" y el verificador falla.
  • Un registro, no dos. Dos TXT distintos con el mismo nombre son dos valores independientes, y ninguno es una llave válida.
  • 2048 es el punto medio. 4096 son tres cadenas y una respuesta DNS que se acerca al límite UDP; funciona, pero no gana nada para DKIM.
  • Rotación: publica la llave nueva con otro selector, cambia el firmante, y borra el selector viejo después del TTL. Nunca edites el selector en uso.
  • SPF tiene el mismo límite, pero un SPF que no cabe en 255 suele ser señal de demasiados include:; ahí la solución es acortarlo, no partirlo.

Referencias: Google Workspace — problemas con DKIM, AWS re:Post — CharacterStringTooLong y el DNS record splitter de Mailhardener si prefieres no usar fold.