zoukankan      html  css  js  c++  java
  • .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

  • 相关阅读:
    LCA问题的离线处理Tarjan算法模版
    匈牙利算法 模版
    poj 1190 dfs
    poj 1376 bfs
    划分树模版
    让innerHTML的脚本也可以运行起来
    Keycode对照表
    Javascript 操作XML简单介绍
    Webdings和Wingdings字符码对应表
    动态加载JS脚本的4种方法
  • 原文地址:https://www.cnblogs.com/nasha/p/10260158.html
Copyright © 2011-2022 走看看