Skip to content
Kyle La Barge edited this page Jan 24, 2020 · 2 revisions

Compatibility

  • ✅ 2.1 | ✅ 2.0 | ⛔ 1.9 | ...

Objective

  • Use a Trusted Certificate (digital-certificate.txt) and a private key (private-key.pem) to achieve silent printing*.

Prerequisites

Component Format Description
private-key.pem PKCS#8 🔑 Private key used for signing
private-key.pfx PKCS#12 🔑 Optionally replaces .pem file for .NET environments
digital-certificate.txt x509 🔒 Trusted certificate*
  • A language capable of reading an RSA private key and generating an SHA1 base64 encoded signature hash.
  • Signing examples are available here: assets/signing

Contents

Steps

  1. Generate a Trusted Certificate (digital-certificate.txt).
  2. Edit qz.security.setCertificatePromise() to use your Trusted Certificate.
  3. Edit qz.security.setSignaturePromise() to use your server-side signing method.
  4. Edit sign-message.php to sign print requests with your private key.
    Examples in other languages can be find in demo/assets/signing of QZ Tray.

Loading the Trusted Certificate

A sample certificate chain is provided with the demo, labeled as "localhost". This will display a trusted message on load of the page.

  1. Edit the setCertificatePromise() provided in the sample.html file.

  2. Replace the "localhost" certificate chain with your Trusted Certificate by changing the $.ajax(...) line to match the address of the certificate.

    The Trusted Certificate generated by QZ Industries, LLC is digital-certificate.txt

    qz.security.setCertificatePromise(function(resolve, reject) {
        $.ajax({ url: "path/to/digital-certificate.txt", cache: false, dataType: "text" }).then(resolve, reject);
    });

    Note: $.ajax is a jQuery function and can be replaced with any ajax library of choice (xhr, PageMethods, etc).

Supplying the Signature

The function setSignaturePromise() is used for proving a JavaScript callback method for signing each privileged method to prevent anonymous printing. This is a security measure to ensure the identity of websites can be verified by the software.

These methods are invoked on any privileged function, which includes finding attached devices, printing, or sending/reading data from a USB or serial device.

GET Method:

Change the $.ajax(...) line to match the address of your php file.

qz.security.setSignaturePromise(function(toSign) {
   return function(resolve, reject) {
      $.ajax("assets/signing/sign-message.php?request=" + toSign).then(resolve, reject);
   };
});

POST Method:

qz.security.setSignaturePromise(function(toSign) {
   return function(resolve, reject) {
      $.post("assets/signing/sign-message.php", {request: toSign}).then(resolve, reject);
   };
});

.NET PageMethods

.NET conveniently exposes a promise-friendly PageMethods object which is capable of calling a server-side .NET function by name directly from JavaScript. This assumes a server-side signing example has already been setup in C#, VB.NET, etc.

qz.security.setSignaturePromise(function (toSign) {
   return function (resolve, reject) {
      PageMethods.SignMessage(toSign, resolve, reject); 
   };
});

Server-side Signing Method

A server-side signing method must be used in combination with the AJAX call. This signing will happen with your company's private key.

Trusted websites with a valid public key chain pair and a properly configured qz.security.setSignaturePromise AJAX function will unlock QZ Tray's ability to communicate silently (print, read serial ports, usb devices, etc). Non-signed requests (Untrusted websites) will continue to show a warning dialog.

In this example we go over how to accomplish this in php by editing the sign-message.php file that is provided with the software (demo/assets/signing/sign-message.php). Examples in other languages including: Ruby, Python, JavaScript, C#, J#, Java, ASP and VB can be found here.

  1. Change the line $KEY = 'private-key.pem'; to match the name of your private key

  2. Edit the line: $req = $_GET['request']; appropriately to edit the method you are using in the AJAX call (GET or POST)

    <?php
    // #########################################################
    // #                     PHP Signing                       #
    // #########################################################
    // Sample key.  Replace with one used for CSR generation
    $KEY = 'private-key.pem';
    
    $req = $_GET['request']; //GET method
    //$req = $_POST['request']; //POST method
    
    $privateKey = openssl_get_privatekey(file_get_contents($KEY));
    
    $signature = null;
    openssl_sign($req, $signature, $privateKey);
    
    if ($signature) {
       header("Content-type: text/plain");
       echo base64_encode($signature);
       exit(0);
    }
    
    echo '<h1>Error signing message</h1>';
    exit(1);
    ?>
  3. If these changes have been done correctly, you will be able to suppress this dialog box:

    image

    This will no longer come from an untrusted source.

    image

    The new certificate should look similar to this (trusted, valid, fingerprint, and contains your information):

    image


Troubleshooting

Invalid JSON

  • If the browser console says Invalid JSON, try changing your HTTP headers in the sign-message example to use text/plain instead of application/json. In PHP, this is done via:

    - header('Content-Type: application/json');
    + header('Content-Type: text/plain');

    Note: This technique will vary depending on the server-side language being used.

Client-side Signing

Key is too short

The console error: key is too short for SigAlg: keylen=99,sha1 is a result of an incompatible private key with the JavaScript signing library, jsrsasign, used in client-side signing. The private-key.pem downloaded from our website needs to be converted using the below openssl command:

openssl rsa -in private-key.pem -out private-key-updated.pem
RSA Key is not defined

The console error: ReferenceError: RSAKey is not defined generally indicates that the jsrsasign-all-min.js URL has changed. We recommend that you download this file to prevent future updates causing issues.

Advanced

To override the Trusted Root certificate:

  • Launch QZ Tray using manually specified certificate (since 1.9.7)

    java -Xms512M -DtrustedRootCert=override.crt -jar qz-tray.jar
  • Or alternately via qz-tray.properties (since 2.0.2)

    authcert.override=override.crt

Clone this wiki locally