【问题标题】:Verifying An RSA Signature In .NET在 .NET 中验证 RSA 签名
【发布时间】:2018-07-22 10:16:40
【问题描述】:

我已经使用 node-rsa 在 Node 中生成了一个私钥和公钥对,并且可以使用 node 中的这些密钥成功签名和验证。

我要做的是使用公钥的模数和指数来验证 .NET 中的签名(由节点中的私钥签名)。

例如我有一个字符串 =“foo”,它有一个由 Node.js 生成的签名“abc123”。 在 .NET 中,我想使用公钥的模数和指数来确认签名“abc123”对“foo”有效。

我想使用模数和指数而不是直接使用公钥的原因是因为我想创建一个 Web 服务,该服务在众所周知的地址公开一个 jwks.json 文件,任何人都可以在该地址验证令牌服务产生。

这是我在 node 中工作的代码:

let privateKey = fs.readFileSync('privateKeyPKCS1.pem');
let publicKey = fs.readFileSync('publicKeyPKCS1.pem');

let testpublicForExport = new NodeRSA(publicKey);
let keyComponents = testpublicForExport.exportKey('components-public');
console.log(keyComponents.n.toString('base64'));

let sign = crypto.createSign('RSA-SHA256');
let stringToSign = 'hello';
sign.update(stringToSign);
let sig = sign.sign(privateKey, 'base64');
console.log('sig='+sig);

let verify = crypto.createVerify('RSA-SHA256');
verify.update(stringToSign);
console.log(verify.verify(publicKey, sig, 'base64'));

这就是我为 .NET 准备的内容(我猜这部分有点错误):

    string modulus = "AJ/dk3x2xQjvNTJpRh/yW6vURVhkubHVrXk33zd76k7/VnHS/8RE49IM+d137kvz8SgVQBOcN1P2yhOFuhbtYR7spwQ5lhob9GNkzR4l/ybgcriIIwGO0vmo264Xz2vERhZ7cVITOvn49VpWRbeVS3Gsn3ecSILdeoh2yNoevqwFGnuPeVyD4JOwo+JhEhXDlzth9UFu18x5k7NNLG2gywI7qX/qMd1+2rnDI33rSLpXiPXGQg9BCmatMx+R4sWV2AGOx9YxI/YBUTl0hSGcWtzsPxGA2zY3oBT0P9FGg+QYvVjHdAkuJsx9QpPo0TlNbrNvodj3KEUwxoGwnWu6Ick=";
    int exponent = 65537;

    string stringToSign = "hello";
    string sig = "fGpD/SMPvS2CK9D4Z4ykUj+dRmPOIK92wdaRnhpAl3toOLdNT+96PUoCQKJ30UIRyXchIlmCvg+QH/MwimXueyY/oBwMH36EpajyraJZA/2RRA3/F7hRIT6/G9lJe69npeGyvMx+LlIov9B+I99KSLiVdYYPSrU4eUb3fe4wuZZd/cSIcwnIVvCrjOSUZAF09sfKx7Qt6LhTcQm2J332PsvotEZUuwTRsHh0QMMUd/eIWZ+yPfPGMX4D5WJEUxnlQT2f9Vfxx7JBnx5IDI9Jf6ohJCl5bymeKjh4ZsBqRINGG0aiElZdhGdcsGfhZGjRR2MMQkIo7ZSRrtaaKO5u9g==";

    RSA rsa = RSA.Create();
    rsa.ImportParameters(new RSAParameters()
    {
        Modulus = Convert.FromBase64String(modulus),
        Exponent = BitConverter.GetBytes(exponent)
    });

    bool isVerified = rsa.VerifyData(Encoding.UTF8.GetBytes(stringToSign), Convert.FromBase64String(sig), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

如有必要,我很乐意使用充气城堡(虽然我不是很熟悉)。

TLDR;我正在尝试仅使用公钥的模数和指数来验证 RSA 签名。

谢谢。

【问题讨论】:

  • .NET 代码还能编译吗?
  • @MaartenBodewes 是的。它编译、运行并 isVerified = false

标签: .net node.js cryptography rsa


【解决方案1】:

这里有两个问题。

首先,假设使用的密钥为 65537,您的 Exponent 值不正确。

您调用了BitConverter.GetBytes,它产生了{ 0x01, 0x00, 0x01, 0x00 }(小端),在上下文(大端)中用作16,777,472,比您预期的大256倍。

最好的办法是将其保存到值为{ 0x01, 0x00, 0x01 } 的静态数组中。

其次,Javascript 代码以其 DER 编码表示形式输出模值,这意味着它以 0x00 开头。 RSAParameters 类期望整数值是无符号的,因此它表示您导入了一个稍微无效的 2056 位密钥。然后它直接拒绝签名,因为密钥的大小错误。您需要删除前导 0x00。

总之:

string modulus = "AJ/dk3x2xQjvNTJpRh/yW6vURVhkubHVrXk33zd76k7/VnHS/8RE49IM+d137kvz8SgVQBOcN1P2yhOFuhbtYR7spwQ5lhob9GNkzR4l/ybgcriIIwGO0vmo264Xz2vERhZ7cVITOvn49VpWRbeVS3Gsn3ecSILdeoh2yNoevqwFGnuPeVyD4JOwo+JhEhXDlzth9UFu18x5k7NNLG2gywI7qX/qMd1+2rnDI33rSLpXiPXGQg9BCmatMx+R4sWV2AGOx9YxI/YBUTl0hSGcWtzsPxGA2zY3oBT0P9FGg+QYvVjHdAkuJsx9QpPo0TlNbrNvodj3KEUwxoGwnWu6Ick=";
int exponent = 65537;

string stringToSign = "hello";
string sig = "fGpD/SMPvS2CK9D4Z4ykUj+dRmPOIK92wdaRnhpAl3toOLdNT+96PUoCQKJ30UIRyXchIlmCvg+QH/MwimXueyY/oBwMH36EpajyraJZA/2RRA3/F7hRIT6/G9lJe69npeGyvMx+LlIov9B+I99KSLiVdYYPSrU4eUb3fe4wuZZd/cSIcwnIVvCrjOSUZAF09sfKx7Qt6LhTcQm2J332PsvotEZUuwTRsHh0QMMUd/eIWZ+yPfPGMX4D5WJEUxnlQT2f9Vfxx7JBnx5IDI9Jf6ohJCl5bymeKjh4ZsBqRINGG0aiElZdhGdcsGfhZGjRR2MMQkIo7ZSRrtaaKO5u9g==";

byte[] modulusBytes = Convert.FromBase64String(modulus);

if (modulusBytes[0] == 0)
{
    modulusBytes = modulusBytes.Skip(1).ToArray();
}

RSA rsa = RSA.Create();
rsa.ImportParameters(new RSAParameters()
{
    Modulus = modulusBytes,
    Exponent = new byte[] { 0x01, 0x00, 0x01 },
});

bool isVerified = rsa.VerifyData(Encoding.UTF8.GetBytes(stringToSign), Convert.FromBase64String(sig), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(isVerified);

【讨论】:

  • 很好的发现,我知道这是一个编码问题,但我无法找到它。顺便说一下,官方GetBytes 的输出被列为“平台相关”,而不是小端。所以不要在一个,呃,sparc之类的东西上运行它。
  • 我在用 BouncyCastle 发布答案之前尝试过这个,这没有帮助
  • @Zergatul 我回头发现 base64 以 A 开头,这在上下文中永远无效。更新了答案以删除填充字节。
  • 这确实是个大问题!谢谢你拯救我的理智:-)
【解决方案2】:

我不知道为什么 .NET RSA 类无法验证签名,一切看起来都很好。下面是 BouncyCastle 代码:

string modulus = "AJ/dk3x2xQjvNTJpRh/yW6vURVhkubHVrXk33zd76k7/VnHS/8RE49IM+d137kvz8SgVQBOcN1P2yhOFuhbtYR7spwQ5lhob9GNkzR4l/ybgcriIIwGO0vmo264Xz2vERhZ7cVITOvn49VpWRbeVS3Gsn3ecSILdeoh2yNoevqwFGnuPeVyD4JOwo+JhEhXDlzth9UFu18x5k7NNLG2gywI7qX/qMd1+2rnDI33rSLpXiPXGQg9BCmatMx+R4sWV2AGOx9YxI/YBUTl0hSGcWtzsPxGA2zY3oBT0P9FGg+QYvVjHdAkuJsx9QpPo0TlNbrNvodj3KEUwxoGwnWu6Ick=";
int exponent = 65537;

string stringToSign = "hello";
string sig = "fGpD/SMPvS2CK9D4Z4ykUj+dRmPOIK92wdaRnhpAl3toOLdNT+96PUoCQKJ30UIRyXchIlmCvg+QH/MwimXueyY/oBwMH36EpajyraJZA/2RRA3/F7hRIT6/G9lJe69npeGyvMx+LlIov9B+I99KSLiVdYYPSrU4eUb3fe4wuZZd/cSIcwnIVvCrjOSUZAF09sfKx7Qt6LhTcQm2J332PsvotEZUuwTRsHh0QMMUd/eIWZ+yPfPGMX4D5WJEUxnlQT2f9Vfxx7JBnx5IDI9Jf6ohJCl5bymeKjh4ZsBqRINGG0aiElZdhGdcsGfhZGjRR2MMQkIo7ZSRrtaaKO5u9g==";

var signer = Org.BouncyCastle.Security.SignerUtilities.GetSigner("SHA256withRSA");
signer.Init(false, new Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters(
    false,
    new Org.BouncyCastle.Math.BigInteger(1, Convert.FromBase64String(modulus)),
    Org.BouncyCastle.Math.BigInteger.ValueOf(exponent)));
byte[] data = Encoding.ASCII.GetBytes(stringToSign);
signer.BlockUpdate(data, 0, data.Length);
bool ok = signer.VerifySignature(Convert.FromBase64String(sig));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-24
    • 1970-01-01
    • 1970-01-01
    • 2012-01-16
    相关资源
    最近更新 更多