Solución al error E_ACCESSDENIED al importar certificados en TrustedPublisher con PowerShell Direct

¿Te has encontrado con el temido “UnauthorizedAccessException HRESULT 0x80070005 (E_ACCESSDENIED)” al intentar automatizar la importación de un certificado de firma de código en el almacén TrustedPublisher dentro de una máquina virtual recién instalada? Esto suele ocurrir cuando se usa PowerShell Direct desde el host para configurar imágenes WIM sin intervención manual. A continuación descubrirás por qué sucede, cómo reproducirlo en laboratorio, la causa raíz a nivel de ACL y varias estrategias de mitigación totalmente automáticas.

Índice

Síntomas del problema

Al ejecutar el cmdlet Import‑Certificate sobre la sesión PowerShell Direct conectada a la VM invitada — incluso autenticándote como Administrator — se devuelve la excepción:

Import-Certificate -FilePath ".\adafruit_industries.cer" `
                  -CertStoreLocation "Cert:\LocalMachine\TrustedPublisher"
Salida
Import-Certificate : Access is denied.  (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
  • El mismo comando funciona inmediatamente si inicias sesión gráficamente en la VM y abres certlm.msc.
  • También funciona si cambias el destino a Cert:\LocalMachine\Root (almacén de autoridades raíz).
  • No se observan errores cuando importas el mismo certificado con CertUtil.exe desde dentro de la VM.

Causa raíz: el almacén TrustedPublisher no existe todavía

En una instalación limpia de Windows Server o Windows 10/11, el contenedor TrustedPublisher (Publicadores de confianza en la MMC) no se crea hasta que un componente gráfico lo solicita por primera vez. Esa petición ocurre, por ejemplo, al abrir certlm.msc. En ese momento se genera la clave de registro:

HKLM\SOFTWARE\Microsoft\SystemCertificates\TrustedPublisher

La clave se crea con una DACL que hereda correctamente los permisos de Administrators. Sin embargo, antes de que exista, el intento de Import‑Certificate hace lo siguiente:

  1. Comprueba si el contenedor está presente.
  2. Intenta crearlo con la DACL por defecto del padre (SystemCertificates).
  3. Esa DACL niega la escritura a cuentas administradoras que no sean SYSTEM.
  4. El subsistema de certificados devuelve el código Win32 5 = ERRORACCESSDENIED, que PowerShell encapsula en un UnauthorizedAccessException.

Diagrama de la herencia de permisos

Clave de registroPermisos por defectoResultado
HKLM\SOFTWARE\Microsoft\SystemCertificatesSólo SYSTEM, TrustedInstallerAdministrators no puede crear subclaves
HKLM\...\TrustedPublisher (una vez creado)Heredado + Administrators (WRITE)Importación funciona

Reproducción paso a paso en laboratorio

  1. Instala Windows 10/11 o Windows Server en una VM de Hyper‑V sin iniciar sesión gráfica.
  2. Desde el host, abre PowerShell y ejecuta:
    $s = New-PSSession -VMName MyVM -Credential (Get-Credential) -EnableNetworkAccess Invoke-Command -Session $s -ScriptBlock { Import-Certificate -FilePath "C:\setup\adafruit_industries.cer" ` -CertStoreLocation "Cert:\LocalMachine\TrustedPublisher" }
  3. Observa la excepción E_ACCESSDENIED.
  4. Conéctate a la VM por vmconnect.exe, inicia sesión, abre certlm.msc y cierra la consola.
  5. Repite el paso 2. Esta vez el cmdlet finaliza con éxito.

Estrategias de solución totalmente automáticas

Pre‑crear la clave de registro

La forma más sencilla es generar el contenedor con la ACL correcta antes de importar:

New-Item -Path HKLM:\SOFTWARE\Microsoft\SystemCertificates\TrustedPublisher -Force | Out-Null

