.NET Core加解密实战系列之——消息摘要与数字签名算法
简介
加解密现状,编写此系列文章的背景:
- 需要考虑系统环境兼容性问题(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();
}
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))}");
}
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)}");
}
数字签名算法
数字签名是一个带有密钥的消息摘要算法,这个密钥包括了公钥和私钥,用于验证数据完整性、认证数据来源和抗否认,遵循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();
}
数字签名算法(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");
}
椭圆曲线数字签名算法(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");
}
下期预告
下一篇将介绍对称加密算法,敬请期待。。。
.NET Core加解密实战系列之——消息摘要与数字签名算法的更多相关文章
- .NET Core加解密实战系列之——对称加密算法
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- .NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- .NET Core加解密实战系列之——RSA非对称加密算法
目录 简介 功能依赖 生成RSA秘钥 PKCS1格式 PKCS8格式 私钥操作 PKCS1与PKCS8格式互转 PKCS1与PKCS8私钥中提取公钥 PEM操作 PEM格式密钥读取 PEM格式密钥写入 ...
- 10.Java 加解密技术系列之 DH
Java 加解密技术系列之 DH 序 概念 原理 代码实现 结果 结束语 序 上一篇文章中简单的介绍了一种非对称加密算法 — — RSA,今天这篇文章,继续介绍另一种非对称加密算法 — — DH.当然 ...
- 8.Java 加解密技术系列之 PBE
Java 加解密技术系列之 PBE 序 概念 原理 代码实现 结束语 序 前 边的几篇文章,已经讲了几个对称加密的算法了,今天这篇文章再介绍最后一种对称加密算法 — — PBE,这种加密算法,对我的认 ...
- 7.java 加解密技术系列之 AES
java 加解密技术系列之 AES 序 概念 原理 应用 代码实现 结束语 序 这篇文章继续介绍对称加密算法,至于今天的主角,不用说,也是个厉害的角色 — — AES.AES 的出现,就是为了来替代原 ...
- 4.Java 加解密技术系列之 HMAC
Java 加解密技术系列之 HMAC 序 背景 正文 代码 结束语 序 上一篇文章中简单的介绍了第二种单向加密算法 — —SHA,同时也给出了 SHA-1 的 Java 代码.有这方面需求的童鞋可以去 ...
- 3.Java 加解密技术系列之 SHA
Java 加解密技术系列之 SHA 序 背景 正文 SHA-1 与 MD5 的比较 代码实现 结束语 序 上一篇文章中介绍了基本的单向加密算法 — — MD5,也大致的说了说它实现的原理.这篇文章继续 ...
- 2.Java 加解密技术系列之 MD5
Java 加解密技术系列之 MD5 序 背景 正文 结束语 序 上一篇文章中,介绍了最基础的编码方式 — — BASE64,也简单的提了一下编码的原理.这篇文章继续加解密的系列,当然也是介绍比较基础的 ...
随机推荐
- 深入理解JVM(③)——之HotSpot虚拟机对象探秘
前言 上篇文章介绍了Java虚拟机的运行时数据区域,大致明白了Java虚拟机内存模型的概况,下面就基于实用优先的原则,以最常用的虚拟机HotSpot和最常用的内存区域Java堆为例,升入探讨一下Hot ...
- 【JVM】垃圾回收器总结(2)——七种垃圾回收器类型
七种垃圾回收器类型 GC的约定参数 DefNew——Default New Generation Tenured——Serial Old ParNew——Parallel New Generation ...
- Java实现 蓝桥杯 算法提高 求arccos值
算法提高 7-2求arccos值 时间限制:10.0s 内存限制:256.0MB 提交此题 问题描述 利用标准库中的cos(x)和fabs(x)函数实现arccos(x)函数,x取值范围是[-1, 1 ...
- Java实现 蓝桥杯VIP 算法训练 连接字符串
算法训练 连接字符串 时间限制:1.0s 内存限制:512.0MB 编程将两个字符串连接起来.例如country与side相连接成为countryside. 输入两行,每行一个字符串(只包含小写字母, ...
- Java实现 LeetCode 9 回文数
9. 回文数 判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121 输出: true 示例 2: 输入: -121 输出: false ...
- 基于Azure IoT开发.NET物联网应用系列-全新的Azure IoT架构
物联网技术已经火了很多年了,业界各大厂商都有各自成熟的解决方案.我们公司主要搞新能源汽车充电,充电桩就是物联网技术的最大应用,车联网.物联网.互联网三网合一.2017年的时候重点研究过Azure Io ...
- Java中的堆和栈
Java中的堆和栈 栈内存 存放基本数据类型和引用变量 堆内存 存放运行时创建的对象 一般来说,通过new关键字创建出来的对象都放在堆内存中 由于JVM是基于堆栈的虚拟机,而每个Java程序都运行在一 ...
- 如何在Linux上安装Redis(内附详细教程)
前言 hello,好久不见,又断更了一段时间.同事大部分离职了,但是活还是一样,所以只能硬着头皮顶上.现在总算歇会了,决定开启Redis源码系列,希望不要啪啪啪打脸. 什么是redis? Redi ...
- arduino 的analogRead() 和analogWrite()
模拟输入analogRead()函数的返回值范围是0 到1023; 而模拟输出analogWrite()函数的输出值范围是0 到255; 所以: val = analogRead(potpin); / ...
- java 中有几种类型的流?
字节流,字符流. 字节流继承于 InputStream \ OutputStream, 字符流继承于 InputStreamReader \ OutputStreamWriter. 在 java.io ...