首页 > 解决方案 > 用 C# 签名的数据,在 C# 中验证,但在 Java 中不验证

问题描述

我已经签署了我想在 C# 和 Java 中验证的数据。虽然它在 C# 中验证良好,但在 Java 中失败(没有例外,但验证函数返回 false。

C#代码:

using System;
using System.Security.Cryptography;

namespace ConsoleApp3
{
    class Program
    {
        static void Main()
        {
            var publicKeyXml = "<RSAKeyValue><Modulus>19mQjm4uJASc0BGcaAm5IRNbQWkT+VlqAN3cqWgdXhjNFY1FaaMV6H7gFJdOffEMRMcPjslH1Sc2HlOSDeZIqZJFZT8Lhk5DAtcA/WtmEhF3zt6cjvPqoWRXGweb2vC0KWGUC+GF3OTlrC4QwHf2xyMe1yeY/opjigWjMJkrDfD8G3nSY5QAPUaeCfrRikC0hq66OIApEwMS4inQcHZKmIq31U2BK84qsB3UWNpsK1XcbMkHOZfvyqhfHLuLScXQgFxzTzrH5/J51UzU1Pc0o4c7T+TJq4i9f9MOs13PhY5uNQwGyQ0H5bf4VQt9dRhZF2T1TxSk0T4PEbXP50dLDjAY9/S/8ud/lhFSqre7KOxIxDcJ/bk1j4UWEChom2TaS5+4QWQyHMfeZtYFlDR3iZw5eL2SuhvG+ZkZ4rfugqY9hUwfpVmhjp6z/CKcpaSuwzxwYxR2WIC79n6uTF97ay9W6SYgKxuz5YjeIUoPamKEOTJi36IsCCjdsa8a8nE4LoFBoeCGYDZTlrHBzQ76T6M7sdaZUrR4uttftwrslcrnYPNc/DVM9Y+oQ1ZcPcTnArn+PbNiRwgj6Bk/+HJ6CpNxhtn6P5STHa5SblBNvVX4AZ//y5xGt9Sc4ouWpLIICwjBo8Xq7HkTEAH4MYzJOvlBQBLeTTxGeI1VBkxfsVk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
            var data = new byte[] {1, 2, 3, 4};
            var signature = Convert.FromBase64String(
                "r0NOhHjRkV3muCh7paDQkKNGTAZFGCK/viWQa33Y7ysM3XsP7+HychZVYJocpKn6M01T5o2ltdjSeJeBPQY3KrLszMJhbGgK4kme6qx/qQj7mLOkefxgEfijxcDkhQ2dmI+RWNPckyqEt0hrqju1TWO4/Pju1wWvSsYiSD17xF9o+yy88Okkg4Z3DFIGXrXaGjJUfnG48tIR1plhoud9LvVFB8uq03XuY47b3JaClh96k/1X8WSCwzD0kw1zdTqkxLP4snLPgyMyG34ChiAgouH4NS6VTKF9iQ0gtOK3Mz2lPuWMDpgyC25C7yuRGtV+6G9Vyu0YBQDW8lbthuKLACoIMsonkZRoY7Sx2l85XnwNQgc40T9itPo6DCoE0FWHEvBkmLgxPTDMaLLG2aovpUGgCaPr5ia2aA10myB+CgPgM95sesHPZTINTESh2tpXim3dMxQPdkruwyLzpZ00oqM2J0Lkz8MMgmPRfrpoxZZoXByWpK1ofoREa20yjxCd7cXp39hIf3spmHsp/s1bS6dF9Iwlybo5gNX9by33TBvpOcUUX7B4XKzuYQ+s7ZnCeYxcAdk4Fd4aYcmEOpsxue+UxKZn+5KRcBm3S3hiR4CIIXG8X+0YU6haI9U3WR9ajmLiMK2aixLtzYkMo7wBPZRJDlua5mR4k2C5Jhjinvg=");
            var rsaKey = new RSACryptoServiceProvider();
            rsaKey.FromXmlString(publicKeyXml);
            if (rsaKey.VerifyData(data, "SHA256", signature))
                Console.WriteLine("VERIFIED");
        }
    }
}

Java代码:

import java.math.BigInteger;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;

