19

.NET Core加解密实战系列之——消息摘要与数字签名算法

 3 years ago
source link: http://www.cnblogs.com/fulu/p/13209066.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

YZneemq.png!web

目录

简介

加解密现状,编写此系列文章的背景:

  • 需要考虑系统环境兼容性问题(Linux、Windows)
  • 语言互通问题(如C#、Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题)
  • 网上资料版本不一、或不全面
  • .NET官方库密码算法提供不全面,很难针对其他语言(Java)进行适配

本系列文章主要介绍如何在 .NET Core 中使用非对称加密算法、编码算法、消息摘要算法、签名算法、对称加密算法、国密算法等一系列算法,如有错误之处,还请大家批评指正。

本系列文章旨在引导大家能快速、轻松的了解接入加解密,乃至自主组合搭配使用BouncyCastle密码术包中提供的算法。

本文中代码示例仅列举了比较常见的使用方式(BouncyCastle中提供的算法远不止这些),另外算法DSA和ECDSA我们未在项目中使用过,代码组合性非常多,因此文中部分代码仅供参考。

本系列代码项目地址: https://github.com/fuluteam/ICH.BouncyCastle.git

上一篇文章《.NET Core加解密实战系列之——RSA非对称加密算法》: https://www.cnblogs.com/fulu/p/13100471.html

功能依赖

BouncyCastle( https://www.bouncycastle.org/csharp) 是一个开放源码的轻量级密码术包;它支持大量的密码术算法,它提供了很多 .NET Core标准库没有的算法。

支持 .NET 4,.NET Standard 1.0-2.0,WP,Silverlight,MonoAndroid,Xamarin.iOS,.NET Core

功能 依赖 Portable.BouncyCastle Portable.BouncyCastle • 1.8.5

消息摘要算法

消息摘要算法分为三类:

  • MD 2/4/5(Message Digest Algorithm 2/4/5):消息摘要算法 MD2、MD4、MD5
  • SHA(Secure Hash Algorithm):安全散列算法
  • MAC(Message Authentication Code):消息认证码

MD算法

MD消息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

家族发展史

  • MD2算法:

    1989年,著名的非对称算法RSA发明人之一麻省理工学院教授罗纳德·李维斯特开发了MD2算法。这个算法首先对信息进行数据补位,使信息的字节长度是16的倍数。再以一个16位的检验和做为补充信息追加到原信息的末尾。最后根据这个新产生的信息计算出一个128位的散列值,MD2算法由此诞生。

  • MD4算法:

    1990年,罗纳德·李维斯特教授开发出较之MD2算法有着更高安全性的MD4算法。MD4算法对后续消息摘要算法起到了推动作用,许多比较有名的消息摘要算法都是在MD4算法的基础上发展而来的,如MD5、SHA-1、RIPE-MD和HAVAL算法等。

  • MD5算法:

    1991年,继MD4算法后,罗纳德·李维斯特教授开发了MD5算法,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。MD5算法经MD2、MD3和MD4算法发展而来,算法复杂程度和安全强度大大提高,MD算法的最终结果都是产生一个128位的信息摘要。这也是MD系列算法的特点。

    1996年,该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。

    2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。

应用场景

消息摘要算法是不可逆的,所以信息摘要场景主要被用来验证信息的完整性,防止信息被篡改,主要场景如下:

  • 验签:对要发送的数据做MD5(一般加slat)MD5值和数据一同发送,接收方接受数据做同样的MD5计算,比较MD5值是否一致
  • 密码保护:比如用户密码存储上,一般都是存储MD5值,更高一级的涉及是针对每个用户生成一个随机的slat,然后进MD5(passport + slat)计算,将这个值存储到DB中

代码实现

MD5

public static class MD5
{
    /// <summary>
    /// 哈希计算(使用BouncyCastle)
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static byte[] Compute(string s)
    {
        if (string.IsNullOrEmpty(s))
        {
            throw new ArgumentNullException(nameof(s));
        }
        var digest = new MD5Digest();
        var resBuf = new byte[digest.GetDigestSize()];
        var input = Encoding.UTF8.GetBytes(s);

        digest.BlockUpdate(input, 0, input.Length);
        digest.DoFinal(resBuf, 0);

        return resBuf;
    }

    /// <summary>
    /// 哈希计算(不使用BouncyCastle)
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static byte[] Compute2(string s)
    {
        if (string.IsNullOrEmpty(s))
        {
            throw new ArgumentNullException(nameof(s));
        }

        using (var md5 = System.Security.Cryptography.MD5.Create())
        {
            return md5.ComputeHash(Encoding.UTF8.GetBytes(s));
        }
    }
}

示例代码

private static void MD5Sample()
{
    var s = "hello md5";
    Console.WriteLine(s);

    var resBytes1 = MD5.Compute("hello md5");

    var resBytes2 = MD5.Compute2("hello md5");

    var a1 = BitConverter.ToString(resBytes1).Replace("-", "");
    Console.WriteLine($"通过BitConverter.ToString转换得到结果:{a1}");
    var a2 = Hex.ToHexString(resBytes1).ToUpper();
    Console.WriteLine($"通过Hex.ToHexString转换得到结果:{a2}");

    var a3 = Hex.ToHexString(resBytes2).ToUpper();

    Console.WriteLine($"不使用BouncyCastle得到结果:{a3}");

    Console.WriteLine();
}

VBNJNnE.jpg!web

SHA算法

安全散列算法(英文:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。

SHA家族的五个算法,分别是SHA-1、SHA-224、SHA-256、SHA-384,和SHA-512,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布;是美国的政府标准。后四者有时并称为SHA-2。SHA-1在许多安全协定中广为使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是MD5(更早之前被广为使用的杂凑函数)的后继者。但 SHA-1的安全性如今被密码学家严重质疑;虽然至今尚未出现对SHA-2有效的攻击,它的算法跟SHA-1基本上仍然相似;因此有些人开始发展其他替代的杂凑算法。

2017年2月23日,Google公司公告宣称他们与CWI Amsterdam合作共同创建了两个有着相同的SHA-1值但内容不同的PDF文件,这代表 SHA-1算法已被正式攻破

应用场景

目前SHA1的应用较为广泛,主要应用于CA和数字证书中,另外在目前互联网中流行的BT软件中,也是使用SHA1来进行文件校验的。

代码实现

SHA1

public class SHA1
{
    /// <summary>
    /// 哈希计算(使用BouncyCastle)
    /// </summary>
    public static byte[] Compute(string s)
    {
        if (string.IsNullOrEmpty(s))
        {
            throw new ArgumentNullException(nameof(s));
        }

        var digest = new Sha1Digest();
        var resBuf = new byte[digest.GetDigestSize()];
        var input = Encoding.UTF8.GetBytes(s);

        digest.BlockUpdate(input, 0, input.Length);
        digest.DoFinal(resBuf, 0);

        return resBuf;
    }

    /// <summary>
    /// 哈希计算(不使用BouncyCastle)
    /// </summary>
    public static byte[] Compute2(string s)
    {
        if (string.IsNullOrEmpty(s))
        {
            throw new ArgumentNullException(nameof(s));
        }

        using (var sha1 = System.Security.Cryptography.SHA1.Create())
        {
            return sha1.ComputeHash(Encoding.UTF8.GetBytes(s));
        }
    }
}

SHA256

public class SHA256
{
    /// <summary>
    /// 哈希计算(使用BouncyCastle)
    /// </summary>
    public static byte[] Compute1(string s)
    {
        if (string.IsNullOrEmpty(s))
        {
            throw new ArgumentNullException(nameof(s));
        }
        var digest = new Sha256Digest();
        var resBuf = new byte[digest.GetDigestSize()];
        var input = Encoding.UTF8.GetBytes(s);

        digest.BlockUpdate(input, 0, input.Length);
        digest.DoFinal(resBuf, 0);

        return resBuf;
    }

    /// <summary>
    /// 哈希计算(不使用BouncyCastle)
    /// </summary>
    public static byte[] Compute2(string s)
    {
        if (string.IsNullOrEmpty(s))
        {
            throw new ArgumentNullException(nameof(s));
        }

        using (var sha256 = System.Security.Cryptography.SHA256.Create())
        {
            return sha256.ComputeHash(Encoding.UTF8.GetBytes(s));
        }
    }
}

示例代码

private static void SHA256Sample()
{
    var s = "hello sha-256";
    Console.WriteLine(s);

    Console.WriteLine($"使用BouncyCastle计算结果(转Base64字符串):{Base64.ToBase64String(SHA256.Compute1(s))}");

    Console.WriteLine($"不使用BouncyCastle计算结果(转Base64字符串):{Base64.ToBase64String(SHA256.Compute2(s))}");
}

YbMJ3yy.png!web

MAC算法

消息认证码算法(英文:Message Authentication Codes,缩写为MAC) 含有密钥的散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加上了密钥。因此MAC算法也经常被称作HMAC算法。消息的散列值由只有通信双方知道的密钥来控制。此时Hash值称作MAC。

HMAC是密钥相关的散列运算消息认证码(Hash-based Message Authentication Code)的缩写,由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一种基于Hash函数和密钥进行消息认证的方法,并于1997年作为RFC2104被公布,并在IPSec和其他网络协议(如SSL)中得以广泛应用,现在已经成为事实上的Internet安全标准。它可以与任何迭代散列函数捆绑使用。

HMAC算法的典型应用

HMAC算法的一个典型应用是用在“挑战/响应”(Challenge/Response)身份认证中,认证流程如下:

(1) 先由客户端向服务器发出一个验证请求。
(2) 服务器接到此请求后生成一个随机数并通过网络传输给客户端(此为挑战)。
(3) 客户端将收到的随机数与自己的密钥进行HMAC-SHA1运算并得到一个结果作为认证证据传给服务器(此为响应)。
(4) 与此同时,服务器也使用该随机数与存储在服务器数据库中的该客户密钥进行HMAC-SHA1运算,如果服务器的运算结果与客户端传回的响应结果相同,则认为客户端是一个合法用户 。

HMAC算法的安全性

HMAC算法引入了密钥,其安全性已经不完全依赖于所使用的HASH算法,安全性主要有以下几点保证:

(1) 使用的密钥是双方事先约定的,第三方不可能知道。由上面介绍应用流程可以看出,作为非法截获信息的第三方,能够得到的信息只有作为“挑战”的随机数和作为“响应”的HMAC结果,无法根据这两个数据推算出密钥。由于不知道密钥,所以无法仿造出一致的响应。
(2)在HMAC算法的应用中,第三方不可能事先知道输出(如果知道,不用构造输入,直接将输出送给服务器即可)。
(3) HMAC算法与一般的加密重要的区别在于它具有“瞬时”性,即认证只在当时有效,而加密算法被破解后,以前的加密结果就可能被解密。

HMAC组合散列函数

与HMAC组合使用的算法有HMac-MD5、HMac-SHA1、HMac-SHA256等等:

public class Algorithms
{
    public const string HMacSHA1="HMAC-SHA1";
    public const string HMacMD5="HMAC-MD5";
    public const string HMacMD4="HMAC-MD4";
    public const string HMacMD2="HMAC-MD2";
    public const string HMacSHA224="HMAC-SHA224";
    public const string HMacSHA256="HMAC-SHA256";
    public const string HMacSHA384="HMAC-SHA384";
    public const string HMacSHA512_224 = "HMAC-SHA512/224";
    public const string HMacSHA512_256="HMAC-SHA512/256";
    public const string HMacRIPEMD128="HMAC-RIPEMD128";
    public const string HMacRIPEMD160="HMAC-RIPEMD160";
    public const string HMacTIGER="HMAC-TIGER";
    public const string HMacKECCAK224="HMAC-KECCAK224";
    public const string HMacKECCAK256="HMAC-KECCAK256";
    public const string HMacKECCAK288="HMAC-KECCAK288";
    public const string HMacKECCAK384="HMAC-KECCAK384";
    public const string HMacSHA3512="HMAC-SHA3-512";
    public const string HMacGOST3411_2012256="HMAC-GOST3411-2012-256";
    public const string HMacGOST3411_2012_512="HMAC-GOST3411-2012-512";
    
    ......
}

代码实现

public class HMAC
{
    /// <summary>
    /// 生成密钥KEY
    /// </summary>
    /// <param name="algorithm">密文算法,参考Algorithms.cs中提供的HMac algorithm</param>
    /// <returns>密钥KEY</returns>
    public static byte[] GeneratorKey(string algorithm)
    {
        var kGen = GeneratorUtilities.GetKeyGenerator(algorithm);
        return kGen.GenerateKey();
    }

    /// <summary>
    /// 哈希计算
    /// </summary>
    /// <param name="data">输入字符串</param>
    /// <param name="key">密钥KEY</param>
    /// <param name="algorithm">密文算法,参考Algorithms.cs中提供的HMac algorithm</param>
    /// <returns>哈希值</returns>
    public static byte[] Compute(string data, byte[] key, string algorithm)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        var keyParameter = new Org.BouncyCastle.Crypto.Parameters.KeyParameter(key);
        var input = Encoding.UTF8.GetBytes(data);
        var mac = MacUtilities.GetMac(algorithm);
        mac.Init(keyParameter);
        mac.BlockUpdate(input, 0, input.Length);
        return MacUtilities.DoFinal(mac);
    }

    /// <summary>
    /// 哈希计算
    /// </summary>
    /// <param name="data">输入字符串</param>
    /// <param name="key">密钥KEY</param>
    /// <param name="digest"></param>
    /// <returns>哈希值</returns>
    public static byte[] Compute(string data, byte[] key, IDigest digest)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        var keyParameter = new Org.BouncyCastle.Crypto.Parameters.KeyParameter(key);
        var input = Encoding.UTF8.GetBytes(data);
        IMac mac = new Org.BouncyCastle.Crypto.Macs.HMac(digest);
        mac.Init(keyParameter);
        mac.BlockUpdate(input, 0, input.Length);
        return MacUtilities.DoFinal(mac);
    }
}

HMAC-MD5

public class HMACMD5
{
    /// <summary>
    /// 生成密钥KEY
    /// </summary>
    public static byte[] GeneratorKey()
    {
        return HMAC.GeneratorKey(Algorithms.HMacMD5);
    }

    /// <summary>
    /// 哈希计算(使用BouncyCastle)
    /// </summary>
    public static byte[] Compute(string data, byte[] key)
    {
        return HMAC.Compute(data, key, Algorithms.HMacMD5);
        //or
        //return HMAC.Compute(data, key, new MD5Digest());
    }

    /// <summary>
    /// 哈希计算(不使用BouncyCastle)
    /// </summary>
    public static byte[] Compute2(string data, string key)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentNullException(nameof(key));
        }

        using (var hmacMd5 = new System.Security.Cryptography.HMACMD5(Encoding.UTF8.GetBytes(key)))
        {
            return hmacMd5.ComputeHash(Encoding.UTF8.GetBytes(data));
        }
    }
}

