生成 RSA 密钥的签名给错误的数据
Generate signature of RSA key give wrong data
我正在使用 https://github.com/phpseclib/phpseclib 生成签名。但是当我用我的 RSA 私钥生成签名时,它生成了签名,但当我用我的 public 密钥验证时它也会给我 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
库有什么问题。我也直接尝试用 openssl 生成,但它仍然没有生成正确的。
有两个问题:
- 要签名的数据必须传递给
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 (described in RFC 8017) in that RSASSA-PKCS1-v1_5 places a digest ID before the hash (in the case of SHA256, this is the ID 0x3031300d060960864801650304020105000420
)。因此,使用 phpseclib(当然)使用 RSASSA-PKCS1-v1_5 的验证失败。
要在 C# 代码中也使用 RSASSA-PKCS1-v1_5,请手动添加摘要 ID 或使用 SignerUtilities.GetSigner("SHA256withRSA")
,以便在签名之前对数据进行哈希处理(然后当然是 explicit 必须省略哈希)。
如果 C# 代码是参考,则需要一个库,无需事先散列即可用于签名,类似于 C# 代码。
phpseclib 使用 RSASSA-PKCS1-v1_5
填充 signing/verifying (对于签名模式 CRYPT_RSA_SIGNATURE_PKCS1
)和不同的 RSAES-PKCS1-v1_5
填充encryption/decryption(用于加密模式CRYPT_RSA_ENCRYPTION_PKCS1
)。因此,这些都不是这里的选项。
如@neubert 的评论所述,phpseclib 提供了在 encryption/decryption 期间禁用填充的可能性(对于加密模式 CRYPT_RSA_ENCRYPTION_NONE
填充是通过 0x00
值)。这允许通过实施自定义填充然后使用私钥加密来进行自定义签名。在当前情况下,这将需要实施 RSASSA-PKCS1-v1_5
填充 而没有 添加摘要 ID 的部分。
这里更简单的是使用已经应用RSASSA-PKCS1-v1_5
填充如果提供了摘要ID和散列的替代方法,这在当前情况下意味着只需要传递散列。此类替代方案的示例是 openssl_private_encrypt
for signing and 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;
我正在使用 https://github.com/phpseclib/phpseclib 生成签名。但是当我用我的 RSA 私钥生成签名时,它生成了签名,但当我用我的 public 密钥验证时它也会给我 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
库有什么问题。我也直接尝试用 openssl 生成,但它仍然没有生成正确的。
有两个问题:
- 要签名的数据必须传递给
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 (described in RFC 8017) in that RSASSA-PKCS1-v1_5 places a digest ID before the hash (in the case of SHA256, this is the ID 0x3031300d060960864801650304020105000420
)。因此,使用 phpseclib(当然)使用 RSASSA-PKCS1-v1_5 的验证失败。
要在 C# 代码中也使用 RSASSA-PKCS1-v1_5,请手动添加摘要 ID 或使用 SignerUtilities.GetSigner("SHA256withRSA")
,以便在签名之前对数据进行哈希处理(然后当然是 explicit 必须省略哈希)。
如果 C# 代码是参考,则需要一个库,无需事先散列即可用于签名,类似于 C# 代码。
phpseclib 使用 RSASSA-PKCS1-v1_5
填充 signing/verifying (对于签名模式 CRYPT_RSA_SIGNATURE_PKCS1
)和不同的 RSAES-PKCS1-v1_5
填充encryption/decryption(用于加密模式CRYPT_RSA_ENCRYPTION_PKCS1
)。因此,这些都不是这里的选项。
如@neubert 的评论所述,phpseclib 提供了在 encryption/decryption 期间禁用填充的可能性(对于加密模式 CRYPT_RSA_ENCRYPTION_NONE
填充是通过 0x00
值)。这允许通过实施自定义填充然后使用私钥加密来进行自定义签名。在当前情况下,这将需要实施 RSASSA-PKCS1-v1_5
填充 而没有 添加摘要 ID 的部分。
这里更简单的是使用已经应用RSASSA-PKCS1-v1_5
填充如果提供了摘要ID和散列的替代方法,这在当前情况下意味着只需要传递散列。此类替代方案的示例是 openssl_private_encrypt
for signing and 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;