【问题标题】:Leveraging ASP.NET machineKey For Encrypting My Own Data利用 ASP.NET machineKey 加密我自己的数据
【发布时间】:2023-03-17 01:53:01
【问题描述】:

我想在 ASP.NET MVC 应用程序中加密一些数据,以防止用户篡改它。我可以使用 Cryptography 类来进行实际的加密/解密,没有问题。主要问题是找出存储加密密钥的位置并管理对它的更改。

由于 ASP.NET 已经为各种事情(ViewData 加密等)维护了一个 machineKey,我想知道是否有任何 ASP.NET 函数可以让我使用 machineKey 加密/解密我自己的数据?这样我就不必设计自己的密钥管理系统了。

【问题讨论】:

    标签: .net asp.net security cryptography


    【解决方案1】:

    对于 .NET Framwork 4.5,您应该使用新的 API:

    public class StringProtector
    {
    
        private const string Purpose = "Authentication Token";
    
        public string Protect(string unprotectedText)
        {
            var unprotectedBytes = Encoding.UTF8.GetBytes(unprotectedText);
            var protectedBytes = MachineKey.Protect(unprotectedBytes, Purpose);
            var protectedText = Convert.ToBase64String(protectedBytes);
            return protectedText;
        }
    
        public string Unprotect(string protectedText)
        {
            var protectedBytes = Convert.FromBase64String(protectedText);
            var unprotectedBytes = MachineKey.Unprotect(protectedBytes, Purpose);
            var unprotectedText = Encoding.UTF8.GetString(unprotectedBytes);
            return unprotectedText;
        }
    
    }
    

    理想情况下,“目的”应该是一个已知的一次性有效值,以防止伪造。

    【讨论】:

    • 谢谢!我想补充一下:msdn.microsoft.com/en-us/library/… 检查“备注”以查看更好的使用“目的”的方法。
    • 只是为了确认,因为引用了机器密钥,我假设在没有粘性会话的负载平衡设置中,您需要确保机器密钥相同。
    • 是的,负载均衡器中所有机器的机器密钥必须相同
    • @MarcoBettiolo - 我很困惑。如果我将您的目的字符串从“身份验证令牌”更改为“DBPassword”,那么有人怎么可能使用您的保护/取消保护功能来伪造每个 Jeff Moser 的表单身份验证令牌?
    【解决方案2】:

    ASP.NET 4.0 中的新 MachineKey 类完全符合您的要求。

    例如:

    public static class StringEncryptor {
        public static string Encrypt(string plaintextValue) {
            var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue);
            return MachineKey.Encode(plaintextBytes, MachineKeyProtection.All);
        }
    
        public static string Decrypt(string encryptedValue) {
            try {
                var decryptedBytes = MachineKey.Decode(encryptedValue, MachineKeyProtection.All);
                return Encoding.UTF8.GetString(decryptedBytes);
            }
            catch {
                return null;
            }
        }
    }
    

    更新:如 here 所述,请注意如何使用它,否则您可能会允许他人伪造表单身份验证令牌。

    【讨论】:

    • 从 .net 4.5 开始,MachinKey.Encode/MachineKey.Decode 已被弃用,而应使用 MachineKey.Protect。根据 Marco Bettiolo 的回应。
    【解决方案3】:

    我猜不是直接的。我不记得我是从哪里得到这个的,可能是 Reflector 和一些博客的组合。

    public abstract class MyAwesomeClass
    {
        private static byte[] cryptKey;
    
        private static MachineKeySection machineKeyConfig =
            (MachineKeySection)ConfigurationManager
                .GetSection("system.web/machineKey");
    
        // ... snip ...
    
        static MyAwesomeClass()
        {
            string configKey;
            byte[] key;
    
            configKey = machineKeyConfig.DecryptionKey;
            if (configKey.Contains("AutoGenerate"))
            {
                throw new ConfigurationErrorsException(
                    Resources.MyAwesomeClass_ExplicitAlgorithmRequired);
            }
    
            key = HexStringToByteArray(configKey);
    
            cryptKey = key;
        }
    
        // ... snip ...
    
        protected static byte[] Encrypt(byte[] inputBuffer)
        {
            SymmetricAlgorithm algorithm;
            byte[] outputBuffer;
    
            if (inputBuffer == null)
            {
                throw new ArgumentNullException("inputBuffer");
            }
    
            algorithm = GetCryptAlgorithm();
    
            using (var ms = new MemoryStream())
            {
                algorithm.GenerateIV();
                ms.Write(algorithm.IV, 0, algorithm.IV.Length);
    
                using (var cs = new CryptoStream(
                     ms, 
                     algorithm.CreateEncryptor(), 
                     CryptoStreamMode.Write))
                {
                    cs.Write(inputBuffer, 0, inputBuffer.Length);
                    cs.FlushFinalBlock();
                }
    
                outputBuffer = ms.ToArray();
            }
    
            return outputBuffer;
        }
    
        protected static byte[] Decrypt(string input)
        {
            SymmetricAlgorithm algorithm;
            byte[] inputBuffer, inputVectorBuffer, outputBuffer;
    
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }
    
            algorithm = GetCryptAlgorithm();
            outputBuffer = null;
    
            try
            {
                inputBuffer = Convert.FromBase64String(input);
    
                inputVectorBuffer = new byte[algorithm.IV.Length];
                Array.Copy(
                     inputBuffer, 
                     inputVectorBuffer,
                     inputVectorBuffer.Length);
                algorithm.IV = inputVectorBuffer;
    
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(
                        ms, 
                        algorithm.CreateDecryptor(), 
                        CryptoStreamMode.Write))
                    {
                        cs.Write(
                            inputBuffer,
                            inputVectorBuffer.Length, 
                            inputBuffer.Length - inputVectorBuffer.Length);
                        cs.FlushFinalBlock();
                    }
    
                    outputBuffer = ms.ToArray();
                }
            }
            catch (FormatException e)
            {
                throw new CryptographicException(
                    "The string could not be decoded.", e);
            }
    
            return outputBuffer;
        }
    
        // ... snip ...
    
        private static SymmetricAlgorithm GetCryptAlgorithm()
        {
            SymmetricAlgorithm algorithm;
            string algorithmName;
    
            algorithmName = machineKeyConfig.Decryption;
            if (algorithmName == "Auto")
            {
                throw new ConfigurationErrorsException(
                    Resources.MyAwesomeClass_ExplicitAlgorithmRequired);
            }
    
            switch (algorithmName)
            {
                case "AES":
                    algorithm = new RijndaelManaged();
                    break;
                case "3DES":
                    algorithm = new TripleDESCryptoServiceProvider();
                    break;
                case "DES":
                    algorithm = new DESCryptoServiceProvider();
                    break;
                default:
                    throw new ConfigurationErrorsException(
                        string.Format(
                            CultureInfo.InvariantCulture,
                            Resources.MyAwesomeClass_UnrecognizedAlgorithmName,
                            algorithmName));
            }
    
            algorithm.Key = cryptKey;
    
            return algorithm;
        }
    
        private static byte[] HexStringToByteArray(string str)
        {
            byte[] buffer;
    
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }
    
            if (str.Length % 2 == 1)
            {
                str = '0' + str;
            }
    
            buffer = new byte[str.Length / 2];
    
            for (int i = 0; i < buffer.Length; ++i)
            {
                buffer[i] = byte.Parse(
                    str.Substring(i * 2, 2),
                    NumberStyles.HexNumber,
                    CultureInfo.InvariantCulture);
            }
    
            return buffer;
        }
    }
    

    告诫购买者!

    【讨论】:

    • 相当完整的发布内部信息是如何完成的。
    【解决方案4】:

    如果您使用的是 3.5 或更早版本,则可以避免大量代码,只需执行以下操作:

    public static string Encrypt(string cookieValue)
    {
        return FormsAuthentication.Encrypt(new FormsAuthenticationTicket(1,
                                                                         string.Empty,
                                                                         DateTime.Now,
                                                                         DateTime.Now.AddMinutes(20160),
                                                                         true,
                                                                         cookieValue));
    }
    
    public static string Decrypt(string encryptedTicket)
    {
        return FormsAuthentication.Decrypt(encryptedTicket).UserData;
    }
    

    我的一位同事说服了我,我认为对于自定义 cookie 这样做是相当合理的,如果不是为了一般加密需求。

    【讨论】:

    • 仅供参考,这假设您不超过最大票证长度 (4096)
    【解决方案5】:

    您也许可以重用MembershipProvider.EncryptPassword 方法,该方法又使用MachineKeySection 类的一些(不幸的是内部)加密方法。

    【讨论】:

    • 幸运的是不再是内部的。请参阅 Jeff Moser 的回答。
    猜你喜欢
    • 2015-12-26
    • 2017-04-11
    • 2020-09-23
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 2017-06-08
    相关资源
    最近更新 更多