C# RSA签名验证失败但Java验证成功,寻求解决方案
RSA签名验证:Java成功但C#失败的解决方法
我遇到了仅通过提供摘要在C#中验证RSA签名失败的问题,对应的Java代码能正常返回true,但C#代码始终返回false。
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);
问题根源
Java代码使用NONEwithRSA算法,意味着直接对原始哈希字节进行RSA PKCS#1签名,没有添加哈希算法的ASN.1标识前缀。而C#的VerifyHash方法默认会自动为传入的哈希值添加对应算法的ASN.1前缀(例如SHA256的前缀为3031300D060960864801650304020105000420),导致验证时的内容与签名生成时的原始哈希不匹配,最终返回false。
解决方法
要对齐Java的逻辑,需要手动实现无哈希前缀的RSA签名验证,以下是可行的代码:
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); using (RSA publicKey = x509Cert.GetRSAPublicKey()) { // 用PKCS#1填充解密签名 byte[] decryptedSignature = publicKey.Decrypt(signatureValueBytes, RSAEncryptionPadding.Pkcs1); // 从解密结果末尾截取与原始哈希长度一致的字节(PKCS#1填充的额外内容在头部) byte[] extractedHash = decryptedSignature.Skip(decryptedSignature.Length - hashBytes.Length).ToArray(); // 比对截取后的哈希与输入哈希是否一致 bool isValid = extractedHash.SequenceEqual(hashBytes); Console.WriteLine(isValid); }
代码说明
- 使用
RSA.Decrypt方法配合RSAEncryptionPadding.Pkcs1解密签名,得到包含PKCS#1填充的原始数据。 - PKCS#1填充的额外字节位于数据头部,因此从解密结果末尾截取与输入哈希长度相同的字节,得到原始签名的哈希值。
- 比对截取后的哈希与输入哈希,判断签名是否有效。
内容的提问来源于stack exchange,提问作者bsanchezb
相关产品推荐
相关产品推荐

