¿Intentas emitir un certificado desde una Autoridad de Certificación de Microsoft y solo aparece la primera entrada SAN? Este artículo explica por qué ocurre cuando usas certreq ‑attrib
y cómo lograr que todas las entradas queden firmadas correctamente.
Resumen del problema
Cuando el comando
certreq -submit -attrib "CertificateTemplate:Appliance
SAN:dns=entry1.contoso.com&dns=entry2.contoso.com&dns=entry3.contoso.com"
llega a la CA, solo entry1.contoso.com se incluye en la extensión Subject Alternative Name (SAN); las demás entradas se pierden. La causa no es la plantilla ni la CA, sino el modo en que certreq
serializa el parámetro -attrib
.
Diagnóstico detallado
Limitación de -attrib
certreq.exe
concatena los atributos recibidos con un separador interno que la CA interpreta como un único valor SAN. En cuanto encuentra el primer “&
”, deja de procesar sub‑valores. Esta limitación no está documentada de forma oficial, pero puede reproducirse desde Windows Server 2008 R2 hasta Windows Server 2025.
Interacción con la plantilla
- La plantilla “Appliance” permite la opción Supply in the request, de modo que la CA confía en que la solicitud ya traiga las SAN correctamente formadas.
- Si esas SAN viajan truncadas, la CA no las reconstituye; simplemente firma lo que recibe.
CSR original generado por el dispositivo
En appliances o load‑balancers es habitual generar un CSR mínimo, sin plantilla ni SAN. Después se intenta “inyectar” esos valores vía -attrib
, encontrando la limitación descrita.
Soluciones y alternativas
Opción | Pasos | Ventajas | Desventajas |
---|---|---|---|
Incluir SAN en un INF + CSR local | 1. Crear un archivo INF con toda la información. 2. Generar el CSR con certreq -new .3. Enviar el CSR con certreq -submit (sin usar -attrib ). | Fiable; control total sobre las extensiones. | Obliga a crear la clave privada localmente y luego exportarla para el appliance. |
Reconfigurar la plantilla y usar la GUI de la CA | 1. Verificar que la plantilla admita Supply in the request. 2. Solicitar desde la consola MMC o la interfaz web e introducir las SAN manualmente. | Rápido en escenarios puntuales. | No es automatizable; propenso a errores. |
Script PowerShell con X509Enrollment COM | 1. Construir un objeto IX509CertificateRequestPkcs10 .2. Agregar múltiples SAN mediante IX509ExtensionAlternativeNames .3. Enviar la solicitud con IX509Enrollment . | Completamente automatizable y sin la limitación de -attrib . | Requiere conocimientos avanzados de la API COM. |
Solución aplicada en la práctica: se eligió la primera ruta: generar un CSR local mediante INF, obtener el certificado válido y convertir el PFX resultante a PEM/KEY/CHAIN para importarlo en el dispositivo.
Implementación paso a paso con archivo INF
Crear el archivo INF
; example.inf
[Version]
Signature="$Windows NT$"
\[NewRequest]
Subject = "CN=entry1.contoso.com"
RequestType = PKCS10
KeyLength = 2048
CertificateTemplate = Appliance
\[Extensions]
2.5.29.17 = "{text}"
continue = "dns=entry1.contoso.com&"
continue = "dns=entry2.contoso.com&"
continue = "dns=entry3.contoso.com"
Generar el CSR
certreq -new example.inf example.csr
Enviar el CSR a la CA
certreq -submit example.csr
Convertir e importar
Una vez obtenido el PFX, convierte a PEM y cadena intermedia:
openssl pkcs12 -in cert.pfx -out cert.pem -nodes
openssl x509 -in cert.pem -text -noout
Implementación con PowerShell y X509Enrollment COM
Para automatizar entornos de CI/CD o grandes lotes de dispositivos, el siguiente fragmento demuestra la creación de un CSR con múltiples SAN directamente en PowerShell 5.1 +:
$dnsList = @(
"entry1.contoso.com",
"entry2.contoso.com",
"entry3.contoso.com"
)
$req = New-Object -ComObject X509Enrollment.CX509CertificateRequestPkcs10
$req.InitializeFromScratch()
Clave RSA
$privateKey = New-Object -ComObject X509Enrollment.CX509PrivateKey
$privateKey.Length = 2048
$privateKey.ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
$privateKey.KeySpec = 1
$privateKey.MachineContext = $true
$privateKey.Create()
$req.PrivateKey = $privateKey
Asignar plantilla
$req.CertificateTemplate = "Appliance"
Nombre común
$name = New-Object -ComObject X509Enrollment.CX500DistinguishedName
$name.Encode("CN=entry1.contoso.com", 0)
$req.Subject = $name
Extensión SAN
$san = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
foreach ($dns in $dnsList) {
$altName = New-Object -ComObject X509Enrollment.CX509AlternativeName
$altName.InitializeFromString(2, $dns) # 2 = <ALTNAMEDNS_NAME>
$san.AlternativeNames.Add($altName)
}
$req.X509Extensions.Add($san)
Crear CSR
$csr = $req.Encode()
[System.IO.File]::WriteAllText("request.csr", $csr)
Enviar a la CA
$enrollment = New-Object -ComObject X509Enrollment.CX509Enrollment
$enrollment.InitializeFromRequest($req)
$enrollment.Submit(0, "request.csr", $null, $null)
De esta manera la CA recibe un CSR bien formado, con todas las SAN ya incorporadas.
Validación del certificado emitido
Antes de importar el certificado en producción, valida que la extensión SAN contenga cada FQDN:
certutil -dump issued.cer | findstr /I "DNS Name"
o bien
openssl x509 -in issued.cer -noout -text | grep "DNS:"
Buenas prácticas para plantillas de certificados
- Principio de mínimo privilegio: habilita Supply in the request solo cuando sea imprescindible.
- Duplicar plantillas integradas: nunca edites la plantilla “Web Server” original; crea un duplicado y ajusta permisos.
- Permisos específicos: asigna Enroll/Auto‑Enroll solo a los grupos que realmente emitan certificados.
- Periodo de validez coherente: evita que la fecha de expiración supere el plazo de la CA emisora.
- Auditoría: activa el registro de eventos 4886/4887 para detectar atributos modificados en solicitudes.
Preguntas frecuentes
¿Puedo añadir SAN comodín (*.contoso.com)?
Sí, siempre que la política interna lo permita y la plantilla no restrinja comodines. Solo inclúyelo tal cual en la lista dns=*.contoso.com
.
¿Funciona igual con certificados de usuario (UPN, email)?
No exactamente. Los UPN y los correos se codifican en la misma extensión SAN, pero como otherName y rfc822Name respectivamente. El conmutador -attrib
presenta la misma limitación: solo se conserva la primera entrada de cada tipo.
¿Qué versiones de Windows están afectadas?
Desde Windows Server 2008 R2 hasta Windows Server 2025 Insider Preview. No existe parche ni plan público para modificar certreq.exe
.
Conclusión
-attrib
es útil para añadir una única SAN, pero deja de ser fiable con varias. La forma segura de trabajar es codificar todas las SAN directamente en el CSR—ya sea a través de un archivo INF, de PowerShell X509Enrollment o de herramientas como OpenSSL—o bien usar la interfaz GUI antes de que la CA firme el certificado. De este modo garantizas que cada nombre DNS quede protegido por la firma y evitas reprocesar solicitudes fallidas.