C#:验证签名不起作用,但适用于 Java

问题描述 投票:0回答:1

我在通过仅提供摘要来使用 C# 验证 RSA 签名时遇到问题。这是我的Java代码,它返回一个“true”值,表明签名是正确的:

        String certificateBase64 = "MIIC6jCCAdKgAwI...";
        String signatureValueBase64 = "d0rLeNX+sQCZ3io0ji6j0P2M4bpGYxA+WKS6KkP4GYDxm4qrfhnvrKUOqvDmXNT3kNhv8I6Se6T4TFqJmYPTTQ8O914nDNmqASlknii6EA4ALExVhXfuZ7BeVNZy33AacBF8K/GIYH9DyvtWz9lVFaIX4BV9176CHo2FdSYXfNnrpJWQBIlpEhnsVHThCAabF/AOM/G2vgr5yO2Etbz3VCyZJesBOEmqRl6VlndFXZBgR527zknrH5fnACYrDacVXnFFY1OYGhaRI7tZrOvgkELnXV55aYhShWjAVcrGxaTC6vCHx3Ou4bsbjSCs4PXNMaaPcHUKH99xXODHFnMOjA==";
        String hashBase64 = "MDEwDQYJYIZIAWUDBAIBBQAEIGlE+Lr0y76nMOdxNLu1k5g4l3feoWU4MYZm8yo0N5vE";

        Base64.Decoder decoder = Base64.getDecoder();

        CertificateFactory x509Factory = CertificateFactory.getInstance("X509");
        X509Certificate x509Cert;
        try (ByteArrayInputStream bais = new ByteArrayInputStream(decoder.decode(certificateBase64))) {
            x509Cert = (X509Certificate) x509Factory.generateCertificate(bais);
        }

        Signature signature = Signature.getInstance("NONEwithRSA");
        signature.initVerify(x509Cert.getPublicKey());
        signature.update(decoder.decode(hashBase64));

        boolean valid = signature.verify(decoder.decode(signatureValueBase64));
        System.out.println(valid);

但是,当我尝试使用提供相同输入参数的 C# 验证签名时,我总是收到“false”。这是我的代码:

    string certificateBase64 = "MIIC6jCCAdKgAwI...";
    string signatureValueBase64 = "d0rLeNX+sQCZ3io0ji6j0P2M4bpGYxA+WKS6KkP4GYDxm4qrfhnvrKUOqvDmXNT3kNhv8I6Se6T4TFqJmYPTTQ8O914nDNmqASlknii6EA4ALExVhXfuZ7BeVNZy33AacBF8K/GIYH9DyvtWz9lVFaIX4BV9176CHo2FdSYXfNnrpJWQBIlpEhnsVHThCAabF/AOM/G2vgr5yO2Etbz3VCyZJesBOEmqRl6VlndFXZBgR527zknrH5fnACYrDacVXnFFY1OYGhaRI7tZrOvgkELnXV55aYhShWjAVcrGxaTC6vCHx3Ou4bsbjSCs4PXNMaaPcHUKH99xXODHFnMOjA==";
    string hashBase64 = "MDEwDQYJYIZIAWUDBAIBBQAEIGlE+Lr0y76nMOdxNLu1k5g4l3feoWU4MYZm8yo0N5vE";

    byte[] certificateBytes = Convert.FromBase64String(certificateBase64);
    byte[] signatureValueBytes = Convert.FromBase64String(signatureValueBase64);
    byte[] hashBytes = Convert.FromBase64String(hashBase64);

    X509Certificate2 x509Cert = new X509Certificate2(certificateBytes);
    RSA publicKey = (RSA)x509Cert.GetRSAPublicKey();

    bool isValid = publicKey.VerifyHash(hashBytes, signatureValueBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        
    Console.WriteLine(isValid);

你有什么想法吗?

我尝试使用 RSA、RSACryptoServiceProvider 和 RSAPKCS1Signatureformatter 在 C# 中验证签名,但都没有成功。 我不是经验丰富的 C# 程序员,所以我不知道所有的可能性。

java c# rsa signature
1个回答
0
投票

Java 代码使用 PKCS#1 v1.5 填充。要找到 C# 代码的修复程序,需要至少对 PKCS#1 v1.5 填充有较高水平的了解:

对于 PKCS#1 v1.5 填充,消息 M 首先被散列:H,然后在前面添加摘要 ID:T = ID || H(这里,T是DigestInfo的DER编码)。
填充时,T 以 0x00 || 为前缀0x01 ||附注|| 0x00,其中 PS 由如此多的 0xFF 值组成,使得签名大小等于 RSA 密钥大小(即模数)。

总体:0x00 || 0x01 ||附注|| 0x00 || T.


在Java代码中,

hashBase64
对应T的Base64编码,其中ID对应SHA256的ID。
NONEwithRSA
定义数据被直接解释为 T(因此消息不会被散列,也不会预先添加 ID)。
这样,签名0x00 || 0x01 ||附注|| 0x00 || T 也会生成,即您将获得相同的 PKCS#1 v1.5 兼容签名,就像您使用原始数据(从中生成哈希)和算法
SHA256withRSA


在 C# 代码中,可以使用

VerifyHash()
实现相同的效果。但是,不必传递 T,而只需传递哈希值。摘要 ID 是由 VerifyHash()
隐式添加的,这就是必须指定摘要的原因。

当前C#代码中的bug是通过了T(即

hashBytes

)而不是H,所以将
hashBytes
替换为
hashBytes[^32..]
,验证成功。

© www.soinside.com 2019 - 2024. All rights reserved.