Alv*_*nus 0 c# encryption public-key-encryption
我需要验证签名的数据。我不知道如何使用公钥。
public bool VerifyData(string data, string signature)
{
//decode signature from base 64
byte[] signatureByte = System.Convert.FromBase64String(signature);
//hash data to sha256
string hashedData = ConvertToSHA256(data);
byte[] hashedDataByte = System.Convert.FromBase64String(hashedData);
//verify with RSA PSS
string absPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/TP/public");
string publicKeyString = File.ReadAllText(absPath);
publicKeyString = RemoveRSAHeaderAndFooter(publicKeyString);
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//This causes error
RSA.ImportCspBlob(System.Convert.FromBase64String(publicKeyString));
RSAParameters rsaParams = RSA.ExportParameters(true);
RSACng RSACng = new RSACng();
RSACng.ImportParameters(rsaParams);
return RSACng.VerifyData(hashedDataByte, signatureByte, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
}
Run Code Online (Sandbox Code Playgroud)
导致RSA.ImportCspBlob错误。我的公钥是字符串类型。它看起来像这样:
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXX
-----END PUBLIC KEY-----
Run Code Online (Sandbox Code Playgroud)
我怎样才能验证它?
这个shell脚本可以用来验证它:
openssl base64 -A -d -in $temp.sign -out $temp.sha256
openssl dgst -sha256 -sigopt rsa_padding_mode:pss -verify $publickey -signature $temp.sha256 $temp.key
Run Code Online (Sandbox Code Playgroud)
提供者版本错误。
所以我根据@Topaco更新了我的代码:
private async Task<bool> IsContentValid(string data)
{
bool valid = false;
string signature = Request.Headers.GetValues("tkpd-signature").FirstOrDefault();
//decode signature from base 64
byte[] signatureByte = System.Convert.FromBase64String(signature);
//hash data to sha256
string hashedData = ConvertToSHA256(data);
byte[] hashedDataByte = System.Convert.FromBase64String(hashedData);
//verify with RSA PSS
string absPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Keys/tppublic");
string publicKeyString = File.ReadAllText(absPath);
PemReader pr = new PemReader(new StringReader(publicKeyString));
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
RSACng rsaCng = new RSACng();
rsaCng.ImportParameters(rsaParams);
valid = rsaCng.VerifyData(hashedDataByte, signatureByte, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
return valid;
}
private string ConvertToSHA256(string data)
{
using (SHA256 mySHA256 = SHA256.Create())
{
var crypt = new System.Security.Cryptography.SHA256Managed();
var hash = new System.Text.StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(data));
foreach (byte theByte in crypto)
{
hash.Append(theByte.ToString("x2"));
}
return hash.ToString();
}
}
Run Code Online (Sandbox Code Playgroud)
不知道是我之前的步骤错误,还是验证错误。
如果我运行该脚本,则验证成功。这个脚本:
openssl base64 -A -d -in $temp.sign -out $temp.sha256
openssl dgst -sha256 -sigopt rsa_padding_mode:pss -verify $publickey -signature $temp.sha256 $temp.key
Run Code Online (Sandbox Code Playgroud)
所以即使我更改了数据而不转换为sha256,验证仍然失败。
这是我的代码:
private async Task<bool> IsContentValid(string data)
{
bool valid = false;
string signature = Request.Headers.GetValues("tkpd-signature").FirstOrDefault();
//decode signature from base 64
byte[] signatureByte = System.Convert.FromBase64String(signature);
//hash data to sha256
//string hashedData = ConvertToSHA256(data);
byte[] hashedDataByte = Encoding.UTF8.GetBytes(data);
//verify with RSA PSS
string absPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Keys/tppublic");
string publicKeyString = File.ReadAllText(absPath);
PemReader pr = new PemReader(new StringReader(publicKeyString));
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
RSACng rsaCng = new RSACng();
rsaCng.ImportParameters(rsaParams);
valid = rsaCng.VerifyData(hashedDataByte, signatureByte, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
return valid;
}
Run Code Online (Sandbox Code Playgroud)
公钥是:
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxYVf6wVycEygE2VTu4q6
fb7eqkDWikliprXeSazUygHXPlbBqDkHmggylaq5M3C8RwExPyvPhtKNZZ7CzuSH
BfW/TxF1SH+htNvr5Tk/kPPQ/S575gmF9KzXXwJq255qfcwNDiiZZDb1tt4IKa4n
bNK1z2GHtX3CasAJDjTH1aFLZHhUStH9mSo4RaXzl5ZUtPJNg+wXVqiTKfDHz3eS
xHxUtpbvenQwlg3uimT+cBmhzYP87WvwM48IaXrqWBTB082z0COacg4FSuovaYeN
uw+UNeGHWPucZ9ZencxyljcXHjeVi+k2oNJqMfbxfOhzJNPua35Tq0MDmP3qaGKF
2YOnRKKaNNaS7XMSx1f64v0HBrUZftcKxQYdKdhXBd1IJoC00ygt0pOx2gNrNq2f
olJIeV9o/8V24ddvgPHWNKLGBtUCMwFV6lPyXWuJm7FlKDvRF5tiUbFKpAwcXZ84
QqIUSNFPfnJSVdxLutIk2o4TRtLPhFABSu3n+aYxWGQ/lOF3E4ZJb7qdbj6gbMNH
1cSa3gGOfMHl3kDipUUUAvRdMPOox9GzC/JFXpVDWtYajaQIF0JwEZ03Nbg41WXw
5t9MpiDWVpfQJZb4zUiZsdHLdcOSF8QZZvGyAl/rq7Bhs2Q6zLYTnpD1sbUHe8sl
kZMzVPgTz12NOn/sz1hCQLMCAwEAAQ==
-----END PUBLIC KEY-----
Run Code Online (Sandbox Code Playgroud)
这是消息数据:
{“msg_id”:1220037023,“message”:“你好”,“thumbnail”:“https://accounts.tokopedia.com/image/v1/u/25088898/user_thumbnail/desktop”,“full_name”:“阿尔文” ,"shop_id":858157,"user_id":25088898,"payload":{"attachment_type":0,"image":{"image_thumbnail":"","image_url":""},"product":{" image_url":"","名称":"","价格":"","product_id":0,"product_url":""}}}
签名是:
nWjyoCpDZfYbhDcVyVMlJfu/3A1gMOgUyPosuLrtycQWSkvLNPhFGtFHW7kI3ByMXLzMiZRIyc6mg5p4AVrozey8+XIV2A3layynfGy10LiJXULDttP/ldTPmg4VdXOIONNEm283dCzVEsiiGhWcx uwx0XD0fD0CLIwwLN4nwOJiRroY6zyWzRCavv8q5zRHWNJnNRN6t6g6SpqZ4HQPOqRlUgMDH2mqLoiZbngnoOvkG1HgvJ1oySL+45rI/ZBLYUE/rZ4N5abI4oTxJ7K8REya1WxX6YVo0B9Gll2 +xI+Z1G9QZCvZQRVYsYf8f0FmbmqDWQebbSm+ULSC6T69yBXvDIA17+TK/fZhlGCjuHClyZbJlpYYUJSIv1Sac8zTGj9rlStiSFR4a96p33SjqPlkbYXT9akDTMH4ao1SIUKNjVRSW8lN7pBZoLNyQwSR 6yYqSixAu6vbiS/DyLsfFDfheK3s8MkzdM7t0U4eqkbHsHbnJFEhXIAPwjgxd3a3uEfD47A0YpJMWQ1ve9WpPJWWSxApRMP80HzQIute86XNGNedLOhxBF9OeO4o82PCxJ9JGS4nRK+AGPAxQzgZq0 8jp5C2TdFXwwW3uAYViNE3u2Pdi17MDDhZ8fDAvhGWn1l8tbiZM/FN9HMR1mXO/jV/PhqDeJ80E6/R1O2POHM=
这是我用来验证它的完整 shell 脚本:
#!/bin/bash
body=$1
publickey=$2
signature=$3
temp="./tmp"
if [[ $# -lt 3 ]] ; then
echo "Usage: verify <request_body> <public_key> <signature>"
exit 1
fi
echo -n $body > $temp.key
echo -n $signature > $temp.sign
openssl base64 -A -d -in $temp.sign -out $temp.sha256
openssl dgst -sha256 -sigopt rsa_padding_mode:pss -verify $publickey -signature $temp.sha256 $temp.key
rm $temp*
Run Code Online (Sandbox Code Playgroud)
以下 BouncyCastle/C# 代码验证签名消息。使用摘要 SHA256,作为填充 PSS (RSASSA-PSS)。公钥采用 X.509 格式、PEM 编码。
PEM 密钥使用实例 WLOG 从字符串加载PemReader(或者可以从文件系统加载)。使用DotNetUtilitiesfrom BouncyCastleRSAParameters创建一个实例,可以直接从RSACngwith导入该实例ImportParameters()。RSACng还封装了签名/验证的方法。请注意SignData/VerifyData需要未散列的消息(与SignHash/不同VerifyHash):
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
string x509Pem = @"-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEaA9DZlPzykcUY3aaqeT8Cmcx
2qUNvB7/QqQQvkPjk+sxeFLqkppuvbbinN3FHMspPhJlOGZf+gRjmwiOoMEkZAHv
nVfX7gtMxLyUAcXBXFx36t2QE5/45TZ4lzI3udvhAPj7uB1sUKDk5trB8EoX1sVA
kKC9ynrKTPDnyNRDAwIDAQAB
-----END PUBLIC KEY-----";
byte[] message = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
byte[] signature = Convert.FromBase64String(@"rsyqqY1bkGJJkZ4DOfXF+IOpYRdDETvy//PCYGbs70N5Vm8O0P5yqnnxuO5PT9hsOUgJMZeyWxeQITrvXu8buYyx4cah+DfYhOMUzrmyZbzjciTyqWGVYAcZEJNfS0fP8t0XSp5DjKXd1nmaMbB4LuBNwvuEdboFCtN6KRNPzFY=");
PemReader pr = new PemReader(new StringReader(x509Pem));
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
RSACng rsaCng = new RSACng();
rsaCng.ImportParameters(rsaParams);
bool verified = rsaCng.VerifyData(message, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
Console.WriteLine(verified); // True
Run Code Online (Sandbox Code Playgroud)
使用 .NET Framework 4.8 进行测试,
附带说明:BouncyCastle 本身支持使用SignerUtilities类进行签名/验证,因此RSACng不必一定使用。
编辑:
第一个 OpenSSL 语句 Base64 解码签名,第二个 OpenSSL 语句验证首先使用 SHA256 散列然后签名(使用 PSS 作为填充)的消息。
正如已经提到的,隐式VerifyData()执行散列,即消息不得被散列并且不需要该方法。第一个参数应该是:ConvertToSHA256()VerifyData()
Encoding.UTF8.GetBytes(data)
Run Code Online (Sandbox Code Playgroud)
data消息在哪里。
请尝试这个。
如果您仍然有问题,请编辑您的问题并发布可重现的示例,即消息、签名和公钥的具体数据,就像我在示例中所做的那样。
编辑:
PSS 填充具有不同的参数,通常为这些参数分配某些默认值。这些参数之一是盐长度。如果盐长度不等于零,PSS 将应用随机生成的盐,这会导致每次生成不同的签名(概率)。salt 长度的常见默认值是摘要输出长度,对于 SHA256 为 32 个字节(RFC 8017,A.2.3. RSASSA-PSS)。
可以通过添加在 OpenSSL 中设置盐长度-sigopt rsa_pss_saltlen:<length>。除了具体长度的规范之外,还有与版本相关的特殊值,例如 v1.1.1 digest(盐长度对应于摘要输出长度,在本例中为 32 字节)、auto(盐长度由签名确定)和max(盐长度对应于最大可能值),此处。
如果在发布的 OpenSSL 中显式指定长度digest(或显式使用 32 字节),则验证失败,即签名时摘要输出长度不会应用为盐长度。但是,如果使用 指定长度max,则验证成功,即签名时使用了最大可能的盐长度。
相比之下,BouncyCastle/C# 将摘要输出长度用作默认值,对于 SHA256,该长度为 32 字节。
因此,验证失败的原因是盐长度不同:签名时的最大盐长度和使用 C# 验证时的摘要输出长度。
注意:由于发布的 OpenSSL 语句中未指定盐长度,因此使用 OpenSSL 默认值。不幸的是,OpenSSL 文档中没有提供这一点,但就成功验证而言,它只能是maxor auto(验证auto当然也是成功的,因为盐长度是直接从签名确定的)。
最大盐长度究竟是多少?盐不能有任何长度。根据 PSS 规范,针对 4096 位密钥/签名和 SHA256(此处)导出了以下最大可能盐长度(以字节为单位):
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
string x509Pem = @"-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEaA9DZlPzykcUY3aaqeT8Cmcx
2qUNvB7/QqQQvkPjk+sxeFLqkppuvbbinN3FHMspPhJlOGZf+gRjmwiOoMEkZAHv
nVfX7gtMxLyUAcXBXFx36t2QE5/45TZ4lzI3udvhAPj7uB1sUKDk5trB8EoX1sVA
kKC9ynrKTPDnyNRDAwIDAQAB
-----END PUBLIC KEY-----";
byte[] message = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
byte[] signature = Convert.FromBase64String(@"rsyqqY1bkGJJkZ4DOfXF+IOpYRdDETvy//PCYGbs70N5Vm8O0P5yqnnxuO5PT9hsOUgJMZeyWxeQITrvXu8buYyx4cah+DfYhOMUzrmyZbzjciTyqWGVYAcZEJNfS0fP8t0XSp5DjKXd1nmaMbB4LuBNwvuEdboFCtN6KRNPzFY=");
PemReader pr = new PemReader(new StringReader(x509Pem));
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
RSACng rsaCng = new RSACng();
rsaCng.ImportParameters(rsaParams);
bool verified = rsaCng.VerifyData(message, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
Console.WriteLine(verified); // True
Run Code Online (Sandbox Code Playgroud)
-sigopt rsa_pss_saltlen:478通过添加到发布的 OpenSSL 语句中可以轻松验证值 478 。验证成功,确认了这个值。
准确地说,478 只是这一签名的盐长度。为了得到明确的答案,必须知道创建签名的实现方式。这可能会使用不同的盐长度,这意外地对应于所发布签名的最大盐长度。这意味着不同的签名可以使用不同的盐长度。但由于这种逻辑不太可能,因此可以假设用于签名的实现应用了最大盐长度。
问题仍然是如何在 C# 中指定与默认值不同的盐长度。据我所知,这对于 C# 板载方法(即RSACng. 但同样,答案是 BouncyCastle,它提供了该类PssSigner:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.OpenSsl;
...
PemReader pr = new PemReader(new StringReader(publicKeyString));
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
PssSigner pssSigner = new PssSigner(new RsaEngine(), new Sha256Digest(), 512 - 32 - 2);
pssSigner.Init(false, publicKey);
byte[] dataByte = Encoding.UTF8.GetBytes(data);
pssSigner.BlockUpdate(dataByte, 0, dataByte.Length);
valid = pssSigner.VerifySignature(signatureByte);
return valid;
...
Run Code Online (Sandbox Code Playgroud)
至此验证成功。
| 归档时间: |
|
| 查看次数: |
6135 次 |
| 最近记录: |