关于.net:利用ASP.NET machineKey加密我自己的数据

Leveraging ASP.NET machineKey For Encrypting My Own Data

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

由于ASP.NET已经为各种各样的东西维护了一个machinekey(viewdata encryption等),我想知道是否有任何ASP.NET函数允许我使用machinekey加密/解密自己的数据?这样我就不用设计自己的钥匙管理系统了。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
    }

}

理想情况下,"目的"应为已知的一次性有效值,以防止锻造。


ASP.NET 4.0中的新machinekey类完全满足您的需要。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
        }
    }
}

更新:正如这里提到的,小心使用它,或者允许他人伪造表单身份验证令牌。


我想不是直接的。我不记得是从哪里得到的,可能是反射镜和一些博客的结合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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;
    }
}

自告奋勇!


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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做这个是相当合理的。


您可能可以重用membershipProvider.encryptPassword方法,该方法反过来使用machineKeySection类的某些(不幸的是,内部)加密方法。