HMAC-SHA1

public class HMACSHA1
{
    /// <summary>
    /// 生成密钥KEY
    /// </summary>
    /// <returns></returns>
    public static byte[] GeneratorKey()
    {
        return HMAC.GeneratorKey(Algorithms.HMacSHA1);
    }

    /// <summary>
    /// 哈希计算(使用BouncyCastle)
    /// </summary>
    public static byte[] Compute(string data, byte[] key)
    {
        return HMAC.Compute(data, key, Algorithms.HMacSHA1);
        //or
        //return HMAC.Compute(data, key, new Sha1Digest());
    }

    /// <summary>
    /// 哈希计算(不使用BouncyCastle)
    /// </summary>
    public static byte[] Compute2(string data, byte[] key)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        using (var hmacSha1 = new System.Security.Cryptography.HMACSHA1(key))
        {
            return hmacSha1.ComputeHash(Encoding.UTF8.GetBytes(data));
        }
    }
}

HMAC-SHA256

public class HMACSHA256
{
    /// <summary>
    /// 生成签名
    /// </summary>
    public static byte[] GeneratorKey()
    {
        return HMAC.GeneratorKey(Algorithms.HMacSHA256);
    }

    /// <summary>
    /// 哈希计算(使用BouncyCastle)
    /// </summary>
    public static byte[] Compute(string data, byte[] key)
    {

        return HMAC.Compute(data, key, Algorithms.HMacSHA256);

        //or
        //return HMAC.Compute(data, key, new Sha256Digest());
    }

