【问题标题】:Should I use MessageDigest to verify a digital signature that signed in C#?我应该使用 MessageDigest 来验证在 C# 中签名的数字签名吗?
【发布时间】:2020-08-27 12:25:15
【问题描述】:

我是 Java 新手,想在 c# 中对一些数据进行签名,并在 Java 中使用 RSA 和 SHA512 进行验证。

C#:

static string SignData(string message, RSAParameters privateKey)
    {
        byte[] signedBytes;
        using (var rsa = new RSACryptoServiceProvider())
        {
            var encoder = new UTF8Encoding();
            byte[] originalData = encoder.GetBytes(message);
                rsa.ImportParameters(privateKey);
               
                signedBytes = rsa.SignData(originalData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);

                rsa.PersistKeyInCsp = false;
        }
        return Convert.ToBase64String(signedBytes);
    }

Java:

 static boolean verifySignature512(String message, String sign, PublicKey publicKey) throws Exception {
    MessageDigest digest = MessageDigest.getInstance("SHA-512");
    byte[] contentDigest = digest.digest(message.getBytes(Charset.forName("UTF-8")));

    Signature signature = Signature.getInstance("Sha512withRSA");
    signature.initVerify(publicKey);
    signature.update(contentDigest);
    return signature.verify(Base64.getDecoder().decode(sign));
}

我使用了 RSA 以及公钥和私钥。 Java 函数总是返回 false,没有错误或异常。 如果我像下面这样删除 MessageDigest,它会开始工作,它是否安全且安全?

static boolean verifySignature512(String message, String sign, PublicKey publicKey) throws Exception {
   // MessageDigest digest = MessageDigest.getInstance("SHA-512");
   // byte[] contentDigest = digest.digest(message.getBytes(Charset.forName("UTF-8")));

    Signature signature = Signature.getInstance("Sha512withRSA");
    signature.initVerify(publicKey);
    signature.update(message.getBytes(Charset.forName("UTF-8")));
    return signature.verify(Base64.getDecoder().decode(sign));
}

【问题讨论】:

    标签: java c# rsa digital-signature


    【解决方案1】:

    Java 端的签名验证必须失败,因为您在双方使用不同的哈希算法。 在 C# 中,您使用的是 SHA1 ('HashAlgorithmName.SHA1'),而 Java 部分使用的是 SHA512 ('Signature signature = Signature.getInstance("Sha512withRSA");')。

    以下代码使用 SHA1 作为哈希算法,但您可以轻松地将其(在所有代码行 :-)更改为 SHA256 或 SHA512。 C# 代码使用公钥验证签名,Java 代码中使用相同的公钥(编码为 PEM) 仅用于验证。

    安全警告:我的示例代码使用不安全的 512 位 RSA 密钥,不应在生产中使用。 没有适当的异常处理,您正在使用填充“RSASignaturePadding.Pkcs1”,它应该 也不再使用了。

    这是我的 C# 代码的输出:

    Should I use MessageDigest to verify a digital signature that signed in C#?
    signedData: mU2bcCMEhG13xG9sKwhaA//dnw2+rbLkwz2737cNU5kb2EBenJIEJ+bA596XccCVKUKPanxMUFoVw2fl8HhCNw==
    The data was verified.
    

    这是 Java 输出:

    RSA instance: SHA1withRSA
    The data was verified.
    

    C#-代码:

    using System;
    using System.Security.Cryptography;
    using System.Text;
    
    class RSACSPSample {
        static void Main() {
    
            try {
                Console.WriteLine("Should I use MessageDigest to verify a digital signature that signed in C#?");
                // Create a UnicodeEncoder to convert between byte array and string.
                ASCIIEncoding ByteConverter = new ASCIIEncoding();
    
                string message = "this is the important message to sign";
    
                // get private and public key ### SAMPLE and UNSECURE 512 bit RSA keypair
                var privateKey = "<RSAKeyValue><Modulus>mfgthqgvK5P6kP00ojzA68+tGMwjEacduojFSukazKPXrZ8Q5XjzfqgJmDQ3wcWe3hWK92O3z/tmAuN47KA0ZQ==</Modulus><Exponent>AQAB</Exponent><P>8VCRao0hZmIv5gVGFLqOD/7n6TQKlekA96U1QVzimKM=</P><Q>o1bchWA5ddDd59FED37QcrakoTXNoxRspFtsLDKEp1c=</Q><DP>ugF0VUE7wYNlkFP4VPoHjuTZNbRbhHn5uOmrRxqlvyk=</DP><DQ>XoGggC9Hr8pUyo9DIPAP7X+Ny5TU0Vm87m/TK9Ni+2s=</DQ><InverseQ>YqOHEP8dgCvz5Q8nhpQgdrKfdlmjkNAFxKF7j3pm09I=</InverseQ><D>mCpGy/rxS08e5iXn26LRQvvm5UfyLKMNZWmAGk2QF8cRGFB7dds/SI9wGTC9xyOoF4N2kWzYdLx+dYbR9lqwbQ==</D></RSAKeyValue>";
                var publicKey = "<RSAKeyValue><Modulus>mfgthqgvK5P6kP00ojzA68+tGMwjEacduojFSukazKPXrZ8Q5XjzfqgJmDQ3wcWe3hWK92O3z/tmAuN47KA0ZQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
                
                // Create a new instance of the RSACryptoServiceProvider class
                RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(512);
                RSAalg.PersistKeyInCsp = false;
                RSAalg.FromXmlString(privateKey);
                RSAParameters rsaParameters = RSAalg.ExportParameters(true);
    
                String signedData = SignData(message, rsaParameters);
                Console.WriteLine("signedData: " + signedData);
    
                // verify with xml-public key
                RSAalg.FromXmlString(publicKey);
                rsaParameters = RSAalg.ExportParameters(false);
                bool verifiedData = VerifyData(message, signedData, rsaParameters);
                
                // Verify the data and display the result to the
                // console.
                if (VerifyData(message, signedData, rsaParameters)) {
                    Console.WriteLine("The data was verified.");
                }
                else {
                    Console.WriteLine("The data does not match the signature.");
                }
            }
            catch(ArgumentNullException) {
                Console.WriteLine("The data was not signed or verified");
            }
        }
        
        static string SignData(string message, RSAParameters privateKey)
        {
            byte[] signedBytes;
            using (var rsa = new RSACryptoServiceProvider())
            {
                var encoder = new UTF8Encoding();
                byte[] originalData = encoder.GetBytes(message);
                    rsa.ImportParameters(privateKey);
                    signedBytes = rsa.SignData(originalData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
                    rsa.PersistKeyInCsp = false;
            }
            return Convert.ToBase64String(signedBytes);
        }
        
        public static bool VerifyData(string message, string signedData, RSAParameters rsaParameters) 
        {
            byte[] messageBytes;
            byte[] signedBytes;
            using (var rsa = new RSACryptoServiceProvider())
            try
            {
                var encoder = new UTF8Encoding();
                messageBytes = encoder.GetBytes(message);
                signedBytes = Convert.FromBase64String(signedData);
                rsa.ImportParameters(rsaParameters);
                return rsa.VerifyData(messageBytes, new SHA1CryptoServiceProvider(), signedBytes);
            }
            catch(CryptographicException e) {
                Console.WriteLine(e.Message);
    
                return false;
            }
        }
    }
    

    Java 代码:

    import java.nio.charset.StandardCharsets;
    import java.security.*;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Base64;
    
    public class MainSha1 {
        public static void main(String[] args) throws GeneralSecurityException {
            System.out.println("Should I use MessageDigest to verify a digital signature that signed in C#?");
            String message = "this is the important message to sign";
            // this is a SAMPLE and UNSECURE RSA 512 bit key
            String publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
                    "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJn4LYaoLyuT+pD9NKI8wOvPrRjMIxGn\n" +
                    "HbqIxUrpGsyj162fEOV4836oCZg0N8HFnt4Vivdjt8/7ZgLjeOygNGUCAwEAAQ==\n" +
                    "-----END PUBLIC KEY-----";
            String signedData = "HS4qvrXpqu97me7yDt9lWXp+QLjKMO8FY4kiUiGhMhi6KmXQXCtmcUWSbg0i+LXv7u5ueRiQNeBnu6UCbPhZLg==";
            String rsaInstanceString = "SHA1withRSA";
            System.out.println("RSA instance: " + rsaInstanceString);
            PublicKey publicKey = getPublicKeyFromString(publicKeyPem);
            boolean verifyData = verifyRsa(publicKey, rsaInstanceString, message.getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(signedData));
            if (verifyData = true) {
                System.out.println("The data was verified.");
            } else {
                System.out.println("The data could NOT get verified.");
            }
    
        }
    
        public static PublicKey getPublicKeyFromString(String key) throws GeneralSecurityException {
            String publicKeyPEM = key;
            publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----", "");
            publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
            publicKeyPEM = publicKeyPEM.replaceAll("[\\r\\n]+", "");
            byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PublicKey pubKey = (PublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
            return pubKey;
        }
    
        public static Boolean verifyRsa(PublicKey publicKey, String rsaInstanceString, byte[] messageByte,
                                        byte[] signatureByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
            Signature publicSignature = Signature.getInstance(rsaInstanceString);
            publicSignature.initVerify(publicKey);
            publicSignature.update(messageByte);
            return publicSignature.verify(signatureByte);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-14
      • 2011-09-22
      • 2014-12-10
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      • 2020-03-23
      • 2014-04-19
      相关资源
      最近更新 更多