public class Main {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
        String publicKeyModulus = "19mQjm4uJASc0BGcaAm5IRNbQWkT+VlqAN3cqWgdXhjNFY1FaaMV6H7gFJdOffEMRMcPjslH1Sc2HlOSDeZIqZJFZT8Lhk5DAtcA/WtmEhF3zt6cjvPqoWRXGweb2vC0KWGUC+GF3OTlrC4QwHf2xyMe1yeY/opjigWjMJkrDfD8G3nSY5QAPUaeCfrRikC0hq66OIApEwMS4inQcHZKmIq31U2BK84qsB3UWNpsK1XcbMkHOZfvyqhfHLuLScXQgFxzTzrH5/J51UzU1Pc0o4c7T+TJq4i9f9MOs13PhY5uNQwGyQ0H5bf4VQt9dRhZF2T1TxSk0T4PEbXP50dLDjAY9/S/8ud/lhFSqre7KOxIxDcJ/bk1j4UWEChom2TaS5+4QWQyHMfeZtYFlDR3iZw5eL2SuhvG+ZkZ4rfugqY9hUwfpVmhjp6z/CKcpaSuwzxwYxR2WIC79n6uTF97ay9W6SYgKxuz5YjeIUoPamKEOTJi36IsCCjdsa8a8nE4LoFBoeCGYDZTlrHBzQ76T6M7sdaZUrR4uttftwrslcrnYPNc/DVM9Y+oQ1ZcPcTnArn+PbNiRwgj6Bk/+HJ6CpNxhtn6P5STHa5SblBNvVX4AZ//y5xGt9Sc4ouWpLIICwjBo8Xq7HkTEAH4MYzJOvlBQBLeTTxGeI1VBkxfsVk=";
        String publicKeyExponent = "AQAB";
        byte[] data = new byte[]{1,2,3,4};
        byte[] signature = Base64.getDecoder().decode(
                "r0NOhHjRkV3muCh7paDQkKNGTAZFGCK/viWQa33Y7ysM3XsP7+HychZVYJocpKn6M01T5o2ltdjSeJeBPQY3KrLszMJhbGgK4kme6qx/qQj7mLOkefxgEfijxcDkhQ2dmI+RWNPckyqEt0hrqju1TWO4/Pju1wWvSsYiSD17xF9o+yy88Okkg4Z3DFIGXrXaGjJUfnG48tIR1plhoud9LvVFB8uq03XuY47b3JaClh96k/1X8WSCwzD0kw1zdTqkxLP4snLPgyMyG34ChiAgouH4NS6VTKF9iQ0gtOK3Mz2lPuWMDpgyC25C7yuRGtV+6G9Vyu0YBQDW8lbthuKLACoIMsonkZRoY7Sx2l85XnwNQgc40T9itPo6DCoE0FWHEvBkmLgxPTDMaLLG2aovpUGgCaPr5ia2aA10myB+CgPgM95sesHPZTINTESh2tpXim3dMxQPdkruwyLzpZ00oqM2J0Lkz8MMgmPRfrpoxZZoXByWpK1ofoREa20yjxCd7cXp39hIf3spmHsp/s1bS6dF9Iwlybo5gNX9by33TBvpOcUUX7B4XKzuYQ+s7ZnCeYxcAdk4Fd4aYcmEOpsxue+UxKZn+5KRcBm3S3hiR4CIIXG8X+0YU6haI9U3WR9ajmLiMK2aixLtzYkMo7wBPZRJDlua5mR4k2C5Jhjinvg=");

        byte[] mod = Base64.getDecoder().decode(publicKeyModulus);
        byte[] exp = Base64.getDecoder().decode(publicKeyExponent);
        BigInteger modulus = new BigInteger(mod);
        BigInteger exponent = new BigInteger(exp);
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature s = Signature.getInstance("SHA256withRSA");
        s.initVerify(pubKey);
        s.update(data);
        if (s.verify(signature))
            System.out.println("VERIFIED");
    }
}

我发现了几个类似的 SO 帖子,但找不到解决方案。有人提到了 .NET 和 Java 之间 BigInteger 编码的区别。我确认字节顺序不同:当在 java 程序中反转“mod”缓冲区时(小端 -> 大端),在创建 BigInteger 之前,“模数”与 .NET 中的相同(如果我采用从 .NET 中的 XML 中提取模数并将其转换为 BigInteger)。指数缓冲区读取 0x01 0x00 0x01,所以没关系(前后相同)。但是在这样做之后,验证仍然失败。

标签: javac#digital-signaturesha256

解决方案


推荐阅读