    /// <summary>
    /// 哈希计算(不使用BouncyCastle)
    /// </summary>
    public static byte[] Compute2(string data, byte[] key)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        using (var hmacSha256 = new System.Security.Cryptography.HMACSHA256(key))
        {
            return hmacSha256.ComputeHash(Encoding.UTF8.GetBytes(data));
        }
    }
}

示例代码

private static void HMacSha256Sample()
{
    var s = "hello hmac sha256";
    Console.WriteLine(s);
    var k = HMACSHA256.GeneratorKey();
    Console.WriteLine($"密钥(十六进制字符串):{Hex.ToHexString(k)}");
    Console.WriteLine($"密钥(Base64字符串):{Base64.ToBase64String(k)}");

    var b1 = HMACSHA256.Compute(s, k);
    Console.WriteLine($"使用BouncyCastle计算结果(转Base64字符串):{Base64.ToBase64String(b1)}");
    Console.WriteLine($"使用BouncyCastle计算结果(转十六进制字符串):{Hex.ToHexString(b1)}");

    var b2 = HMACSHA256.Compute2(s, k);

    Console.WriteLine($"不使用BouncyCastle计算结果(转Base64字符串):{Base64.ToBase64String(b2)}");
    Console.WriteLine($"不使用BouncyCastle计算结果(转十六进制字符串):{Hex.ToHexString(b2)}");
}

QvuIreN.png!web

数字签名算法

数字签名是一个带有密钥的消息摘要算法,这个密钥包括了公钥和私钥,用于验证数据完整性、认证数据来源和抗否认,遵循OSI参考模型、私钥签名和公钥验证。也是非对称加密算法和消息摘要算法的结合体

FIPS 186-4规定了三种可用于数据保护的数字签名生成和验证技术:数字签名算法(DSA),椭圆曲线数字签名算法(ECDSA)和Rivest-Shamir Adelman算法( RSA)。

Rivest-Shamir Adelman算法( RSA)

RSA算法是1977年由罗纳德·里维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

RSA是目前计算机密码学中最经典算法,也是目前为止使用最广泛的数字签名算法,从提出到现在已近三十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。

代码实现

SHA1WithRSA

public class SHA1WithRSA
{
    /// <summary>
    /// 生成签名
    /// </summary>
    public static string GenerateSignature(string data, RSAParameters privateKey)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        using (var rsa = System.Security.Cryptography.RSA.Create())
        {
            rsa.ImportParameters(privateKey);
            return Base64.ToBase64String(rsa.SignData(Encoding.UTF8.GetBytes(data), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1));
        }
    }

    /// <summary>
    /// 验证签名
    /// </summary>
    public static bool VerifySignature(string data, string sign, RSAParameters publicKey)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        if (string.IsNullOrEmpty(sign))
        {
            throw new ArgumentNullException(nameof(sign));
        }

        using (var rsa = System.Security.Cryptography.RSA.Create())
        {
            rsa.ImportParameters(publicKey);
            return rsa.VerifyData(Encoding.UTF8.GetBytes(data), Base64.Decode(sign), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
        }
    }

    /// <summary>
    /// 生成签名
    /// </summary>
    public static string GenerateSignature(string data, AsymmetricKeyParameter privateKey)
    {
        var byteData = Encoding.UTF8.GetBytes(data);
        var normalSig = SignerUtilities.GetSigner("SHA1WithRSA");
        normalSig.Init(true, privateKey);
        normalSig.BlockUpdate(byteData, 0, data.Length);
        var normalResult = normalSig.GenerateSignature();
        return Base64.ToBase64String(normalResult);
    }

    /// <summary>
    /// 验证签名
    /// </summary>
    public static bool VerifySignature(string data, string sign, AsymmetricKeyParameter publicKey)
    {
        var signBytes = Base64.Decode(sign);
        var plainBytes = Encoding.UTF8.GetBytes(data);
        var verifier = SignerUtilities.GetSigner("SHA1WithRSA");
        verifier.Init(false, publicKey);
        verifier.BlockUpdate(plainBytes, 0, plainBytes.Length);

        return verifier.VerifySignature(signBytes);
    }
}

