1

I try to sign messages in javascript before sending to a PHP application. The PHP application must check the signature to be sure it's not false.

In javascript I use cryptico.js.

This is the js function for signing messages

var sign = function(passphrase, text) {
    signingkey = cryptico.generateRSAKey(passphrase, 2048);
    signString = cryptico.b16to64(signingkey.signString(text, "sha256"));
    return signString;
}

This is the function for getting the public key:

var getPublicKey = function(passphrase) {
 var rsaKey = cryptico.generateRSAKey(passphrase, 2048);
 return = cryptico.publicKeyString(rsaKey);
}

For example, for the message "message" and the passphrase "test2" the public key and signature are

qH/J3/gvF/h5U02uPyC9Qzn/hHEV5DzB9nFfqk5zbQqHdInVe4sfL+npa+4fjLGrBU30Iuvcr+o9paEjzpH5dY48cq6JHqz1RyJ0CQIc2Jr5+sS4eL1ZIjxWlyN1pKMR+4aE2rlDAad56Ad1cytiaHuVvyK/gdtbKiuGroSQhJ1EVfZ60m3NIqnqmpi5Zdsnmzny4VH/d66BcGXxGaGaUaqFn0WTypuwIMZMMtzZEK7peKoaW4H4rfkfdrKcD8AaT+z9v5lLGkTl0NcZZ4LN9sSUzsHNfyAFK6cSXo/73z0tDAlGb5K+yWV6UHoYW1rcoIsxlNRZM6/6FYgMXbbfow==

XbF4O6v6oadEQGtdpQ7d54Q2JB9/ZEXEUH3S1FMn4E/PSqk7HLXjG4tNfuiUBa5eS8kYV49gwC8Yr+mn6YUAHt+K9lHPSsmltWoiHNOaas4aqai9nlyeft4TYYhP+GYbQfw+3n2TcO39s6M0vw0m0a8AX9JfF02JwCUhP4bu4dzG6Bl5dj000TbUkric14Jyurp8OHmmMvKW62TvXPhNOW39+wS1Qkfn9Bxmzi8UEVSVe3wP45JWZNgmgeGnpubDhD05FJEDErfVtZ/DRKD81q5YRd4X4cCkeDPDcJLgKW1jkCsA7yBqESXPDSkkrVUM06A9qMFUwk4mRI88fZ8ryQ==

I'm asking me how to verify it in php?

I tryed something like:

$rsa = new Crypt_RSA();
$rsa->loadKey('qH/J3/gvF/h5U02uPyC9Qzn/hHEV5DzB9nFfqk5zbQqHdInVe4sfL+npa+4fjLGrBU30Iuvcr+o9paEjzpH5dY48cq6JHqz1RyJ0CQIc2Jr5+sS4eL1ZIjxWlyN1pKMR+4aE2rlDAad56Ad1cytiaHuVvyK/gdtbKiuGroSQhJ1EVfZ60m3NIqnqmpi5Zdsnmzny4VH/d66BcGXxGaGaUaqFn0WTypuwIMZMMtzZEK7peKoaW4H4rfkfdrKcD8AaT+z9v5lLGkTl0NcZZ4LN9sSUzsHNfyAFK6cSXo/73z0tDAlGb5K+yWV6UHoYW1rcoIsxlNRZM6/6FYgMXbbfow=='); // public key
echo $rsa->verify('message', 'XbF4O6v6oadEQGtdpQ7d54Q2JB9/ZEXEUH3S1FMn4E/PSqk7HLXjG4tNfuiUBa5eS8kYV49gwC8Yr+mn6YUAHt+K9lHPSsmltWoiHNOaas4aqai9nlyeft4TYYhP+GYbQfw+3n2TcO39s6M0vw0m0a8AX9JfF02JwCUhP4bu4dzG6Bl5dj000TbUkric14Jyurp8OHmmMvKW62TvXPhNOW39+wS1Qkfn9Bxmzi8UEVSVe3wP45JWZNgmgeGnpubDhD05FJEDErfVtZ/DRKD81q5YRd4X4cCkeDPDcJLgKW1jkCsA7yBqESXPDSkkrVUM06A9qMFUwk4mRI88fZ8ryQ==') ? 'verified' : 'unverified';

I think the signature and/or public key are not formated correctly for php. Any idea?

