簡介
加解密現狀,編寫此係列文章的背景:
- 需要考慮系統環境相容性問題(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");
}
下期預告
下一篇將介紹對稱加密演算法,敬請期待。。。