【问题标题】:RSA: Python signed message verified in PHPRSA:在 PHP 中验证的 Python 签名消息
【发布时间】:2021-05-03 10:34:02
【问题描述】:

我有一个 10 个字符的代码,我想通过我的 python 程序签名,然后将代码和签名放在一个 URL 中,然后由 PHP SLIM API 处理。在这里签名应该得到验证。

我在 python 中生成了我的 RSA 密钥,如下所示:

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization


def gen_key():
    private_key = rsa.generate_private_key(
        public_exponent=65537, key_size=2048, backend=default_backend()
    )
    return private_key


def save_key(pk):
    pem_priv = pk.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )
    with open(os.path.join('.', 'private_key.pem'), 'wb') as pem_out:
        pem_out.write(pem_priv)

    pem_pub = pk.public_key().public_bytes(
        encoding=serialization.Encoding.PEM,
        format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo
    )
    with open(os.path.join('.', 'public_key.pem'), 'wb') as pem_out:
        pem_out.write(pem_pub)


def main():
    priv_key = gen_key()
    save_key(priv_key)

我在 python 中这样对密钥进行签名:

private_key = load_key()
pub_key = private_key.public_key()

code = '09DD57CE10'
signature = private_key.sign(
 str.encode(code),
 padding.PSS(
  mgf=padding.MGF1(hashes.SHA256()),
  salt_length=padding.PSS.MAX_LENGTH
 ),
 hashes.SHA256()
)

url是这样构建的

my_url = 'https://www.exmaple.com/codes?code={}&signature={}'.format(
        code,
        signature.hex()
    )

因为签名是一个字节对象,所以我使用.hex() 函数将其转换为字符串

现在,在 PHP 中,我正在尝试验证代码和签名:

use phpseclib3\Crypt\PublicKeyLoader;
$key = PublicKeyLoader::load(file_get_contents(__DIR__ . "/public_key.pem"));
echo $key->verify($code, pack('h*', $signature)) ? 'yes' : 'no';

我也尝试过使用 PHP openssl_verify

$pub_key = file_get_contents(__DIR__ . "/public_key.pem");
$res = openssl_verify($code, pack('n*', $signature), $pub_key, OPENSSL_ALGO_SHA256);

但是,它总是告诉我签名是错误的,而我显然知道,通常它是正确的签名。 RSA 密钥在 python 和 php 中都是正确且相同的密钥。 我认为问题在于签名以及我必须如何将其转换为字符串,然后在 python 和 php 中转换回字符串等字节。

【问题讨论】:

    标签: python php rsa signature verify


    【解决方案1】:

    Python 代码使用PSS.MAX_LENGTH 作为盐长度。该值表示最大盐长度,建议在 Cryptography 文档(s.here)中使用:

    salt_length (int) – 盐的长度。建议设置为PSS.MAX_LENGTH

    在指定 PKCS#1 和 PSS 的 RFC8017 中,盐长度的默认值定义为哈希的输出长度(s.A.2.3. RSASSA-PSS):

    对于给定的hashAlgorithmsaltLength 的默认值是哈希值的八位字节长度。

    大多数库,例如PHPSECLIB,申请salt长度的默认值,默认定义在RFC8017中,即hash的输出长度(s.here)。因此,必须明确地设置最大盐长度。最大盐长度由 (s.here) 给出:

    signature length (bytes) - digest output length (bytes) - 2 = 256 - 32 - 2 = 222 
    

    对于 2048 位密钥和 SHA256。

    因此,PHP代码中的验证必须更改如下:

    $verified = $key->
                withPadding(RSA::SIGNATURE_PSS)->
                //withHash('sha256')->                  // default
                //withMGFHash('sha256')->               // default
                withSaltLength(256-32-2)->              // set maximum salt length
                verify($code, pack('H*', $signature));  // alternatively hex2bin()
    

    请注意,问题h(十六进制字符串,低半字节在前)的发布代码中以pack()的格式字符串指定。我在我的代码 sn-p 中选择了更常见的 H(十六进制字符串,高半字节优先),它也与 Python 的 hex() 兼容。最终,要选择的格式字符串取决于 Python 代码中应用的编码。

    使用此更改,在我的机器上,可以使用 PHP 代码成功验证使用 Python 代码生成的签名。

    当然,Python 代码的盐长度也可以适应摘要的输出长度(在本例中为 32 个字节)。

    顺便说一句,无法使用openssl_verify() 进行验证,因为不支持 PSS。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-15
      • 2016-04-17
      • 1970-01-01
      相关资源
      最近更新 更多