Como firmar apps de Xojo en Windows

A continuación encontrarás traducido al castellano el artículo escrito por Gabriel Ludosanu y publicado originalmente en el Blog oficial de Xojo.

Has compilado una app de Windows, la has comprimido en un Zip y enviado a un usuario; entonces Windows puede sorprenderles con el clásico mensaje de “Windows está protegiendo su PC”. Un binario perfectamente limpio pero no firmado no incluye la identidad sobre quien lo ha publicado, y tampoco ha establecido una reputación “SmartScreen”; de modo que Windows no tiene motivos para confiar en él y ejecutarlo.

A continuación te guiaremos en el proceso completo para firmar los archivos EXE y DLL en Windows, utilizando para ello Azure Artifact Signing.

Este es el resumen, para que puedas entender mejor el proceso:

  • Configuración inicial (pasos 1-9). Cuenta en Azure, una serie de clics en el portal, y validación de la identidad. Esta es la parte lenta. Contempla de un día a una semana para la validación de la identidad.
  • Con cada compilación (paso 10). Se trata de un script encargado de firmar tu EXE y DLLs en segundos. Si ya has realizado la configuración inicial, dirígete al paso 10.

¿Por qué he elegido Azure Artifact para la firma?

  • Esta opción tiene un coste de 9,99 dólares norteamericanos por mes para un total de hasta 5.000 firmas (opción Basic); sin necesidad de tener que utilizar un token hardware basado en USB.
  • Es totalmente gestionado. Microsoft rota los certificados a diario. Nunca verás o tendrás que almacenar la clave privada.
  • Reputación basada en la identidad. El firmado basado en la confianza pública (Public Trust), liga la firma a la identidad validada en vez de hacerlo sobre un certificado que hayas de gestionar por tu cuenta. Aun es posible que aparezcan los avisos de SmartScreen la primera vez o en descargas de bajo volumen. El firmado no garantiza que desaparezcan de inmediato.

Una observación sobre los nombres: hasta hace relativamente poco tiempo, este servicio se denominaba “Azure Trusted Signing”. Es la misma cosa, usando ahora un nuevo nombre. Si ves referencias a “Trusted Signing” en documentación antigua o en capturas de pantalla, se trata del mismo servicio.

Requisitos previos

  • Cuenta de Microsoft (obtendrás una cuando crees una cuenta en Azure).
  • Suscripción de pago en Azure. Este es el principal escollo inicial: Artifact Signing no funciona con suscripciones de prueba, gratuitas o esponsorizadas. Actualiza en primer lugar al pago por uso (Cost Management + Billing > tu suscripción > actualizar) — https://azure.microsoft.com/en-us/products/artifact-signing.

Paso 1. Registra el proveedor de recursos

En primer lugar, una orientación rápida: una suscripción de Azure es el contenedor de facturación para todo lo que crees. Azure carga tus servicios en este, y también define qué recursos están activos y quiénes pueden acceder a ellos. Cuando hayas creado la cuenta, Azure habrá creado automáticamente uno para ti (a menudo con un nombre como “Azure suscription 1”).

Opcionalmente puedes crear otra suscripción. Si quieres una facturación más clara y acceder a los límites para la firma, crea una de tipo dedicado; de lo contrario, utiliza tu actual suscripción de pago y mantén los recursos de firma en un grupo de recursos dedicado.

A continuación:

  • Portal > Subscriptions > la suscripción de tu elección.
  • Menú izquierdo > Settings > Resource providers.
  • Busca Microsoft.CodeSigning > haz clic en … > Registrar. Espera a que finalice el proceso de registro.

Paso 2. Crear la cuenta de Artifact Signing

Mantén juntos los recursos de firma en la misma suscripción y un grupo dedicado de recursos.

  • Portal search > Artifact Signing Accounts > Create.
  • Suscription: la suscripción de pago de tu elección. Grupo de Recursos: por ejemplo rg.artifact-signing.
  • Nombre de cuenta: alfanumérica con una longitud de 3 a 24, letra inicial, unica de forma local (sin usar guiones de forma consecutiva); por ejemplo, xojoappsigning.
  • Region: selecciona una que recuerdes posteriormente. Esta determinará tu punto final de firma.
  • Precio: Basic, excepto que superes las 5.000 firmas por mes.
  • Crear, luego anota el URI de la cuenta en la página mostrada. Esta sigue el patrón https://.codesigning.azure.net/.

Paso 3. Asigna el rol Identity Verifier

Necesitas hacer esto para que el botón identity-validation esté activado.

  • Ve a tu cuenta Artifact Signing, menú izquierdo > control de acceso (IAM) > añadir asignación de rol.
  • Rol: Artifact Signing Identity Verifier (utiliza el campo de búsqueda) > asígnala a tu propia cuenta de usuario.

Paso 4. Validación de Identidad (la puerta real)

  • En tu cuenta Artifact Signing (la creada en el Paso 2), dirígete al menú de la izquierda > Objects > Identity validations > New Identity > Public.
  • Public es lo que quieres para el firmado normal de una app (de lo que se ocupa SmartScreen).
  • Rellena el nombre legal de la empresa, sitio web, direcciones de correo electrónico, identificador de la compañía (CIF, DUNS, VAT/IVA), así como la dirección. Encontrarás dos contratiempos:
  • El email secundario debe encontrarse bajo el mismo dominio que el principal.
  • Desarrolladores individuales: el formulario se toma de la cuenta de facturación de Azure, y su nombre/dirección debe coincidir con el ID.
  • Esto es lo que ocurre luego: el estado pasa de In Progress > Action Required (aquí es donde tendrás que esperar horas o días… en mi caso fue una hora). Serás enviado a un verificador externo (AU10TIX). Aquí tendrás que subir el ID oficial (DNI) y un selfie tomado con tu teléfono; y luego añadir el ID verificado resultante a Microsoft Authenticator y esperar otra hora o así.
  • Línea temporal: oficialmente de 1 a 20 días laborables. En la práctica, de unas horas a unos pocos días. Si falla en algún punto, has de volver a probar con un identificador diferente (tu CIF en vez de DUNS, por ejemplo), y asegurarte de que el nombre de tu compañía y dirección coincida exactamente con los documentos.

Paso 5. Crear el perfil certificado

  • Desde la cuenta Artifact Signing, dirígete al menú izquierdo > Objects > Certificate profiles > Create.
  • Tipo: Public Trus.
  • Asígnale un nombre (de 5 a 100 caracteres); luego, selecciona la validación de identidad ya completada bajo Verified CN y O.
  • Crear. Nunca verás o tendrás el certificado. Se rota por tí de forma transparente. El certificado público se incluye en cada archivo que firmes.

Paso 6. Asignar el rol Certificate Profile Signer

Este es el rol que realmente te permitirá firmar. Dado que estamos usando el CLI de Azure, va bajo tu usuario. No es necesario registrar ninguna app (este es un proceso separado o alternativo, pero no hablaremos de ello en este artículo).

  • Desde tu cuenta Artifact Signing, menú izquierdo > Access control (IAM) > Add role assignment.
  • Rol: Artifact Signing Certificate Profile Signer > asigna tu propia cuenta de usuario.

Permite que pasen unos cuantos minutos para que la asignación de rol se propague. Si intentas firmar inmediatamente y obtienes un error 403… espera unos 5 minutos y vuelve a intentarlo.

Paso 7. Instalar las herramientas de firmado

  • En primer lugar, el CLI de Azure: winget install Microsoft.AzureCLI

Lo usarás en el Paso 9 para autenticarte con Azure.

  • SignTool se incluye con el SDK de Windows. Instálalo con: winget install –id Microsoft.WindowsSDK –exact
  • Ejecuta esto para encontrar el signtool.exe x64 más reciente:
Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin" -Recurse -Filter signtool.exe | Where-Object { $_.FullName -match '\\x64\\signtool\.exe
  • Anota la ruta.
  • .NET 8 runtime (x64).
  • Microsoft Visual C++ Redistributable.
  • Artifact Signing Client Tools:winget install -e –id Microsoft.Azure.ArtifactSigningClientTools
  • Esto instala Azure.CodeSigning.Dlib.dll (la librería que utiliza signtool para comunicarse con Azure).
  • Ubica la ruta real de la dll tras su instalación:
Get-ChildItem -Path "C:\Program Files","$env:LOCALAPPDATA" -Recurse -Filter Azure.CodeSigning.Dlib.dll -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName
  • Anota la ruta.

Si signtool devuelve el error “command not found”, es normal; no se encuentra en PATH. Utiliza la ruta completa o bien añade …\10\\x64 a tu PATH.

Paso 8. Crea el archivo de metadatos

Se trata de un pequeño JSON con tres valores. Asígnale el nombre que desees; por ejemplo artifact-code-signing.json:

{
	"Endpoint": "https://<region-code>.codesigning.azure.net",
	"CodeSigningAccountName": "xojoappsigning",
	"CertificateProfileName": "...."
}
  • Utilizarás con frecuencia este archivo JSON; de modo que guárdalo en una ubicación segura.
  • Endpoint = el URI de tu cuenta (específica a la región; a de concordar con la ubicación de tu cuenta + perfil, de lo contrario obtendrás un error 403).
  • CodeSigningAccountName = el nombre de la cuenta Artifact Signing obtenido en el Paso 2.
  • CertificateProfileName = el nombre del perfil del certificado creado en el Paso 5.

Paso 9. Autenticar con el CLI de Azure

az login

Esto abrirá tu navegador. Accede con la cuenta de Microsoft asociada a tu suscripción de Azure. Cuando se ejecute signtool, dlib usará automáticamente la sesión de Azure CLI en la que estés logado.

En el caso de que tengas múltiples suscripciones de Azure, apunta el CLI a la correcta y confírmala con:

az account set --subscription "<your subscription id>"
az account show --query name -o tsv

El segundo comando debería imprimir el nombre de tu suscripción para la firma de código. Si tienes múltiples cuentas de Microsoft (por ejemplo una cuenta de trabajo y otra personal), conéctate a la correcta:

az login --tenant "<tenant id>"

Ocasionalmente tendrás que volver a ejecutar az login tras largos periodos de inactividad.

Paso 10. Firma tu app Xojo

Recuerda, has de firmar la aplicación compilada tras cada nueva compilación.

Qué firmar (y qué no)

La carpeta de la app compilada por Xojo para Windows tendrá, aproximadamente, el siguiente aspecto. Firma los binarios compilados que vayas a distribuir bajo tu identidad; deja sin firmar las dependencias correspondientes a Xojo, Microsoft u otros proveedores y que ya están firmadas.

Windows 64 bit/
└── TuApp/
    ├── TuApp.exe              ← firma
    ├── <tus propias DLL>          ← firma
    ├── XojoGUIFramework64.dll   ← saltar, Xojo proporciona DLLs firmadas
    ├── TuApp Libs/            ← saltar, Xojo proporciona DLLs firmadas
    ├── msvcp140.dll, vcruntime*.dll …  ← saltar (runtime de Microsoft)
    └── TuApp.exe.WebView2/    ← saltar (cache WebView2)

El Guión

Guarda lo siguiente como sign.ps1, junto a tu proyecto de Xojo. Actualiza las rutas de la parte superior y ejecútalo desde PowerShell:

$ErrorActionPreference = "Stop"
 
$Signtool    = "C:\...\signtool.exe"                            # ruta del Paso 7
$Dlib        = "C:\...\Azure.CodeSigning.Dlib.dll"              # ruta del Paso 7
$Metadata    = "C:\...\artifact-code-signing.json"              # ruta del Paso 8
$BuildFolder = "F:\...\TuApp"                                 # carpeta que contiene TuApp.exe
 
# Instalador (Inno Setup) — opcional. Define $EnableInno a $false para saltar esta parte.
$EnableInno  = $true
$InnoSetup   = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
$IssScript   = "F:\...\TuAppSetup.iss"                        # tu guión de Inno Setup
$Installer   = "F:\...\TuAppSetup.exe"                        # salida de .iss
 
# Firma los binarios que compiles o distribuyas bajo tu identidad.
# Deja sin firmar las dependencias de Xojo, Microsoft, y otros proveedores; pues ya están firmadas.
$FilesToSign = @(
    "$BuildFolder\TuApp.exe"
    "$BuildFolder\MiPlugin.dll"
    # añade el resto de tus archivos, uno por línea
)
 
function Invoke-SigningCommand {
    param([string[]]$Arguments)
 
    & $Signtool @Arguments
    if ($LASTEXITCODE -ne 0) {
        throw "SignTool failed with exit code $LASTEXITCODE."
    }
}
 
foreach ($f in $FilesToSign) {
    Invoke-SigningCommand @(
        "sign", "/v", "/fd", "SHA256",
        "/tr", "http://timestamp.acs.microsoft.com", "/td", "SHA256",
        "/dlib", $Dlib, "/dmdf", $Metadata,
        $f
    )
}
 
if ($EnableInno) {
    # Crea un instalador, y fírmalo.
    & $InnoSetup $IssScript
    if ($LASTEXITCODE -ne 0) {
        throw "Inno Setup failed with exit code $LASTEXITCODE."
    }
 
    Invoke-SigningCommand @(
        "sign", "/v", "/fd", "SHA256",
        "/tr", "http://timestamp.acs.microsoft.com", "/td", "SHA256",
        "/dlib", $Dlib, "/dmdf", $Metadata,
        $Installer
    )
}

El significado de las banderas:

  • /tr http://timestamp.acs.microsoft.com: el servidor de timestamp. El http:// (no https) es intencional.
  • /fd SHA256 y /td SHA256: SHA-256 para el archivo y timestamp.
    /dlib: el plugin de Azure del Paso 7.
    /dmdf: Tu JSON con metadatos del Paso 8.

Confirmar que ha funcionado

Dos comprobaciones:

  • Signtool: $Signtool verify /v /pa “$BuildFolder\TuApp.exe” Deberías de ver “Successfully verified”.
  • O bien: haz clic con el botón derecho en TuApp.exe → Propiedades → Firmas Digitales → Debería de mostrarse el nombre de tu organización.

Las Reglas de Oro

  • Firma tras cada nueva compilación. Cada nuevo .exe es un binario sin firmar.
  • Firma antes de crear el zip. Firma los archivos, y empaquétalos luego (en .zip). Firmar dentro de un zip no funciona.

Recapitulación

  • Sólo una vez (Pasos 1 al 9): registra el proveedor Microsoft.CodeSigning > crea la cuenta > asigna el rol Identity Verifier > completa la validación de identidad > crea el perfil de certificado > asigna el papel Signer > instala las utilidades > escribe el JSON con metadatos > az login.
  • Cada vez (Paso 10): ejecuta sign.ps1

Conclusión

Azure Artifact Signing es una opción gestionada de bajo coste para el firmado de apps Windows, y la autenticación Azure CLI significa que las compilaciones locales no precisan que almacenes una clave privada. La configuración conlleva una parte de burocracia por parte de Azure, y la validación es la parte más lenta. Después de ello, sólo necesitas un script por compilación.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *