¿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:
-days 3650→ válido por 10 años-nodes→ sin contraseña en la clave privada (requerido para firmar con JS)CN=*.miempresa.com→ wildcard; si quieres que funcione con absolutamente cualquier dominio, puedes ponerCN=*
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
| Error | Causa | Solución |
|---|---|---|
ant: command not found | ANT_HOME no está en PATH | Reinicia el CMD después de setx |
Cannot find JAVA_HOME | JDK no instalado o mal configurado | Verifica con java -version y revisa variables de entorno |
BUILD FAILED - NSIS | NSIS no instalado o no en PATH | Instala NSIS y agrega su carpeta al PATH |
| Popup sigue apareciendo | cert.pem no coincide con private-key.pem | Regenera el par juntos con el mismo comando |
authcert.override ignorado | Soft reload en vez de restart completo | Exit completo desde la bandeja del sistema |
⚠️ Seguridad: La
private-key.pemnunca debe exponerse públicamente. Mantenla solo en tu servidor de aplicaciones. Elcert.pemsí es público y se distribuye incrustado en QZ Tray.