1. 程式人生 > >.NET Core加解密實戰系列之——RSA非對稱加密演算法

.NET Core加解密實戰系列之——RSA非對稱加密演算法

![](https://img2020.cnblogs.com/blog/2029875/202006/2029875-20200612165254059-1754283874.png) --- [TOC] ## 簡介 加解密現狀,編寫此專案的背景: - 需要考慮系統環境相容性問題(Linux、Windows) - 語言互通問題(如C#、Java) - 網上資料版本不一、不全面 - .NET官方庫密碼演算法提供不全面,很難針對其他語言(Java)進行適配 本系列文章主要介紹如何結合BouncyCastle在 .NET Core 中使用非對稱加密演算法、編碼演算法、雜湊演算法、對稱加密演算法、國密演算法等一系列演算法,內容篇幅程式碼居多(加解密演算法相關的原理知識網上有很多,因此不過多介紹)。如有錯誤之處,還請大家批評指正。 本系列程式碼專案地址:https://github.com/fuluteam/ICH.BouncyCastle.git ## 功能依賴 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](https://www.nuget.org/packages/Portable.BouncyCastle/1.8.5) | ## 生成RSA祕鑰 ### PKCS1格式 ```csharp /// /// PKCS1(非Java適用) /// /// 金鑰長度”一般只是指模值的位長度。目前主流可選值:1024、2048、3072、4096... /// PEM格式 /// public RSAKeyParameter Pkcs1(int keySize, bool format=false) { var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA"); keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize)); var keyPair = keyGenerator.GenerateKeyPair(); var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public); var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private); if (!format) { return new RSAKeyParameter { PrivateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()), PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded()) }; } var rsaKey = new RSAKeyParameter(); using (var sw = new StringWriter()) { var pWrt = new PemWriter(sw); pWrt.WriteObject(keyPair.Private); pWrt.Writer.Close(); rsaKey.PrivateKey = sw.ToString(); } using (var sw = new StringWriter()) { var pWrt = new PemWriter(sw); pWrt.WriteObject(keyPair.Public); pWrt.Writer.Close(); rsaKey.PublicKey = sw.ToString(); } return rsaKey; } ``` ### PKCS8格式 ```csharp /// /// PKCS8(JAVA適用) ///
/// 金鑰長度”一般只是指模值的位長度。目前主流可選值:1024、2048、3072、4096... /// PEM格式 /// public RSAKeyParameter Pkcs8(int keySize, bool format=false) { var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA"); keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize)); var keyPair = keyGenerator.GenerateKeyPair(); var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public); var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private); if (!format) { return new RSAKeyParameter { PrivateKey = Base64.ToBase64String(privateKeyInfo.GetEncoded()), PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded()) }; } var rsaKey = new RSAKeyParameter(); using (var sw = new StringWriter()) { var pWrt = new PemWriter(sw); var pkcs8 = new Pkcs8Generator(keyPair.Private); pWrt.WriteObject(pkcs8); pWrt.Writer.Close(); rsaKey.PrivateKey = sw.ToString(); } using (var sw = new StringWriter()) { var pWrt = new PemWriter(sw); pWrt.WriteObject(keyPair.Public); pWrt.Writer.Close(); rsaKey.PublicKey = sw.ToString(); } return rsaKey; } ``` ## 私鑰操作 ### PKCS1與PKCS8格式互轉 ==僅私鑰有PKCS1和PKCS8之分,公鑰無格式區別。== ```csharp /// /// Pkcs1>>Pkcs8 ///
/// Pkcs1私鑰 /// 是否轉PEM格式 /// public static string PrivateKeyPkcs1ToPkcs8(string privateKey, bool format = false) { var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey); if (format) { var sw = new StringWriter(); var pWrt = new PemWriter(sw); var pkcs8 = new Pkcs8Generator(akp); pWrt.WriteObject(pkcs8); pWrt.Writer.Close(); return sw.ToString(); } else { var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp); return Base64.ToBase64String(privateKeyInfo.GetEncoded()); } } ``` ```csharp /// /// Pkcs8>>Pkcs1 ///
/// Pkcs8私鑰 /// 是否轉PEM格式 /// public static string PrivateKeyPkcs8ToPkcs1(string privateKey, bool format = false) { var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey); if (format) { var sw = new StringWriter(); var pWrt = new PemWriter(sw); pWrt.WriteObject(akp); pWrt.Writer.Close(); return sw.ToString(); } else { var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp); return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()); } } ``` ### PKCS1與PKCS8私鑰中提取公鑰 ```csharp /// /// 從Pkcs1私鑰中提取公鑰 /// /// Pkcs1私鑰 /// public static string GetPublicKeyFromPrivateKeyPkcs1(string privateKey) { var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey)); var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent); var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient); var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter); var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public); return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded()); } ``` ```csharp /// /// 從Pkcs8私鑰中提取公鑰 /// /// Pkcs8私鑰 /// public static string GetPublicKeyFromPrivateKeyPkcs8(string privateKey) { var privateKeyInfo = PrivateKeyInfo.GetInstance(Asn1Object.FromByteArray(Base64.Decode(privateKey))); privateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()); var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey)); var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent); var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient); var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter); var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public); return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded()); } ``` ## PEM操作 ### PEM格式金鑰讀取 ```csharp public static string ReadPkcs1PrivateKey(string text) { if (!text.StartsWith("-----BEGIN RSA PRIVATE KEY-----")) { return text; } using (var reader = new StringReader(text)) { var pr = new PemReader(reader); var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair; pr.Reader.Close(); var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair?.Private); return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()); } } public static string ReadPkcs8PrivateKey(string text) { if (!text.StartsWith("-----BEGIN PRIVATE KEY-----")) { return text; } using (var reader = new StringReader(text)) { var pr = new PemReader(reader); var akp = pr.ReadObject() as AsymmetricKeyParameter; ; pr.Reader.Close(); return Base64.ToBase64String(PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp).GetEncoded()); } } public static string ReadPublicKey(string text) { if (!text.StartsWith("-----BEGIN PUBLIC KEY-----")) { return text; } using (var reader = new StringReader(text)) { var pr = new PemReader(reader); var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair; pr.Reader.Close(); var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair?.Public); returnBase64.ToBase64String(subjectPublicKeyIno.GetEncoded()); } } ``` ### PEM格式金鑰寫入 ```csharp public static string WritePkcs1PrivateKey(string privateKey) { if (privateKey.StartsWith("-----BEGIN RSA PRIVATE KEY-----")) { return privateKey; } var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey); using (var sw = new StringWriter()) { var pWrt = new PemWriter(sw); pWrt.WriteObject(akp); pWrt.Writer.Close(); return sw.ToString(); } } public static string WritePkcs8PrivateKey(string privateKey) { if (privateKey.StartsWith("-----BEGIN PRIVATE KEY-----")) { return privateKey; } var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey); using (var sw = new StringWriter()) { var pWrt = new PemWriter(sw); var pkcs8 = new Pkcs8Generator(akp); pWrt.WriteObject(pkcs8); pWrt.Writer.Close(); return sw.ToString(); } } public static string WritePublicKey(string publicKey) { if (publicKey.StartsWith("-----BEGIN PUBLIC KEY-----")) { return publicKey; } var akp = RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(publicKey); using (var sw = new StringWriter()) { var pWrt = new PemWriter(sw); pWrt.WriteObject(akp); pWrt.Writer.Close(); return sw.ToString(); } } ``` ## RSA加解密 ### 獲取非對稱祕鑰引數(AsymmetricKeyParameter) ```csharp /// /// -----BEGIN RSA PRIVATE KEY----- /// ... /// -----END RSA PRIVATE KEY----- /// /// Pkcs1格式私鑰 /// public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPrivateKey(string privateKey) { if (string.IsNullOrEmpty(privateKey)) { throw new ArgumentNullException(nameof(privateKey)); } var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey)); return new RsaPrivateCrtKeyParameters(instance.Modulus, instance.PublicExponent, instance.PrivateExponent,instance.Prime1, instance.Prime2, instance.Exponent1, instance.Exponent2, instance.Coefficient); } /// /// -----BEGIN PRIVATE KEY----- /// ... /// -----END PRIVATE KEY----- /// /// Pkcs8格式私鑰 /// public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormAsn1PrivateKey(string privateKey) { return PrivateKeyFactory.CreateKey(Base64.Decode(privateKey)); } /// /// -----BEGIN PUBLIC KEY----- /// ... /// -----END PUBLIC KEY----- /// /// 公鑰 /// public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPublicKey(string publicKey) { if (string.IsNullOrEmpty(publicKey)) { throw new ArgumentNullException(nameof(publicKey)); } return PublicKeyFactory.CreateKey(Base64.Decode(publicKey)); } ``` ### RSA加解與解密 ```csharp /// /// RSA加密 /// /// 未加密資料位元組陣列 /// 非對稱金鑰引數 /// 密文演算法 /// 已加密資料位元組陣列 public static byte[] Encrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (string.IsNullOrEmpty(algorithm)) { throw new ArgumentNullException(nameof(algorithm)); } var bufferedCipher = CipherUtilities.GetCipher(algorithm); bufferedCipher.Init(true, parameters); return bufferedCipher.DoFinal(data); } /// /// RSA解密 /// /// 已加密資料位元組陣列 /// 非對稱金鑰引數 /// 密文演算法 /// 未加密資料位元組陣列 public static byte[] Decrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (string.IsNullOrEmpty(algorithm)) { throw new ArgumentNullException(nameof(algorithm)); } var bufferedCipher = CipherUtilities.GetCipher(algorithm); bufferedCipher.Init(false, parameters); return bufferedCipher.DoFinal(data); } /// /// RSA加密——Base64 /// /// 未加密字串 /// 非對稱金鑰引數 /// 密文演算法 /// 已加密Base64字串 public static string EncryptToBase64(string data, AsymmetricKeyParameter parameters, string algorithm) { return Base64.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm)); } /// /// RSA解密——Base64 /// /// 已加密Base64字串 /// 非對稱金鑰引數 /// 密文演算法 /// 未加密字串 public static string DecryptFromBase64(string data, AsymmetricKeyParameter parameters, string algorithm) { return Encoding.UTF8.GetString(Decrypt(Base64.Decode(data), parameters, algorithm)); } /// /// RSA加密——十六進位制 /// /// 未加密字串 /// 非對稱金鑰引數 /// 密文演算法 /// 已加密十六進位制字串 public static string EncryptToHex(string data, AsymmetricKeyParameter parameters, string algorithm) { return Hex.ToHexString(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm)); } /// /// RSA解密——十六進位制 /// /// 已加密十六進位制字串 /// 非對稱金鑰引數 /// 密文演算法 /// 未加密字串 public static string DecryptFromHex(string data, AsymmetricKeyParameter parameters, string algorithm) { return Encoding.UTF8.GetString(Decrypt(Hex.Decode(data), parameters, algorithm)); } ``` ### RSA密文演算法 ```csharp public const string RSA_NONE_NoPadding = "RSA/NONE/NoPadding"; public const string RSA_NONE_PKCS1Padding = "RSA/NONE/PKCS1Padding"; public const string RSA_NONE_OAEPPadding = "RSA/NONE/OAEPPadding"; public const string RSA_NONE_OAEPWithSHA1AndMGF1Padding = "RSA/NONE/OAEPWithSHA1AndMGF1Padding"; public const string RSA_NONE_OAEPWithSHA224AndMGF1Padding = "RSA/NONE/OAEPWithSHA224AndMGF1Padding"; public const string RSA_NONE_OAEPWithSHA256AndMGF1Padding = "RSA/NONE/OAEPWithSHA256AndMGF1Padding"; public const string RSA_NONE_OAEPWithSHA384AndMGF1Padding = "RSA/NONE/OAEPWithSHA384AndMGF1Padding"; public const string RSA_NONE_OAEPWithMD5AndMGF1Padding = "RSA/NONE/OAEPWithMD5AndMGF1Padding"; public const string RSA_ECB_NoPadding = "RSA/ECB/NoPadding"; public const string RSA_ECB_PKCS1Padding = "RSA/ECB/PKCS1Padding"; public const string RSA_ECB_OAEPPadding = "RSA/ECB/OAEPPadding"; public const string RSA_ECB_OAEPWithSHA1AndMGF1Padding = "RSA/ECB/OAEPWithSHA1AndMGF1Padding"; public const string RSA_ECB_OAEPWithSHA224AndMGF1Padding = "RSA/ECB/OAEPWithSHA224AndMGF1Padding"; public const string RSA_ECB_OAEPWithSHA256AndMGF1Padding = "RSA/ECB/OAEPWithSHA256AndMGF1Padding"; public const string RSA_ECB_OAEPWithSHA384AndMGF1Padding = "RSA/ECB/OAEPWithSHA384AndMGF1Padding"; public const string RSA_ECB_OAEPWithMD5AndMGF1Padding = "RSA/ECB/OAEPWithMD5AndMGF1Padding"; ...... ``` ## 編碼演算法 大家要明白,不管是對稱演算法還是非對稱演算法,其輸入與輸出均是位元組陣列,通常我們要結合編碼演算法對加密之後或解密之前的資料,進行編碼操作。 ### BouncyCastle提供的Base64編碼演算法 ```csharp namespace Org.BouncyCastle.Utilities.Encoders { public sealed class Base64 { // public static byte[] Decode(byte[] data); // public static byte[] Decode(string data); // public static int Decode(string data, Stream outStream); // public static byte[] Encode(byte[] data); // public static byte[] Encode(byte[] data, int off, int length); // public static int Encode(byte[] data, Stream outStream); // public static int Encode(byte[] data, int off, int length, Stream outStream); public static string ToBase64String(byte[] data); public static string ToBase64String(byte[] data, int off, int length); } } ``` ### BouncyCastle提供的Hex十六進位制編碼演算法 ```csharp namespace Org.BouncyCastle.Utilities.Encoders { // // 摘要: // Class to decode and encode Hex. public sealed class Hex { // public static byte[] Decode(byte[] data); // public static byte[] Decode(string data); // public static int Decode(string data, Stream outStream); // public static byte[] Encode(byte[] data); // public static byte[] Encode(byte[] data, int off, int length); // public static int Encode(byte[] data, Stream outStream); // public static int Encode(byte[] data, int off, int length, Stream outStream); public static string ToHexString(byte[] data); public static string ToHexString(byte[] data, int off, int length); } } ``` ## RSA加解密示例 ```csharp private static void RSA_ECB_PKCS1Padding() { var data = "hello rsa"; Console.WriteLine($"加密原文:{data}"); // rsa pkcs8 private key encrypt //algorithm rsa/ecb/pkcs1padding var pkcs8data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(pkcs8_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding); Console.WriteLine("金鑰格式:pkcs8,密文演算法:rsa/ecb/pkcs1padding,加密結果"); Console.WriteLine(pkcs8data); //rsa pkcs1 private key encrypt //algorithm rsa/ecb/pkcs1padding var pkcs1data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(pkcs1_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding); Console.WriteLine($"金鑰格式:pkcs1,密文演算法:rsa/ecb/pkcs1padding"); Console.WriteLine(pkcs1data); Console.WriteLine($"加密結果比對是否一致:{pkcs8data.Equals(pkcs1data)}"); var _1024_public_key = RSAKeyConverter.GetPublicKeyFromPrivateKeyPkcs1(pkcs1_1024_private_key); Console.WriteLine($"從pkcs1私鑰中提取公鑰:"); Console.WriteLine(_1024_public_key); Console.WriteLine("使用公鑰解密資料:"); //rsa public key decrypt //algorithm rsa/ecb/pkcs1padding Console.WriteLine(RSA.DecryptFromBase64(pkcs1data, RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(_1024_public_key),CipherAlgorithms.RSA_ECB_PKCS1Padding)); Console.WriteLine(); } ``` ![image](https://images.cnblogs.com/cnblogs_com/ynbt/1775471/o_200528052757sample.jpg) ## 下期預告 下一篇將介紹雜湊演算法(HMACSHA1、HMACSHA256、SHA1、SHA1WithRSA、SHA256、SHA256WithRSA),敬請期待... ![](https://img2020.cnblogs.com/blog/2029875/202006/2029875-20200612165240184-1675495