Import-Certificate -FilePath '.\adafruit\_industries.cer' \`
-CertStoreLocation 'Cert:\LocalMachine\TrustedPublisher'

Ventajas: mínima dependencia externa, mantiene el flujo de trabajo PowerShell puro.
Desventajas: requiere permisos de escritura sobre la rama SystemCertificates; en entornos muy bloqueados podría fallar.

Usar CertUtil.exe (crea el almacén si falta)

CertUtil emplea la API CRYPT32 que, a diferencia de Import‑Certificate, invoca internamente CertCreateCertificateContext con la opción CERTSTORECREATENEWFLAG. Ello fuerza la creación del contenedor con los permisos apropiados. Ejemplo:

certutil.exe -addstore "TrustedPublisher" ".\adafruit_industries.cer"

Ventajas: no necesita tocar el registro; menor código.
Desventajas: salida menos estructurada; dificulta la captura de errores en scripts complejos.

Ejecutar el bloque de importación como NT AUTHORITY\SYSTEM

El contexto SYSTEM posee SeRestorePrivilege y puede escribir en cualquier contenedor de certificados, exista o no. Con PowerShell Direct puedes invocar comandos como SYSTEM sin habilitar credenciales especiales:

Invoke-Command -VMName MyVM -ScriptBlock {
    Import-Certificate -FilePath "C:\setup\adafruit_industries.cer" `
                       -CertStoreLocation "Cert:\LocalMachine\TrustedPublisher"
} -Credential (New-Object pscredential('NT AUTHORITY\SYSTEM',
              (ConvertTo-SecureString '' -AsPlainText -Force)))

Ventajas: elimina por completo las barreras de ACL.
Desventajas: riesgo de elevar demasiado privilegio; conviene restringir el bloque a tareas puntuales.

Comprobar los atributos del certificado antes de importar

El almacén TrustedPublisher admite exclusivamente certificados cuya Extended Key Usage (EKU) incluya Code Signing (1.3.6.1.5.5.7.3.3). Si el certificado carece de esta EKU, Windows devuelve también E_ACCESSDENIED, dando la falsa impresión de un problema de permisos. Para verificarlo:

# Muestra EKU del certificado
Get-PfxCertificate -FilePath .\adafruit_industries.cer |
  Select-Object -ExpandProperty EnhancedKeyUsageList

Buenas prácticas para pipelines CI/CD

  • Incluye la creación del contenedor en tu módulo de personalización de imágenes antes de instalar software firmado.
  • Firma los ejecutables internos con un certificado válido para Code Signing y renueva antes de que expire.
  • Registra eventos con Write-EventLog cuando la importación falle; facilita la telemetría en entornos híbridos.
  • Ejecuta tests idempotentes: si el certificado ya existe, omite la importación para acelerar despliegues.
  • Documenta la autoridad emisora en tu wiki de plataforma para evitar confusiones cuando cambie el proveedor.

Script completo de referencia

param(
    [Parameter(Mandatory)]
    [string]$VMName,```
[Parameter(Mandatory)]
[string]$CertPath
```
)

function Ensure-TrustedPublisherStore {
if (-not (Test-Path 'HKLM:\SOFTWARE\Microsoft\SystemCertificates\TrustedPublisher')) {
New-Item -Path 'HKLM:\SOFTWARE\Microsoft\SystemCertificates\TrustedPublisher' -Force | Out-Null
}
}

\$session = New-PSSession -VMName \$VMName -Credential (Get-Credential) -EnableNetworkAccess

Invoke-Command -Session \$session -ScriptBlock \${function\:Ensure-TrustedPublisherStore}

Invoke-Command -Session \$session -ScriptBlock {
param(\$CertPathRemote)
Import-Certificate -FilePath \$CertPathRemote `                       -CertStoreLocation 'Cert:\LocalMachine\TrustedPublisher'`
-Verbose
} -ArgumentList (\$CertPath)

Write-Host "Certificado importado correctamente en TrustedPublisher dentro de \$VMName"

Preguntas frecuentes

¿Puedo simplemente copiar el certificado a LocalMachine\Root y olvidarme del problema?

No es recomendable. Root es para autoridades de certificación, no para certificados de firma de código individuales. Colocarlo allí disminuye la seguridad al confiar de forma global en la entidad emisora.

¿PowerShell 7/PowerShell Core eliminan este comportamiento?

No. La API de certificados subyacente pertenece al sistema operativo; el cmdlet Import‑Certificate sigue dependiendo de ella, por lo que la restricción persiste.

¿Qué ocurre en entornos no Hyper‑V, por ejemplo VMware o KVM?

Si la VM arranca por primera vez sin que ningún proceso cree el contenedor TrustedPublisher, la situación es idéntica, independientemente del hipervisor.

Conclusión

El error E_ACCESSDENIED al importar un certificado en TrustedPublisher mediante PowerShell Direct no se debe ni a credenciales insuficientes ni a un fallo de la propia cmdlet. El verdadero problema es que el almacén no existe todavía o se crea con una ACL que bloquea la escritura a administradores estándar. Con una de las soluciones descritas — pre‑crear la clave de registro, usar CertUtil.exe o elevar el script al contexto SYSTEM — podrás mantener tus pipelines de creación de imágenes completamente desatendidos y evitar tediosas sesiones manuales.

Índice