簡體   English   中英

我應該使用 MessageDigest 來驗證在 C# 中簽名的數字簽名嗎?

[英]Should I use MessageDigest to verify a digital signature that signed in C#?

我是 Java 新手,想在 C# 中簽署一些數據,並使用 RSA 和 SHA512 在 Java 中進行驗證。

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);
    }

爪哇:

 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# 中,您使用的是 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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM