Closed tresf closed 8 years ago
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); });
},
});
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)?
@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.
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...
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.
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:
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?
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.
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.
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.
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.
This bug report has been migrated here: https://github.com/qzind/tray/issues/1
This bug report has been migrated here (closed, working): https://github.com/qzind/tray/issues/1
Jump to solution here:
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.