How to convert phpseclib's CRYPT_RSA_SIGNATURE

2019-05-06 19:29发布

问题:

How to convert PHP's $rsa->verify function to node? I used the crypto's verify function like this:

const crypto = require('crypto');
const verify = crypto.createVerify('RSA-SHA256');

verify.update('some data to sign');

const public_key = getPublicKeySomehow();
const signature = getSignatureToVerify();
console.log(verify.verify(public_key, signature));

But it always return false. When I run my data and the signature through php it returns true! Any idea what I might be doing wrong?

The php verify function looks like this :

function verify($message, $signature)
{
    if (empty($this->modulus) || empty($this->exponent)) {
        return false;
    }

    switch ($this->signatureMode) {
        case CRYPT_RSA_SIGNATURE_PKCS1:
            return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
        //case CRYPT_RSA_SIGNATURE_PSS:
        default:
            return $this->_rsassa_pss_verify($message, $signature);
    }
}

It seems to be using CRYPT_RSA_SIGNATURE_PKCS1. How to use it in node?

function _rsassa_pss_verify($m, $s)
    {
        // Length checking

        if (strlen($s) != $this->k) {
            user_error('Invalid signature');
            return false;
        }

        // RSA verification

        $modBits = 8 * $this->k;

        $s2 = $this->_os2ip($s);
        $m2 = $this->_rsavp1($s2);
        if ($m2 === false) {
            user_error('Invalid signature');
            return false;
        }
        $em = $this->_i2osp($m2, $modBits >> 3);
        if ($em === false) {
            user_error('Invalid signature');
            return false;
        }

        // EMSA-PSS verification

        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
    }

回答1:

My PHP code:

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->loadKey('-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqHweEuX181yfc0JNVzDilMU1hzias41USnFh3z/+QZOGHZrxY
Plv2WuBD6mwlJkPbr8KZ6N2MrDeblrtOKiQqufRmzO/7hSEzAMmt/BS8+QjdULbn
FlGWf0NCDyZgFS2zq/byXvdPBRB2JAO1VW6yGGtdEF+cTX6og0VQKhd6rQIDAQAB
AoGATfhcxMsTJaWjAoSiEn/AMo4vLjWC6lhjBF2d+oPdWPuzlhBTIIqsMXQjN114
5WntTgH3Sf09lZzU+72Q6VlNK3xaXF5URbyQ6fV5wUhkudHKYiV9YmgA8xaBJPxe
PWCyQUIeWAh4vKStPZH1nSQ7WD0FXS/Se74Pn+bjw2n/mOECQQDdyrJOcRZhgX2c
pv6lHtY6vmuAkoQTOBlDfAFZNuyilsPeewXJgPptsj1JbTfqVzbpCOSBWtoQF0K2
mG5nBqf5AkEAxFwnbVEVl8AS4hjCBTwyHWDRSWrYh61z/9o2PYO1Jo1XJbCt24E2
jacs4thLNANa1FArsJNkUn6oINUfwk6dVQJAGM9KwNGPolqc1YAsrgXUCwAwAFLj
aU72LIFbmUI2mD1rLaDvVcoSuWCd0G/iOFmHL+wpu3qROAmSEeDdQLMS6QJBAJE3
IcVbiN+AIbbp3lrmyJ0nY7+q79VvNnFJnIFjrQ2Ey6VJA1ppNcIOl0hv8zXVIVAQ
ti7Q4gmMN6SIWQNnBj0CQDpc55PwtHpMcBIW5+nRfIsWIePTAzy6kERTuKRO0VPP
dRlIQ1q9P4O9hXEEqR5Yg4wknQMY5VTfmpH6q5pKzzI=
-----END RSA PRIVATE KEY-----');

$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$rsa->setHash('sha256');
echo bin2hex($rsa->sign('zzz'));

My node.js code (using node-rsa):

var NodeRSA = require("node-rsa");

var key = new NodeRSA("-----BEGIN PUBLIC KEY-----\n" +
    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqHweEuX181yfc0JNVzDilMU1h\n" +
    "zias41USnFh3z/+QZOGHZrxYPlv2WuBD6mwlJkPbr8KZ6N2MrDeblrtOKiQqufRm\n" +
    "zO/7hSEzAMmt/BS8+QjdULbnFlGWf0NCDyZgFS2zq/byXvdPBRB2JAO1VW6yGGtd\n" +
    "EF+cTX6og0VQKhd6rQIDAQAB\n" +
    "-----END PUBLIC KEY-----");

//console.log(key.exportKey("pkcs8"));

key.setOptions("pkcs1-sh256");

var signature = "4b2149baacc8f5616b84c258f6b34526315f64bd2afdeb60967b65534a2c" +
        "084b9499b902672c75e7b0cf75aca6b7aee9269abdb374d95b3a28c6c82b" +
        "68961b71dd3925acb69eb028f9bc5f9537cae6c9d1f4588fa62521a210e2" +
        "f8ee18deee266423de48a78b8411cb0c8fadce979fe4fc54272c0f4ab1db" +
        "9d7fd35441bfb6b7";
console.log(key.verify("zzz", signature, "binary", "hex") ? "valid" : "invalid");

phpseclib generated the signature. Since it's PKCS#1 the signature is deterministic so you'll get the same signature every time you try to generate it. The public key in the node.js code corresponds to the private key in the PHP code. You can verify that, yourself, by doing echo $rsa->getPrivateKey(); in the PHP code.

The node.js code outputs "valid" with the phpseclib-generated signature so it works. If you change the hex encoded signature you'll get "invalid" so that's another way to validate the signature verification process.