phpgorsadigital-signaturephpseclib

Verify RSA-PSS With phpseclib


So i tried to verify rsa-ps signature that signed in golang with php library phpseclib v3. The php verification keep getting me an error. But what makes me confuse is when i try sign the signature in php and verify that in both golang and php it works.

this is the code that i use for sign and verify the signature in golang

Sign signature

rng := rand.Reader
rsa.SignPSS(rng, key, crypto.SHA256, digest, nil)

Verify signature

rsa.VerifyPSS(key, crypto.SHA256, digest, signature, nil)

and this is the code to verify and sign the signature in php

Verify signature

$key = RSA::loadPublicKey('the key');
$result = $key->withHash('sha256')->withMGFHash('sha256')->verify($message, $signature);

Sign signature

$key = RSA::loadPrivateKey('the key');
$key = $private->withPadding(RSA::SIGNATURE_PSS);
$signature = $private->withHash('sha256')->withMGFHash('sha256')->sign($message);

NOTE The value of signature return in encrypted base64 and i already decrypt the value before verify the signature

Anyone can explain hows that happen ? and what should i do to make it works for both ?


Solution

  • PSS allows the specification of various parameters (s. RFC8017, 9.1.1): Digest, mask generation function (in practice, MGF1 is applied and only its digest can be specified) and salt length.
    The verification with PHP fails because although you specify the digests explicitly (so that they are identical in both codes), you use the library defaults for the salt length, which are different. Therefore, the two codes are not compatible:

    Therefore, in order for the PHP code to verify the signature of the Go code, the salt length must be explicitly set to the maximum size using withSaltLength():

    $key = RSA::load($pubKey);
    $result =   $key
                ->withHash('sha256')                // default (in V3)
                ->withMGFHash('sha256')             // default (in V3)
                ->withSaltLength(256 - 32 - 2)      // Fix! Replace 256 with your signature / key length in bytes
                ->verify($message, $signature);
    print($result ? 'valid signature' : 'invalid signature'); // valid signature
    

    For completeness: The maximum salt length can also be determined with:

    $key->getLength()/8 - $key->getHash()->getLengthInBytes() - 2
    

    Alternatively, the digest output length (32 bytes for SHA256), which is applied by default in the PHP code, can be used as salt length in the Go code:

    var ops rsa.PSSOptions
    ops.SaltLength = rsa.PSSSaltLengthEqualsHash
    signature, err := rsa.SignPSS(rng, keyPkcs8, crypto.SHA256, digest, &ops)
    

    In practice, the digest output length is often used as salt length.