.NetCore分布式部署中的DataProtection密钥安全性

在.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

相关文章