【问题标题】:Generate signature of RSA key give wrong data生成 RSA 密钥的签名给出错误的数据
【发布时间】:2020-03-27 10:03:10
【问题描述】:

我正在使用https://github.com/phpseclib/phpseclib 来生成签名。但是当我用我的 RSA 私钥生成签名时,它会生成签名,但当我用我的公钥验证时它也会给我unverified

这是我的代码:

<?php

// Include library
include('Crypt/RSA.php');

// Create an Instance
$rsa = new Crypt_RSA();
$hash = new Crypt_Hash('sha256');

$data = array(
     "name" => "hello"
);

$data = json_encode($data, true);

// Load Private Key
$rsa->loadKey('-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAlwK9IAETYwSW6p0hVEue3+WyMtRW5MA3BS2Bzf3B0Qimnvfl
....
-----END RSA PRIVATE KEY-----');

// Message to be signed
$plaintext = $data;

// Set signing signature
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$hashed = $hash->hash($plaintext); 
$encrypted = $rsa->sign($hashed); // Sign Data
$signature = base64_encode($encrypted); // Encode to base64 the signed Data

echo $signature;

$rsa->loadKey('-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlwK9IAETYwSW6p0hVEue
.....
-----END PUBLIC KEY-----'); // public key
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';

现在上面的代码生成签名但不正确。

在此之后,我尝试BouncyCastle lib 仅在 .NET 中使用上述密钥生成签名。它生成了完美的签名并经过验证。

我不知道phpseclib lib 有什么问题。我也直接尝试使用 openssl 生成,但仍然没有生成正确的。

【问题讨论】:

    标签: c# php .net rsa phpseclib


    【解决方案1】:

    有两个问题:

    • 要签名的数据必须传递给sign 方法,而不是这些数据的哈希值。 sign 方法本身执行散列。默认情况下,sha1 用作摘要。必须使用 setHash 明确指定不同的摘要。
    • 必须将签名传递给 verify 方法,而不是 Base64 编码的签名。

    尝试以下修改:

    ...
    $rsa->setHash('sha256');                                                         // Specify digest, e.g. sha256 (default is sha1)
    $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
    $signature = $rsa->sign($plaintext);                                             // Pass plaintext instead of plaintext hash.
    echo base64_encode($signature) . "\n";
    ...
    echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified' . "\n";    // Pass signature instead of Base64 encoded signature
    ...
    

    文档中也有对应的example


    更新:

    C# 代码中的规范SignerUtilities.GetSigner("RSA") 表示对数据进行签名没有事先散列,即必须在之前显式执行散列,这也是当前 C# 代码中的大小写(使用 SHA256)。

    但是,这种方式与标准 RSASSA-PKCS1-v1_5(在 RFC 8017 中描述)的不同之处在于 RSASSA-PKCS1-v1_5 在哈希之前放置了一个摘要 ID(对于 SHA256,这是 ID 0x3031300d060960864801650304020105000420)。因此,使用 phpseclib(当然)使用 RSASSA-PKCS1-v1_5 的验证失败。

    要在 C# 代码中也使用 RSASSA-PKCS1-v1_5,请手动添加摘要 ID 或使用 SignerUtilities.GetSigner("SHA256withRSA") 以便在签名之前对数据进行散列(当然 explicit 散列必须省略)。


    如果 C# 代码是引用,则需要一个库,该库可用于在没有事先散列的情况下进行签名,类似于 C# 代码。

    phpseclib 使用RSASSA-PKCS1-v1_5 填充进行签名/验证(用于签名模式CRYPT_RSA_SIGNATURE_PKCS1)和不同的RSAES-PKCS1-v1_5 填充用于加密/解密(用于加密模式CRYPT_RSA_ENCRYPTION_PKCS1)。因此,这些都不是这里的选项。

    正如@neubert 的评论中所述,phpseclib 提供了在加密/解密期间禁用填充的可能性(对于加密模式CRYPT_RSA_ENCRYPTION_NONE 填充是使用0x00 值完成的)。这允许通过实现自定义填充然后使用私钥加密来进行自定义签名。在当前情况下,这将需要实现RSASSA-PKCS1-v1_5 填充没有添加摘要 ID 的部分。

    这里更容易使用已经应用RSASSA-PKCS1-v1_5填充的替代方法如果提供了摘要ID和散列,这在当前情况下意味着只有散列需要要通过。此类替代方法的示例包括用于签名的openssl_private_encrypt 和用于验证的openssl_public_decrypt

    $data = array(
        "name" => "hello"
    );
    $data = json_encode($data, true);
    
    $hashed = hash('sha256', $data, true);                            // Hash data using sha256
    
    // Signing
    openssl_private_encrypt($hashed, $signature, $privateKey);        // Encrypt data using the private key
    echo base64_encode($signature) . "\n";                            // Output: csorNNekiVXJzQaT/FVCKrlSPfonQYw7dStKsjgVuW/jAcRafk4Yeuzw4rc1WxfgheMaa31DROnhrRCBS6VNUm8w2N/XwX2zmImFLj5KZlnRPne7CZX3YHM3qa5CRf/1VWdNnXMZnBecIe6QltECQLsQ/8fRzaYJpZO6tZjIRf4fBYwqMHWhHlTn6UR3A0GDKSEU5mI2lvzl+9ov+x6AFCLT3sa8hc3aZDfO7SWmQHNHCwrBC9QFNTOHY+/oJXtKYMfY3IskvLSmKmvKc6vh8XANBYp+NCl3/9hNdhbF4psdJXuZgf3aSpy0koBttdcE3js0NoS8Q91ry2WJtkcCrA==
    
    // Verifying
    openssl_public_decrypt($signature, $decrypted, $publicKey);       // Decrypt data using the public key
    $verified = ($decrypted == $hashed) ? 'verified' : 'unverified';  // Verify data 
    echo $verified;
    

    【讨论】:

    • 好的。明白了,但生成的签名仍然不同。生成错误的签名。
    • @deepak - 请注意标识符必须小写,即sha256,否则将无法识别摘要,而只会使用默认摘要sha1。最好发布一个私有 test 密钥,以及预期的和当前生成的签名。
    • 更新了我的私钥和预期的签名。
    • 你确定这两个签名都使用了 1.sha256、2.相同的填充和 3.相同的数据(待签名)吗?签名HHN19M1qy... 似乎是正确的,可以很容易地验证,例如hereSHA256withRSA。你能发布生成其他签名的 C# 代码吗?
    • 问题的原因是C#代码不符合RSASSA-PKCS1-v1_5标准,请看我回答中的更新部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 2016-01-03
    相关资源
    最近更新 更多