Skip to content
This repository has been archived by the owner on Dec 10, 2018. It is now read-only.

Client-side Signing #99

Closed
tresf opened this issue Oct 19, 2015 · 10 comments
Closed

Client-side Signing #99

tresf opened this issue Oct 19, 2015 · 10 comments

Comments

@tresf
Copy link
Contributor

tresf commented Oct 19, 2015

This bug report has been migrated here (closed, working):
qzind/tray#1

Jump to solution here:

Added to examples here:
https://github.com/qzind/tray/blob/2.0/assets/signing/sign-message.js


Provide a code example for client-side signing:

Our guide offers many server-side signing examples:

But no client-side examples. The only method I'm aware of is through JavaScript which has the security risk of exposing the private key to all the users.

Our Node.js example sign-message.js is a good place to start.

@tresf
Copy link
Contributor Author

tresf commented Dec 4, 2015

Still struggling with this... should be relatively simple... code so far...

$.ajax({
        method: 'GET',
        url: "private-key.pem",
        async: false,
        dataType: "text",
        success: function(data) {
            // Strip non-base64 data
            data = data.replace(/-----BEGIN PRIVATE KEY-----|-----END PRIVATE KEY-----|\r|\n/g, '');
            alert(data);
            crypto.subtle.importKey(
                "jwk", 
                {
                    kty: "RSA",
                    e: "AQAB",
                    n: data,
                    alg: "RS256",
                    ext: false
                },
                { 
                    name: "RSASSA-PKCS1-v1_5",
                    hash: {name: "SHA-256"}
                },
                false,
                ["sign"]        
            ).then(function(privateKey) {
                console.log(privateKey);
                crypto.subtle.sign(
                    {
                        name: "RSASSA-PKCS1-v1_5",
                    },
                    privateKey,
                    "DATA-TO-SIGN"
                ).then(function(signature){
                    console.log(new Uint8Array(signature));
                }).catch(function(err){
                    console.error(err)
                });

            }).catch(function(err) { console.error(err); });
        },
    });

@emerinohdz
Copy link

As pointed out, signing client side is a security concern, because the private key will be available, I believe this breaks the whole security model implemented in qz-tray.

But taking a deeper look at this, anyone could actually request to sign the print request to the server and bypass the whole thing...

I guess the right question is: what is the purpose of signing print requests? It seems it is to avoid 3rd parties to simply send print jobs without the end user noticing (that's why the alert is shown), but what if the 3rd party has access to the client computer? I believe they could do as they please, and start sending signed print requests... can anyone please explain this (or point me to a link or source code where I can see an explanation)?

@emerinohdz
Copy link

@tresf I think you can get away with the ajax call... I'm gonna give this a try later today and see if I can provide the code for this issue.

@tresf
Copy link
Contributor Author

tresf commented Dec 4, 2015

I guess the right question is: what is the purpose of signing print requests?

To mandate a level of verification by the person talking to hardware.

But taking a deeper look at this, anyone could actually request to sign the print request to the server and bypass the whole thing...

In a server-side-signing example (the only method we currently support), the onus is on the service provider to protect the signing logic from unauthorized access.

As pointed out, signing client side is a security concern, because the private key will be available, I believe this breaks the whole security model implemented in qz-tray.

Well... first for the business need...

Next... how to do it without compromising the private key... We've had some internal conversations about this...

  • One option is to mitigate leakage via a fast-expiring certificates to protect it from re-use (i.e. 1-day). Requires the offline client to have a long array of private keys and short-lived certificates to serve up to clients.
  • A second albeit new and undocumented (6970815) method, is to allow the clients to provide their own root certificate. This would mitigate the damage of the private key being lost, as the single client which created the key would be the only client affected when the key gets compromised.

Keep in mind that most clients are small shops which already have their software behind an authentication portal, so the risk measurement would differ based on how long the key is valid, how accessible the key is combined with each client's specific exposures.

@emerinohdz
Copy link

Once a 3rd party gets access to a client (e.g. client gets compromised), I believe this whole model will get compromised, no matter what we do to try to mitigate this (we can only make it 'least bad'), let me explain.

If a 3rd party has access to the client, he'll be able to:

  • Send an authenticated request to the server to request signing. This is bad, because he already knows one authentication token, so he could reuse it to ask the server to sign a request for a different client.
  • If the certificates are short-lived, it won't matter, he'll be able to use the one that is valid. If he gets one of these certs, he can reuse it to send signed petitions to other clients' qz-tray servers.

For the second method you point out (clients provide their own root certificate), how is the qz-tray desktop app going to validate signed requests? I'm understanding it like this: there will be one intermediate cert for each client, which means we'll have to provide a specific cert for each client when distributing (which may be a problem depending on use case, perhaps generating a new JAR on-the-fly with the right cert?), that way if the cert of this client gets compromised, it'll affect this client only.

I believe the second method might be the safest of them all, if an attacker gets access to the certs, he'll be able to harm this client's qz-tray server only.

Please, don't get me wrong, I'm trying to understand (and analyze) the whole security model for qz-tray. I think sticking to one solution would be the best approach (e.g. getting rid of the whole server-signs-requests method and make all clients use the "second" method instead). What do you think?

@tresf
Copy link
Contributor Author

tresf commented Dec 5, 2015

For the second method you point out (clients provide their own root certificate), how is the qz-tray desktop app going to validate signed requests?

It's not plug-and-play, but it doesn't require recompiling. It's a command-line option which would have to be provided to the shortcut on each workstation. It's in the commit message of the aforementioned commit and it will not be available until 1.9.5.

that way if the cert of this client gets compromised, it'll affect this client only.

Correct, minus the intermediate stuff. If they shim the launcher, they'd be using their own cert as the only trusted root, so whether or not they choose to use an intermediate cert is entirely up to how they roll out their PKI.

@emerinohdz
Copy link

I see, I hope this gets revisited for (maybe?) v2. In the meantime, I'm getting back an epson printer later today, I'll see if I can get this working client side.

@tresf
Copy link
Contributor Author

tresf commented Dec 5, 2015

e.g. getting rid of the whole server-signs-requests method and make all clients use the "second" method instead. What do you think?

Yes, I think overriding the trusted root may be the best solution for these client-side-signing edge-cases. They'll still need client-side signing as well as a potential install-time option to auto-install the trusted root override.

In regards to avoid having the server do client-side signing, we're open to ideas. 2.0 will be done and ready for testing in a couple more weeks, so if this is going to change, it needs to be done now.

@augugiacoEnta
Copy link

Hi everyone,

If you need sign messages on the client-side, you can take this approach:

var publicKey = "your public key";

 qz.security.setCertificatePromise(function (resolve, reject) {
            //$.ajax("/assets/secure/qz/digital-certificate.txt").then(resolve, reject);
            resolve(publicKey);
        });
qz.security.setSignaturePromise(function (toSign) {
            return function (resolve, reject) {

        var privateKey = "your private key";
                var certificate509 = new X509();
                certificate509.readCertPEM(publicKey);
                var sha1Encryptator = certificate509.subjectPublicKeyRSA;
                sha1Encryptator.readPrivateKeyFromPEMString(privateKey);
                var messageSignedHex = sha1Encryptator.signString(toSign, 'sha1');
                var messageSignedStr = hextorstr(messageSignedHex);
                var finallyMsgSignedBase64 = stob64(messageSignedStr);

                resolve(finallyMsgSignedBase64);

            };
        }); 

I used an external library called jsrsasign
Take a look here: http://kjur.github.io/jsrsasign/

It worked fine for me and my app is more fast that before.

I thought this info would be util for you.

Regards.

@tresf
Copy link
Contributor Author

tresf commented Mar 15, 2016

This bug report has been migrated here:
qzind/tray#1

@tresf tresf closed this as completed Mar 15, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants