使用SHA256WithRSA來簽名和驗籤(.NET/C#)

FrankYou發表於2018-02-02

RSACryptoServiceProvider does work with SHA2-based signatures, but you have to invest some effort into it.

When you use a certificate to get your RSACryptoServiceProvider it really matters what's the underlying CryptoAPI provider. By default, when you create a certificate with 'makecert', it's "RSA-FULL" which only supports SHA1 hashes for signature. You need the new "RSA-AES" one that supports SHA2.

So, you can create your certificate with an additional option: -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" (or an equivalent -sy 24) and then your code would look like (in .NET 4.0):

var rsa = signerCertificate.PrivateKey as RSACryptoServiceProvider;
byte[] signature = rsa.SignData(data, CryptoConfig.CreateFromName("SHA256"));

 

If you are unable to change the way your certificate is issued, there is a semi-ligitimate workaround that is based on the fact that by default RSACryptoServiceProvider is created with support for SHA2. So, the following code would also work, but it is a bit uglier: (what this code does is it creates a new RSACryptoServiceProvider and imports the keys from the one we got from the certificate)

public string Sign(string contentForSign,string priKeyFile, string keyPwd)
{
    var rsa = GetPrivateKey(priKeyFile,keyPwd);
    // Create a new RSACryptoServiceProvider
    var rsaClear = new RSACryptoServiceProvider();
    // Export RSA parameters from 'rsa' and import them into 'rsaClear'
    var paras = rsa.ExportParameters(true);
    rsaClear.ImportParameters(paras);
    using (var sha256 = new SHA256CryptoServiceProvider())
    {
       var signData = rsa.SignData(Encoding.UTF8.GetBytes(contentForSign), sha256);
       return BytesToHex(signData);
    }
}

public bool VerifySign(string contentForSign, string signedData,pubKeyFile)
{
    var  rsa = GetPublicKey(pubKeyFile);

    using (var sha256 = new SHA256CryptoServiceProvider())
    {
       return rsa.VerifyData(Encoding.UTF8.GetBytes(contentForSign), sha256, HexToBytes(signedData));
    }   
}

/// <summary>
/// 獲取簽名證書私鑰
/// </summary>
/// <param name="priKeyFile"></param>
/// <param name="keyPwd"></param>
/// <returns></returns>
private static RSACryptoServiceProvider GetPrivateKey(string priKeyFile, string keyPwd)
{
    var pc = new X509Certificate2(priKeyFile, keyPwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
    return (RSACryptoServiceProvider) pc.PrivateKey;
}

/// <summary>
/// 獲取驗簽證書
/// </summary>
/// <param name="pubKeyFile"></param>
/// <returns></returns>
private static RSACryptoServiceProvider GetPublicKey(string pubKeyFile)
{
    var pc = new X509Certificate2(pubKeyFile);
    return (RSACryptoServiceProvider) pc.PublicKey.Key;
}
  public static byte[] HexToBytes(string text)
        {
            if (text.Length % 2 != 0)
                throw new ArgumentException("text 長度為奇數。");

            List<byte> lstRet = new List<byte>();
            for (int i = 0; i < text.Length; i = i + 2)
            {
                lstRet.Add(Convert.ToByte(text.Substring(i, 2), 16));
            }
            return lstRet.ToArray();
        }

        /// <summary>
        /// bytes轉換hex
        /// </summary>
        /// <param name="data">bytes</param>
        /// <returns>轉換後的hex字串</returns>
        public static string BytesToHex(byte[] data)
        {
            StringBuilder sbRet = new StringBuilder(data.Length * 2);
            for (int i = 0; i < data.Length; i++)
            {
                sbRet.Append(Convert.ToString(data[i], 16).PadLeft(2, '0'));
            }
            return sbRet.ToString();
        }

 https://stackoverflow.com/questions/10673146/signature-with-sha256

相關文章