在.NET中验证RSA签名

时间:2018-07-22 10:16:40

标签: .net node.js cryptography rsa

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

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

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

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

这是我在节点上工作的代码:

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签名。

谢谢。

2 个答案:

答案 0 :(得分:2)

这里有两个问题。

首先,假设键使用的是65537,则您的指数值不正确。

您打电话给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);

答案 1 :(得分:1)

我不知道为什么.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));