SHA256WithRSA

public class SHA256WithRSA
{
    /// <summary>
    /// 生成签名
    /// </summary>
    public static string GenerateSignature(string data, RSAParameters privateKey)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        using (var rsa = System.Security.Cryptography.RSA.Create())
        {
            rsa.ImportParameters(privateKey);
            return Base64.ToBase64String(rsa.SignData(Encoding.UTF8.GetBytes(data), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
        }
    }

    /// <summary>
    /// 验证签名
    /// </summary>
    public static bool VerifySignature(string data, string sign, RSAParameters publicKey)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        if (string.IsNullOrEmpty(sign))
        {
            throw new ArgumentNullException(nameof(sign));
        }

        using (var rsa = System.Security.Cryptography.RSA.Create())
        {
            rsa.ImportParameters(publicKey);
            return rsa.VerifyData(Encoding.UTF8.GetBytes(data), Base64.Decode(sign), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
    }

    /// <summary>
    /// 生成签名
    /// </summary>
    public static string GenerateSignature(string data, AsymmetricKeyParameter parameter)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        if (parameter == null)
        {
            throw new ArgumentNullException(nameof(parameter));
        }

        var signer = SignerUtilities.GetSigner("SHA256WithRSA");
        signer.Init(true, parameter);
        var bytes = Encoding.UTF8.GetBytes(data);
        signer.BlockUpdate(bytes, 0, bytes.Length);
        return Base64.ToBase64String(signer.GenerateSignature());
    }

    /// <summary>
    /// 验证签名
    /// </summary>
    public static bool VerifySignature(string data, string sign, AsymmetricKeyParameter parameter)
    {
        if (string.IsNullOrEmpty(data))
        {
            throw new ArgumentNullException(nameof(data));
        }

        if (string.IsNullOrEmpty(sign))
        {
            throw new ArgumentNullException(nameof(sign));
        }

        if (parameter == null)
        {
            throw new ArgumentNullException(nameof(parameter));
        }

        var verifier = SignerUtilities.GetSigner("SHA256WithRSA");
        verifier.Init(false, parameter);
        var bytes = Encoding.UTF8.GetBytes(data);
        verifier.BlockUpdate(bytes, 0, bytes.Length);
        return verifier.VerifySignature(Base64.Decode(sign));
    }
}

示例代码

private static void SHA256WithRSA_Sample()
{
    var s = "hello sha256 with rsa";
    Console.WriteLine(s);

    var keyParameter = RSAKeyGenerator.Pkcs8(2048);

    Console.WriteLine("私钥:");
    Console.WriteLine(keyParameter.PrivateKey);
    Console.WriteLine("公钥:");
    Console.WriteLine(keyParameter.PublicKey);

    Console.WriteLine();

    Console.WriteLine("使用BouncyCastle:");

    var sign1 = SHA256WithRSA.GenerateSignature(s,
         RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(keyParameter.PrivateKey));
    Console.WriteLine("sign1:");
    Console.WriteLine(sign1);

    var verified1 = SHA256WithRSA.VerifySignature(s, sign1,
        RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(keyParameter.PublicKey));

    Console.WriteLine("验证结果:");
    Console.WriteLine(verified1 ? "signature verified" : "signature not verified");
    Console.WriteLine();

    Console.WriteLine("不使用BouncyCastle:");

    var sign2 = SHA256WithRSA.GenerateSignature(s,
        RSAUtilities.GetRsaParametersFormAsn1PrivateKey(keyParameter.PrivateKey));

    Console.WriteLine("sign2:");
    Console.WriteLine(sign2);

    var verified2 = SHA256WithRSA.VerifySignature(s, sign1,
        RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(keyParameter.PublicKey));

    Console.WriteLine("验证结果:");

    Console.WriteLine(verified2 ? "signature verified" : "signature not verified");
    Console.WriteLine();
}

3UJNzyF.png!web

数字签名算法(DSA)

Digital Signature Algorithm (DSA)是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。DSA是基于整数有限域离散对数难题的,其安全性与RSA相比差不多。

DSA(用于数字签名算法)的签名生成速度很快,验证速度很慢,加密时更慢,但解密时速度很快

目前,最好使用RSA 2048位密钥(也可以用4096位的RSA密钥),商业RSA证书比DSA证书被更广泛地部署。

OpenSSH 7.0及以上版本默认禁用了ssh-dss(DSA)公钥算法。官方没有给出具体的解释,但其中可能有OpenSSH,DSA密钥位数生成的原因,同时生成签名时随机性差,可能会泄漏私钥,且以现在机算机的算力,DSA 1024-bit已经实际上可破解,建议不使用。

一般来说,还是推荐大家使用RSA算法。

代码实现

SHA1/DSA

/// <summary>
/// 生成签名
/// </summary>
public static string GenerateSignature(string data, AsymmetricKeyParameter privateKey)
{
    var byteData = Encoding.UTF8.GetBytes(data);
    var normalSig = SignerUtilities.GetSigner("SHA1/DSA");
    normalSig.Init(true, privateKey);
    normalSig.BlockUpdate(byteData, 0, data.Length);
    var normalResult = normalSig.GenerateSignature();
    return Base64.ToBase64String(normalResult);
}

/// <summary>
/// 签名验证
/// </summary>
public static bool VerifySignature(string data, string sign, AsymmetricKeyParameter publicKey)
{
    var signBytes = Base64.Decode(sign);
    var plainBytes = Encoding.UTF8.GetBytes(data);
    var verifier = SignerUtilities.GetSigner("SHA1/DSA");
    verifier.Init(false, publicKey);
    verifier.BlockUpdate(plainBytes, 0, plainBytes.Length);

    return verifier.VerifySignature(signBytes);
}

SHA256/DSA

/// <summary>
/// 生成签名
/// </summary>
public static string GenerateSignature(string data, AsymmetricKeyParameter parameter)
{
    if (string.IsNullOrEmpty(data))
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (parameter == null)
    {
        throw new ArgumentNullException(nameof(parameter));
    }

    var signer = SignerUtilities.GetSigner("SHA256/DSA");
    signer.Init(true, parameter);
    var bytes = Encoding.UTF8.GetBytes(data);
    signer.BlockUpdate(bytes, 0, bytes.Length);
    return Base64.ToBase64String(signer.GenerateSignature());
}

/// <summary>
/// 验证签名
/// </summary>
public static bool VerifySignature(string data, string sign, AsymmetricKeyParameter parameter)
{
    if (string.IsNullOrEmpty(data))
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (string.IsNullOrEmpty(sign))
    {
        throw new ArgumentNullException(nameof(sign));
    }

    if (parameter == null)
    {
        throw new ArgumentNullException(nameof(parameter));
    }

    var verifier = SignerUtilities.GetSigner("SHA256/DSA");
    verifier.Init(false, parameter);
    var bytes = Encoding.UTF8.GetBytes(data);
    verifier.BlockUpdate(bytes, 0, bytes.Length);
    return verifier.VerifySignature(Base64.Decode(sign));
}

示例代码

private static void SHA256WithDSA_Sample()
{
    var s = "hello dsa";
    Console.WriteLine(s);
    var keyParameter = DSAKeyGenerator.Generator();

    Console.WriteLine("私钥:");
    Console.WriteLine(keyParameter.PrivateKey);
    Console.WriteLine("公钥:");
    Console.WriteLine(keyParameter.PublicKey);

    Console.WriteLine();

    var sign = SHA256WithDSA.GenerateSignature(s,
        RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(keyParameter.PrivateKey));

    Console.WriteLine($"sign:{sign}");

    var verified = SHA256WithDSA.VerifySignature(s, sign,
        RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(keyParameter.PublicKey));

    Console.WriteLine("验证结果:");
    Console.WriteLine(verified ? "signature verified" : "signature not verified");
}

ZJnq2eU.png!web

椭圆曲线数字签名算法(ECDSA)

椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。ECDSA于1999年成为ANSI标准,并于2000年成为IEEE和NIST标准。它在1998年既已为ISO所接受,并且包含它的其他一些标准亦在ISO的考虑之中。与普通的离散对数问题(discrete logarithm problem DLP)和大数分解问题(integer factorization problem IFP)不同,椭圆曲线离散对数问题(elliptic curve discrete logarithm problem ECDLP)没有亚指数时间的解决方法。因此椭圆曲线密码的单位比特强度要高于其他公钥体制。

ECDSA是ECC与DSA的结合,整个签名过程与DSA类似,所不一样的是签名中采取的算法为ECC

ECC与RSA 相比,有以下的优点:

  • 相同密钥长度下,安全性能更高,如160位ECC已经与1024位RSA、DSA有相同的安全强度。
  • 计算量小,处理速度快,在私钥的处理速度上(解密和签名),ECC远 比RSA、DSA快得多。
  • 存储空间占用小 ECC的密钥尺寸和系统参数与RSA、DSA相比要小得多, 所以占用的存储空间小得多。
  • 带宽要求低使得ECC具有广泛得应用前景。

代码实现

SHA1/ECDSA

/// <summary>
/// 生成签名
/// </summary>
public static string GenerateSignature(string data, AsymmetricKeyParameter parameter)
{
    if (string.IsNullOrEmpty(data))
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (parameter == null)
    {
        throw new ArgumentNullException(nameof(parameter));
    }

    var signer = SignerUtilities.GetSigner("SHA1/ECDSA");
    signer.Init(true, parameter);
    var bytes = Encoding.UTF8.GetBytes(data);
    signer.BlockUpdate(bytes, 0, bytes.Length);
    return Base64.ToBase64String(signer.GenerateSignature());
}

/// <summary>
/// 验证签名
/// </summary>
public static bool VerifySignature(string data, string sign, AsymmetricKeyParameter parameter)
{
    if (string.IsNullOrEmpty(data))
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (string.IsNullOrEmpty(sign))
    {
        throw new ArgumentNullException(nameof(sign));
    }

    if (parameter == null)
    {
        throw new ArgumentNullException(nameof(parameter));
    }

    var verifier = SignerUtilities.GetSigner("SHA1/ECDSA");
    verifier.Init(false, parameter);
    var bytes = Encoding.UTF8.GetBytes(data);
    verifier.BlockUpdate(bytes, 0, bytes.Length);
    return verifier.VerifySignature(Base64.Decode(sign));
}

SHA256/ECDSA

/// <summary>
/// 生成签名
/// </summary>
public static string GenerateSignature(string data, AsymmetricKeyParameter parameter)
{
    if (string.IsNullOrEmpty(data))
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (parameter == null)
    {
        throw new ArgumentNullException(nameof(parameter));
    }

    var signer = SignerUtilities.GetSigner("SHA256/ECDSA");
    signer.Init(true, parameter);
    var bytes = Encoding.UTF8.GetBytes(data);
    signer.BlockUpdate(bytes, 0, bytes.Length);
    return Base64.ToBase64String(signer.GenerateSignature());
}

/// <summary>
/// 验证签名
/// </summary>
public static bool VerifySignature(string data, string sign, AsymmetricKeyParameter parameter)
{
    if (string.IsNullOrEmpty(data))
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (string.IsNullOrEmpty(sign))
    {
        throw new ArgumentNullException(nameof(sign));
    }

    if (parameter == null)
    {
        throw new ArgumentNullException(nameof(parameter));
    }

    var verifier = SignerUtilities.GetSigner("SHA256/ECDSA");
    verifier.Init(false, parameter);
    var bytes = Encoding.UTF8.GetBytes(data);
    verifier.BlockUpdate(bytes, 0, bytes.Length);
    return verifier.VerifySignature(Base64.Decode(sign));
}

示例代码

private static void SHA256WithECDSA_Sample()
{
    var s = "hello ec dsa";
    Console.WriteLine(s);
    var keyParameter = ECDSAKeyGenerator.Generator();

    Console.WriteLine("私钥:");
    Console.WriteLine(keyParameter.PrivateKey);
    Console.WriteLine("公钥:");
    Console.WriteLine(keyParameter.PublicKey);

    var sign = SHA256WithECDSA.GenerateSignature(s,
        RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(keyParameter.PrivateKey));

    Console.WriteLine($"sign:{sign}");

    var verified = SHA256WithECDSA.VerifySignature(s, sign,
        RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(keyParameter.PublicKey));

    Console.WriteLine("验证结果:");
    Console.WriteLine(verified ? "signature verified" : "signature not verified");
}

IVbyy2y.png!web

下期预告

下一篇将介绍对称加密算法,敬请期待。。。

3aqYziQ.png!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK