在.NetCore中預設使用DataProtection來保護數據,例如Cooike等。一般情況下DataProtection生成的密鑰會被加密後存儲,例如預設的文件存儲 可以看到使用了Windows DPAPI加密。 但是如果更改預設設置例如使用的外部存儲如redis則此時密鑰預設是不加密的 微軟 ...
在.NetCore中預設使用DataProtection來保護數據,例如Cooike等。一般情況下DataProtection生成的密鑰會被加密後存儲,例如預設的文件存儲
可以看到使用了Windows DPAPI加密。
但是如果更改預設設置例如使用的外部存儲如redis則此時密鑰預設是不加密的
微軟說明如下
警告密鑰未加密,這個時候如果redis被破解,系統的密鑰也就泄漏了。
微軟提供了2個介面IXmlEncryptor,IXmlDecryptor來實現密鑰的加密解密,下麵使用AES來簡單現實,也可以替換為任何加密方式
namespace Microsoft.AspNetCore.DataProtection { /// <summary> /// Extensions for configuring data protection using an <see cref="IDataProtectionBuilder"/>. /// </summary> public static class DataProtectionBuilderExtensions { /// <summary> /// Configures keys to be encrypted with AES before being persisted to /// storage. /// </summary> /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param> /// use on the local machine, 'false' if the key should only be decryptable by the current /// Windows user account.</param> /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns> public static IDataProtectionBuilder ProtectKeysWithAES(this IDataProtectionBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services => { //var loggerFactory = services.GetService<ILoggerFactory>() ?? NullLoggerFactory.Instance; return new ConfigureOptions<KeyManagementOptions>(options => { options.XmlEncryptor = new AesXmlEncryptor(); }); }); return builder; } } /// <summary> /// An <see cref="IXmlEncryptor"/> that encrypts XML elements with a Aes encryptor. /// </summary> sealed class AesXmlEncryptor : IXmlEncryptor { /// <summary> /// Encrypts the specified <see cref="XElement"/> with a null encryptor, i.e., /// by returning the original value of <paramref name="plaintextElement"/> unencrypted. /// </summary> /// <param name="plaintextElement">The plaintext to echo back.</param> /// <returns> /// An <see cref="EncryptedXmlInfo"/> that contains the null-encrypted value of /// <paramref name="plaintextElement"/> along with information about how to /// decrypt it. /// </returns> public EncryptedXmlInfo Encrypt(XElement plaintextElement) { if (plaintextElement == null) { throw new ArgumentNullException(nameof(plaintextElement)); } // <encryptedKey> // <!-- This key is encrypted with {provider}. --> // <value>{base64}</value> // </encryptedKey> var Jsonxmlstr =JsonConvert.SerializeObject(plaintextElement); var EncryptedData = EncryptHelper.AESEncrypt(Jsonxmlstr, "b587be32-0420-4eb1-89c6-01bb999e18fe"); var newElement = new XElement("encryptedKey", new XComment(" This key is encrypted with AES."), new XElement("value",EncryptedData)); return new EncryptedXmlInfo(newElement, typeof(AesXmlDecryptor)); } } /// <summary> /// An <see cref="IXmlDecryptor"/> that decrypts XML elements with a Aes decryptor. /// </summary> sealed class AesXmlDecryptor : IXmlDecryptor { /// <summary> /// Decrypts the specified XML element. /// </summary> /// <param name="encryptedElement">An encrypted XML element.</param> /// <returns>The decrypted form of <paramref name="encryptedElement"/>.</returns> public XElement Decrypt(XElement encryptedElement) { if (encryptedElement == null) { throw new ArgumentNullException(nameof(encryptedElement)); } // <encryptedKey> // <!-- This key is encrypted with {provider}. --> // <value>{base64}</value> // </encryptedKey> var EncryptedData=(string)encryptedElement.Element("value"); var Jsonxmlstr = EncryptHelper.AESDecrypt(EncryptedData, "b587be32-0420-4eb1-89c6-01bb999e18fe"); // Return a clone of the single child node. return JsonConvert.DeserializeObject<XElement>(Jsonxmlstr); } } #region AES public class EncryptHelper { static readonly byte[] AES_IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; /// <summary> /// AES加密演算法 /// </summary> /// <param name="encryptString">加密前字元串</param> /// <param name="keytype">秘鑰</param> /// <returns></returns> public static string AESEncrypt(string encryptString, string encryptKey) { if (string.IsNullOrWhiteSpace(encryptString)) return null; if (string.IsNullOrWhiteSpace(encryptKey)) return null; encryptKey = encryptKey.PadRight(32, ' '); byte[] keyBytes = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 32)); using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider()) { aesAlg.Key = keyBytes; aesAlg.IV = AES_IV; ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { swEncrypt.Write(encryptString); } byte[] bytes = msEncrypt.ToArray(); return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_'); } } } } /// <summary> /// AES解密演算法 /// </summary> /// <param name="decryptString">解密前的字元串</param> /// <param name="keytype">秘鑰</param> /// <returns></returns> public static string AESDecrypt(string decryptString, string decryptKey) { if (string.IsNullOrWhiteSpace(decryptString)) return null; decryptString = decryptString.Replace('-', '+').Replace('_', '/'); if (string.IsNullOrWhiteSpace(decryptKey)) return null; decryptKey = decryptKey.PadRight(32, ' '); byte[] keyBytes = Encoding.UTF8.GetBytes(decryptKey.Substring(0, 32)); Byte[] inputBytes = Convert.FromBase64String(decryptString); using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider()) { aesAlg.Key = keyBytes; aesAlg.IV = AES_IV; ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); using (MemoryStream msEncrypt = new MemoryStream(inputBytes)) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srEncrypt = new StreamReader(csEncrypt)) { return srEncrypt.ReadToEnd(); } } } } } } #endregion }View Code
調用也很簡單.ProtectKeysWithAES()即可
services.AddDataProtection().SetApplicationName("DataProtection").PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(RedisConnection), "DataProtection-Keys").ProtectKeysWithAES();
加密後的密鑰如下
註:在生成密鑰之前要刪除之前的密鑰,不然會使用舊密鑰而不生成新的密鑰直到密鑰過期。
對於AES所使用密鑰也要進行保護,可以使用第三方密鑰存儲庫如Azure 密鑰保管庫,或者也可以使用X509證書來來加密。
github https://github.com/saber-wang/DataProtection