Skip to content
MaytokVerso
Atrás

Compilar QZTray de forma correcta en Windows

¿Qué es QZ Tray y por qué compilarlo?

QZ Tray es una aplicación Java que permite imprimir desde el navegador web directamente a impresoras físicas. Por defecto, cada vez que un sitio web intenta imprimir, aparece un popup de autorización. Al compilarlo con tu propio certificado incrustado, ese popup desaparece para cualquier sitio que firme sus mensajes con tu clave privada, sin importar el dominio.


FASE 1 — Instalación de Dependencias

1.1 — Java JDK

QZ Tray 2.2 requiere JDK 11 o superior (se recomienda Liberica OpenJDK 11 HotSpot). QZ Tray 3.0 requiere JDK 21 o superior.

Descarga e instala BellSoft Liberica JDK 11 (recomendado por el proyecto):

https://bell-sw.com/pages/downloads/

Selecciona: Standard JDK, Windows x86 64-bit, formato .msi → instala con doble clic.

1.2 — Apache Ant

Descarga desde:

https://ant.apache.org/bindownload.cgi

Extrae el ZIP en C:\ant\

1.3 — Variables de entorno

Abre Símbolo del sistema como Administrador y ejecuta:

setx JAVA_HOME "C:\Program Files\BellSoft\LibericaJDK-11"
setx ANT_HOME "C:\ant"
setx PATH "%PATH%;%ANT_HOME%\bin;%JAVA_HOME%\bin"

Cierra y vuelve a abrir el CMD. Verifica:

java -version
ant -version

Ambos deben mostrar su versión sin error.

1.4 — NSIS (instalador para Windows)

Instala NSIS 3.0 o superior desde:

https://nsis.sourceforge.net/Download

Instala con opciones por defecto.

1.5 — Git

https://git-scm.com/download/win

Instala con opciones por defecto. Verifica con git --version.

1.6 — OpenSSL (para generar el certificado)

Descarga OpenSSL para Windows:

https://slproweb.com/products/Win32OpenSSL.html

Instala Win64 OpenSSL v3.x.x (versión completa, no “light”). La ruta por defecto será C:\Program Files\OpenSSL-Win64\.

Agrega a PATH:

setx PATH "%PATH%;C:\Program Files\OpenSSL-Win64\bin"

Verifica: openssl version


FASE 2 — Clonar el Código Fuente

cd C:\
git clone https://github.com/qzind/tray
cd tray
git pull

Esto descarga la última versión del código en C:\tray\.


FASE 3 — Generar el Certificado Universal (Wildcard / Cualquier Dominio)

Este es el paso clave. Generarás un par de clave privada + certificado autofirmado. El truco para que funcione con cualquier dominio es usar un Common Name wildcard (* o *.tuempresa.com) y luego incrustar el .crt en la compilación.

3.1 — Crear carpeta de trabajo

mkdir C:\qzcerts
cd C:\qzcerts

3.2 — Generar clave privada y certificado autofirmado

openssl req -x509 -newkey rsa:2048 ^
  -keyout private-key.pem ^
  -out cert.pem ^
  -days 3650 ^
  -nodes ^
  -subj "/C=PE/ST=Lima/L=Lima/O=MiEmpresa/OU=IT/CN=*.miempresa.com"

Explicación de parámetros:

El campo Common Name (CN) es muy importante: debe estar en formato wildcard. Por ejemplo: *.midominio.com. Este comando genera un certificado con vigencia de 30 años si usas -days 11499.

3.3 — Convertir a formato PFX (opcional para .NET)

openssl pkcs12 -inkey private-key.pem -in cert.pem -export -out privateKey.pfx

Se pedirá una contraseña de exportación (puedes dejarla vacía si es para uso interno).

3.4 — Verificar los archivos generados

C:\qzcerts\
├── cert.pem          ← Certificado público (va en la compilación y en el servidor web)
├── private-key.pem   ← Clave privada (NUNCA compartir, va en el servidor web)
└── privateKey.pfx    ← Para entornos .NET (opcional)

FASE 4 — Compilar QZ Tray con el Certificado Incrustado

Para incrustar un certificado personalizado como certificado de confianza interno, usa el parámetro -Dauthcert.use al compilar con Ant.

4.1 — Compilar e incrustar el certificado

cd C:\tray
C:\ant\bin\ant nsis -Dauthcert.use="C:\qzcerts\cert.pem"

Este comando construye el instalador usando Ant con el nuevo archivo de certificado. Asegúrate de modificar las rutas según donde tengas instalado Ant y donde esté el archivo cert.pem. Los nuevos archivos de instalación estarán en la carpeta out del código fuente.

4.2 — Resultado

El instalador estará en:

C:\tray\out\qz-tray-x.x.x.x.exe

4.3 — (Opcional) Compilar para arquitecturas específicas

:: Intel 64-bit
ant nsis -Dtarget.arch=x86_64 -Dauthcert.use="C:\qzcerts\cert.pem"

:: ARM 64-bit
ant nsis -Dtarget.arch=arm64 -Dauthcert.use="C:\qzcerts\cert.pem"

FASE 5 — Instalar QZ Tray en el cliente

Ejecuta el .exe generado en la máquina cliente (o distribúyelo a tus usuarios). Instala normalmente.


FASE 6 — Configuración Post-Instalación (authcert.override)

Si ya tienes QZ Tray instalado y no quieres recompilar, puedes configurar el certificado directamente en el archivo de propiedades:

6.1 — Copiar el certificado al cliente

Coloca cert.pem en: C:\Program Files\QZ Tray\auth\cert.pem

6.2 — Editar el archivo de propiedades

Abre C:\Program Files\QZ Tray\qz-tray.properties con Notepad como Administrador y agrega:

authcert.override=C:\\Program Files\\QZ Tray\\auth\\cert.pem

Configura la propiedad authcert.override en el archivo qz-tray.properties para que apunte al certificado público autofirmado en el sistema de archivos. Después, haz un reinicio completo (no recarga suave) de QZ Tray y ya no recibirás prompts de autorización.

6.3 — Reiniciar QZ Tray completamente

Busca el ícono de QZ Tray en la bandeja del sistema → clic derecho → Exit. Luego vuelve a abrirlo desde el menú de inicio.


FASE 7 — Firmar Mensajes desde tu Servidor Web

Para que el popup no aparezca en ningún dominio, tu aplicación web debe firmar los mensajes con la private-key.pem. QZ Tray verificará la firma contra el cert.pem incrustado.

Ejemplo en PHP

<?php
function signMessage($privateKey, $toSign) {
    $pkeyid = openssl_get_privatekey(file_get_contents($privateKey));
    openssl_sign($toSign, $signature, $pkeyid, "sha512");
    openssl_free_key($pkeyid);
    return base64_encode($signature);
}

$privateKey = '/ruta/a/private-key.pem';
$toSign = time();  // timestamp actual
$signature = signMessage($privateKey, $toSign);
echo json_encode(['timestamp' => $toSign, 'signature' => $signature]);

En JavaScript (frontend)

qz.security.setCertificatePromise(function(resolve, reject) {
    fetch('/ruta/a/digital-certificate.txt')
        .then(r => r.text())
        .then(resolve)
        .catch(reject);
});

qz.security.setSignatureAlgorithm("SHA512");
qz.security.setSignaturePromise(function(toSign) {
    return function(resolve, reject) {
        fetch('/firmar.php?request=' + toSign)
            .then(r => r.text())
            .then(resolve)
            .catch(reject);
    };
});

Resumen del Flujo

[OpenSSL] Generar cert.pem + private-key.pem

[Ant] Compilar QZ Tray con -Dauthcert.use=cert.pem

[Distribuir] Instalar el .exe compilado en clientes

[Servidor web] Firmar mensajes con private-key.pem

[QZ Tray] Verifica firma → sin popup → imprime silenciosamente

Solución de Problemas Comunes

ErrorCausaSolución
ant: command not foundANT_HOME no está en PATHReinicia el CMD después de setx
Cannot find JAVA_HOMEJDK no instalado o mal configuradoVerifica con java -version y revisa variables de entorno
BUILD FAILED - NSISNSIS no instalado o no en PATHInstala NSIS y agrega su carpeta al PATH
Popup sigue apareciendocert.pem no coincide con private-key.pemRegenera el par juntos con el mismo comando
authcert.override ignoradoSoft reload en vez de restart completoExit completo desde la bandeja del sistema

⚠️ Seguridad: La private-key.pem nunca debe exponerse públicamente. Mantenla solo en tu servidor de aplicaciones. El cert.pem sí es público y se distribuye incrustado en QZ Tray.


Comparte este post en:

Next Post
Solucionando el error de API 'You need to sign in or sign up' de Chatwoot en CapRover