Thank you in advance,

[EDIT] I'm not sure the signature is correct. If I use the js function cryptico.b64to16(signature), the signature will be somethink like :

5db1783babfaa1a744406b5da50edde78436241f7f6445c4507dd2d45327e04fcf4aa93b1cb5e31b8b4d7ee89405ae5e4bc918578f60c02f18afe9a7e985001edf8af651cf4ac9a5b56a221cd39a6ace1aa9a8bd9e5c9e7ede1361884ff8661b41fc3ede7d9370edfdb3a334bf0d26d1af005fd25f174d89c025213f86eee1dcc6e81979763d34d136d492b89cd78272baba7c3879a632f296eb64ef5cf84d396dfdfb04b54247e7f41c66ce2f141154957b7c0fe3925664d82681e1a7a6e6c3843d3914910312b7d5b59fc344a0fcd6ae5845de17e1c0a47833c37092e0296d63902b00ef206a1125cf0d2924ad550cd3a03da8c154c24e26448f3c7d9f2bc9

I am not sure about the format of the param key of $rsa->verify. I tryed to add the prefix ssh-rsa. But it do not works better.

So I tryed the to signature format and the to key. The message is each time "unverified"

neubert
  • 15,947
  • 24
  • 120
  • 212
Madef
  • 619
  • 5
  • 9
  • You know that on each invocation of `cryptico.generateRSAKey(passphrase, 2048)`, a (hopefully) **different** RSA key will be produced, right? – Stefano Sanfilippo May 29 '14 at 10:31
  • It's seams that each time it the same. I use three JS script/page to test the lib in JS. One to generate public key, an other to sign and an other one to verify. For the first I only give the passphrase, for the second the passphrase and the message and the third the public key, the message and the signature. – Madef May 29 '14 at 11:19
  • If a RSA key generation function (routine) always returns the same key, well, don't use that! Unless you are testing the PHP script and you absolutely don't care about the JS part, I strongly advice changing library. RSA keys must be as random as possible. – Stefano Sanfilippo May 29 '14 at 11:54
  • Maybe IK made a mistake, or crytico is not a good lib. `cryptico.generateRSAKey('test2', 2048).signString('message', "sha256");` Is each time the same signature. Idem for the public key: `cryptico.publicKeyString(cryptico.generateRSAKey('test2', 2048));` – Madef May 29 '14 at 12:07
  • The same message generating the same signature is ok in RSA. More, it's intended to be always the same. What is not ok is that you get the same public key (thus, the same private key too) on every call to `cryptico.publicKeyString(cryptico.generateRSAKey('test2', 2048));`. I cannot vouch for cryptico (I have never used it), but this is certainly a red flag. – Stefano Sanfilippo May 29 '14 at 12:10
  • Do you have any other JS lib to recommend me? – Madef May 29 '14 at 12:13
  • No, I am sorry. Also, since you did not notice the RSA issue by yourself, I can deduce that you have little or no experience in writing crypto/security code. I am not implying that you are bad programmer, you might be very skilled for what I know, but you should leave crypto code to people who know very well what they are doing. While an error in "ordinary" code _might_ result in a security bug, an error in crypto code will _certainly_ result in a severe security issue. – Stefano Sanfilippo May 29 '14 at 13:22
  • 1
    Also, by reading some lines of Cryptico code, I can deduce as well that the guy who wrote it did not know very well what she was doing... – Stefano Sanfilippo May 29 '14 at 13:29
  • cryptico doesn't store keys in any sort of standard format near as I can tell: http://stackoverflow.com/a/15643041/569976 phpseclib - the RSA library you're using - supports standardized signature schemes - PKCS1 and PSS. Given that cryptico's key format is custom it seems likely that their signature format is as well. – neubert May 29 '14 at 13:39
  • Stefano Sanfilippo> You're absolutely right. I try to understand how signatures work. This code is only done for local tests. So it's not very important if there is a security hole in my code because it's made for learning and will never be used in production (but of course it's better if there is no safety issues). One thing I do not understand. If the public key changes each time "Sam" sent a message. How Matt can be sure the message is form Sam and not from an other? – Madef May 29 '14 at 14:29
  • In fact, the key should be generated once. You keep the private key in a safe place and distribute the public. Now, one can encrypt messages using its recipient private key, send it and have the recipient decrypt them using his/her private key. So anyone can encrypt a message and send it to you, but you are the only one who can decrypt it. This is only a portion of a secure system. You also need to make sure that the encrypted message was really sent by a certain person. A certificate is what associates an identity with a public key. – Stefano Sanfilippo May 29 '14 at 14:44
  • This is an highly theoretical field, you should stop here and take some time to read something about [PKI](https://en.wikipedia.org/wiki/Public_key_infrastructure) at least... – Stefano Sanfilippo May 29 '14 at 14:47
  • Woops... substitute the second occurence of "private" with "public". – Stefano Sanfilippo May 29 '14 at 16:38
  • Ok, thanks. I understood why it's not random. The passphrase is used to change the seed random of Math.random. To be more precise it use the sha256 of the passphrase. I asume it's secured only when the passphrase is big. – Madef May 29 '14 at 17:21
  • I tryed with other js lib (jsrsasign & jsencrypt). jsencrypt, for creating keys and jsrsasign for sign and check. But PHP's signatures and jsrsasign's signatures are not the same. So signatures generated with jsrsasign cannot be validated with PHP. – Madef May 29 '14 at 17:26
  • Not that I know anything about jsrsasign / jsencrypt but... phpseclib uses the PSS signature scheme by default. Although it's more secure it's also not as commonly used. PKCS1 is. See http://phpseclib.sourceforge.net/rsa/examples.html#sign,sign2 for more info. – neubert May 29 '14 at 17:45
  • Yes, the system is as secure as its password. Which, generally speaking, is not a bad assumption, but certainly is when talking about RSA. In fact, RSA does not even need a password to work, it should be only a form of backup security in case someone steals your private key. – Stefano Sanfilippo May 29 '14 at 18:27

1 Answers1

3

Thanks @neubert, it's du to PSS signature.

So here is the solution.

I use:

  • phpseclib : PHP lib used to validate message
  • jsrsasign : JS lib used to sign message
  • jsencrypt : JS lib to create private and public key

First, generate the keys in JS:

crypt = new JSEncrypt({default_key_size: 512});
var key = crypt.getKey();

var publicKey = key.getPublicKey();
var privateKey = key.getPrivateKey();

Secondly, create the signature:

var rsa = new RSAKey();
rsa.readPrivateKeyFromPEMString(privateKey);
var hSig = rsa.signStringPSS('message', 'sha1');
var signature = linebrk(hSig, 64);
console.log(signature);

By default the signature is not in the good format. We have to encode hSig in base 64 with the function base64Chars

var base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
b16to64 = function(h) {
    var i;
    var c;
    var ret = "";
    if(h.length % 2 == 1)
    {
        h = "0" + h;
    }
    for (i = 0; i + 3 <= h.length; i += 3)
    {
        c = parseInt(h.substring(i, i + 3), 16);
        ret += base64Chars.charAt(c >> 6) + base64Chars.charAt(c & 63);
    }
    if (i + 1 == h.length)
    {
        c = parseInt(h.substring(i, i + 1), 16);
        ret += base64Chars.charAt(c << 2);
    }
    else if (i + 2 == h.length)
    {
        c = parseInt(h.substring(i, i + 2), 16);
        ret += base64Chars.charAt(c >> 2) + base64Chars.charAt((c & 3) << 4);
    }
    while ((ret.length & 3) > 0) ret += "=";
    return ret;
}

To finish we validate in PHP. We assume the signature and the public key are stored in the vars with the same name:

$rsa = new Crypt_RSA();
$rsa->loadKey($publickey); // public key;
echo $rsa->verify('message', base64_decode($signature)) ? 'verified' : 'unverified';
Madef
  • 619
  • 5
  • 9
  • Well done. You can move the encode part to the server if you wish, by removing the `b16to64` and replacing `base64_decode` with [`hex2bin`](http://www.php.net/manual/en/function.hex2bin.php) on the server. Anyway, you should know that cryptography in browsers with JS is _a very bad idea_ (see [this](http://matasano.com/articles/javascript-cryptography/)), at least until the [Web Crypto API](http://www.w3.org/TR/2014/WD-WebCryptoAPI-20140325/) is largely deployed. – Stefano Sanfilippo Jun 01 '14 at 08:24