ASP.NET中的AES与VB.NET [英] AES in ASP.NET with VB.NET

查看:146
本文介绍了ASP.NET中的AES与VB.NET的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有关使用AES加密URL链接的一个好的链接或文章是什么?在ASP.NET中使用VB.NET 2005将用户名传递给另一个网站?

What is a good link or article on encrypting a URL link with AES to pass username to another web site in ASP.NET using VB.NET 2005? FYI: The receiving web site will have access to the private KEY to decrypt.

推荐答案

首先



不要这样做!编写自己的加密系统很容易导致犯错误。最好使用现有的系统,或者如果没有,请让知道加密的人去做。如果您必须自己动手,请阅读实用加密

请记住:我们已经有足够快速,不安全的系统了。
(Bruce Schneier) - 做正确的事情,以后担心性能。

And please, remember: "We already have enough fast, insecure systems." (Bruce Schneier) -- Do things correct and worry about performance later.

也就是说,如果你坚持使用AES来自己滚动,这里有几个指针。

That said, if you are stuck on using AES to roll your own, here are a few pointers.

AES是一种分块密码。给定一个密钥和一个明文块,它将它转换为一个特定的密文。这样做的问题是,相同的数据块将每次使用相同的密钥生成相同的密文。因此,假设您发送的数据类似于:

AES is a block cipher. Given a key and a block of plaintext, it converts it to a specific ciphertext. The problem with this is that the same blocks of data will generate the same ciphertext with the same key, every time. So suppose you send data like this:

user = Encrypt(Username)& roles = Encrypt(UserRoles)

user=Encrypt(Username)&roles=Encrypt(UserRoles)

它们是两个单独的块,并且UserRoles加密每次都将有相同的密文,而不管名称。我需要的是一个管理员的密文,我可以把它与我的密码的用户名。糟糕。

They're two separate blocks, and the UserRoles encryption will have the same ciphertext each time, regardless of the name. All I need is the ciphertext for an admin, and I can drop it right in with my cipher'd username. Oops.

因此,有密码操作模式。主要的想法是,你将获取一个块的密文,并将其XOR到下一个块的密文。这样,我们将做Encrypt(UserRoles,Username),用户名密码受UserRoles的影响。

So, there are cipher operation modes. The main idea is that you'll take the ciphertext of one block, and XOR it into the ciphertext of the next block. That way we'll do Encrypt(UserRoles, Username), and the Username ciphertext is affected by the UserRoles.

问题是第一个块仍然是脆弱的 - 只是通过查看某人的密文,我可能知道他们的角色。输入初始化向量。 IV启动密码并确保其具有随机数据来加密流的其余部分。因此,现在UserRoles密文具有随机IV XOR的密文。问题解决。

The problem is that the first block is still vulnerable - just by seeing someone's ciphertext, I might know their roles. Enter the initialization vector. The IV "starts up" the cipher and ensures it has random data to encrypt the rest of the stream. So now the UserRoles ciphertext has the ciphertext of the random IV XOR'd in. Problem solved.

因此,确保为每个消息生成一个随机IV。 IV不敏感,可以用密文发送明文。

So, make sure you generate a random IV for each message. The IV is not sensitive and can be sent plaintext with the ciphertext. Use an IV large enough -- the size of the block should be fine for many cases.

AES不提供完整性功能。任何人都可以修改您的密文,并且解密仍然可以工作。一般来说,它不太可能是有效的数据,但可能很难知道有效数据是什么。例如,如果你传输一个GUID加密的,很容易修改一些位,并生成一个完全不同的。这可能会导致应用程序错误等。

AES doesn't provide integrity features. Anyone can modify your ciphertext, and the decrypt will still work. It's unlikely it'll be valid data in general, but it might be hard to know what valid data is. For instance, if you're transmitting a GUID encrypted, it'd be easy to modify some bits and generate a completely different one. That could lead to application errors and so on.

修复的是对纯文本运行散列算法(使用SHA256或SHA512),并将其包括在数据你传送。因此,如果我的消息是(UserName,Roles),您将发送(UserName,Roles,Hash(UserName,Roles))。

The fix there is to run a hash algorithm (use SHA256 or SHA512) on the plaintext, and include that in the data you transmit. So if my message is (UserName, Roles), you'll send (UserName, Roles, Hash(UserName, Roles)). Now if someone tampers with the ciphertext by flipping a bit, the hash will no longer compute and you can reject the message.

如果需要从密码生成密钥,请使用内置类: System.Security.Cryptography.PasswordDeriveBytes 。这提供了盐化和迭代,这可以提高导出密钥的强度并且如果密钥被破坏则减少发现密码的机会。

If you need to generate a key from a password, use the built-in class: System.Security.Cryptography.PasswordDeriveBytes. This provides salting and iterations, which can improve the strength of derived keys and reduce the chance of discovering the password if the key is compromised.

编辑:对不起,以前没有提到过:P。你还需要确保你有一个反重放系统。如果你只是加密消息并传递,任何人谁获得该消息可以重新发送。为了避免这种情况,您应该向消息添加时间戳。如果时间戳与某个阈值不同,则拒绝该消息。您可能还想要在其中包含一次性ID(可以是IV),并拒绝来自使用相同ID的其他IP的有效时间消息。

Sorry for not mentioning this earlier :P. You also need to make sure you have an anti-replay system. If you simply encrypt the message and pass it around, anyone who gets the message can just resend it. To avoid this, you should add a timestamp to the message. If the timestamp is different by a certain threshold, reject the message. You may also want to include a one-time ID with it (this could be the IV) and reject time-valid messages that come from other IPs using the same ID.

确保在包含时间信息时进行哈希验证很重要。否则,如果您没有检测到这种强力尝试,有人可能会篡改一小部分密文,并可能生成有效的时间戳。

It's important to make sure you do the hash verification when you include the timing information. Otherwise, someone could tamper with a bit of the ciphertext and potentially generate a valid timestamp if you don't detect such brute force attempts.

由于显然正确地使用IV对于一些人是有争议的,下面是一些代码将生成随机IV并将它们添加到您的输出。它还将执行身份验证步骤,确保加密的数据未被修改。

Since apparently using an IV correctly is controversial for some folks, here's some code that'll generate random IVs and add them to your output for you. It'll also perform the authentication step, making sure the encrypted data wasn't modified.

using System;
using System.Security.Cryptography;
using System.Text;

class AesDemo {

    const int HASH_SIZE = 32; //SHA256

    /// <summary>Performs encryption with random IV (prepended to output), and includes hash of plaintext for verification.</summary>
    public static byte[] Encrypt(string password, byte[] passwordSalt, byte[] plainText) {
        // Construct message with hash
        var msg = new byte[HASH_SIZE + plainText.Length];
        var hash = computeHash(plainText, 0, plainText.Length);
        Buffer.BlockCopy(hash, 0, msg, 0, HASH_SIZE);
        Buffer.BlockCopy(plainText, 0, msg, HASH_SIZE, plainText.Length);

        // Encrypt
        using (var aes = createAes(password, passwordSalt)) {
            aes.GenerateIV();
            using (var enc = aes.CreateEncryptor()) {

                var encBytes = enc.TransformFinalBlock(msg, 0, msg.Length);
                // Prepend IV to result
                var res = new byte[aes.IV.Length + encBytes.Length];
                Buffer.BlockCopy(aes.IV, 0, res, 0, aes.IV.Length);
                Buffer.BlockCopy(encBytes, 0, res, aes.IV.Length, encBytes.Length);
                return res;
            }
        }
    }

    public static byte[] Decrypt(string password, byte[] passwordSalt, byte[] cipherText) {
        using (var aes = createAes(password, passwordSalt)) {
            var iv = new byte[aes.IV.Length];
            Buffer.BlockCopy(cipherText, 0, iv, 0, iv.Length);
            aes.IV = iv; // Probably could copy right to the byte array, but that's not guaranteed

            using (var dec = aes.CreateDecryptor()) {
                var decBytes = dec.TransformFinalBlock(cipherText, iv.Length, cipherText.Length - iv.Length);

                // Verify hash
                var hash = computeHash(decBytes, HASH_SIZE, decBytes.Length - HASH_SIZE);
                var existingHash = new byte[HASH_SIZE];
                Buffer.BlockCopy(decBytes, 0, existingHash, 0, HASH_SIZE);
                if (!compareBytes(existingHash, hash)){
                    throw new CryptographicException("Message hash incorrect.");
                }

                // Hash is valid, we're done
                var res = new byte[decBytes.Length - HASH_SIZE];
                Buffer.BlockCopy(decBytes, HASH_SIZE, res, 0, res.Length);
                return res;
            }
        }
    }

    static bool compareBytes(byte[] a1, byte[] a2) {
        if (a1.Length != a2.Length) return false;
        for (int i = 0; i < a1.Length; i++) {
            if (a1[i] != a2[i]) return false;
        }
        return true;
    }

    static Aes createAes(string password, byte[] salt) {
        // Salt may not be needed if password is safe
        if (password.Length < 8) throw new ArgumentException("Password must be at least 8 characters.", "password");
        if (salt.Length < 8) throw new ArgumentException("Salt must be at least 8 bytes.", "salt");
        var pdb = new PasswordDeriveBytes(password, salt, "SHA512", 129);
        var key = pdb.GetBytes(16);

        var aes = Aes.Create();
        aes.Mode = CipherMode.CBC;
        aes.Key = pdb.GetBytes(aes.KeySize / 8);
        return aes;
    }

    static byte[] computeHash(byte[] data, int offset, int count) {
        using (var sha = SHA256.Create()) {
            return sha.ComputeHash(data, offset, count);
        }
    }

    public static void Main() {
        var password = "1234567890!";
        var salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
        var ct1 = Encrypt(password, salt, Encoding.UTF8.GetBytes("Alice; Bob; Eve;: PerformAct1"));
        Console.WriteLine(Convert.ToBase64String(ct1));
        var ct2 = Encrypt(password, salt, Encoding.UTF8.GetBytes("Alice; Bob; Eve;: PerformAct2"));
        Console.WriteLine(Convert.ToBase64String(ct2));

        var pt1 = Decrypt(password, salt, ct1);
        Console.WriteLine(Encoding.UTF8.GetString(pt1));
        var pt2 = Decrypt(password, salt, ct2);
        Console.WriteLine(Encoding.UTF8.GetString(pt2));

        // Now check tampering
        try {
            ct1[30]++;
            Decrypt(password, salt, ct1);
            Console.WriteLine("Error: tamper detection failed.");
        } catch (Exception ex) {
            Console.WriteLine("Success: tampering detected.");
            Console.WriteLine(ex.ToString());
        }
    }
}

输出:

JZVaD327sDmCmdzY0PsysnRgHbbC3eHb7YXALb0qxFVlr7Lkj8WaOZWc1ayWCvfhTUz / y0QMz + uv0PwmuG8VBVEQThaNTD02JlhIs1DjJtg =
QQvDujNJ31qTu / foDFUiVMeWTU0jKL / UJJfFAvmFtz361o3KSUlk / ZH + 4701mlFEU4Ce6VuAAuaiP1EENBJ74Wc8mE / QTofkUMHoa65 / 5E4 =
爱丽丝;鲍勃Eve;:PerformAct1 Alice;
Bob; Eve ;: PerformAct2成功:
检测到篡改。
System.Security.Cryptography.CryptographicException:
消息散列不正确。
AesDemo.Decrypt(String password,
Byte [] passwordSalt,Byte []
cipherText)in
C:\Program.cs:line
46 at
中的AesDemo.Main()C:\Program.cs:line
100

JZVaD327sDmCmdzY0PsysnRgHbbC3eHb7YXALb0qxFVlr7Lkj8WaOZWc1ayWCvfhTUz/y0QMz+uv0PwmuG8VBVEQThaNTD02JlhIs1DjJtg= QQvDujNJ31qTu/foDFUiVMeWTU0jKL/UJJfFAvmFtz361o3KSUlk/zH+4701mlFEU4Ce6VuAAuaiP1EENBJ74Wc8mE/QTofkUMHoa65/5e4= Alice; Bob; Eve;: PerformAct1 Alice; Bob; Eve;: PerformAct2 Success: tampering detected. System.Security.Cryptography.CryptographicException: Message hash incorrect. at AesDemo.Decrypt(String password, Byte[] passwordSalt, Byte[] cipherText) in C:\Program.cs:line 46 at AesDemo.Main() in C:\Program.cs:line 100

random IV和哈希,以下是输出类型:

After removing the random IV and the hash, here's the type of output:


tZfHJSFTXYX8V38AqEfYVXU5Dl / meUVAond70yIKGHY =
tZfHJSFTXYX8V38AqEfYVcf9a3U8vIEk1LuqGEyRZXM =

tZfHJSFTXYX8V38AqEfYVXU5Dl/meUVAond70yIKGHY= tZfHJSFTXYX8V38AqEfYVcf9a3U8vIEk1LuqGEyRZXM=

请注意,第一个块对应于Alice; Bob; Eve;是一样的。

Notice how the first block, corresponding to "Alice; Bob; Eve;" is the same. "Corner case" indeed.

这是一个简单的例子,传递一个64位整数。只是加密,你开放攻击。事实上,即使使用CBC填充,攻击也很容易完成。

Here's a simple example of passing a 64-bit integer. Just encrypt and you're open to attack. In fact, the attack is easily done, even with CBC padding.

public static void Main() {
    var buff = new byte[8];
    new Random().NextBytes(buff);
    var v = BitConverter.ToUInt64(buff, 0);
    Console.WriteLine("Value: " + v.ToString());
    Console.WriteLine("Value (bytes): " + BitConverter.ToString(BitConverter.GetBytes(v)));
    var aes = Aes.Create();
    aes.GenerateIV();
    aes.GenerateKey();
    var encBytes = aes.CreateEncryptor().TransformFinalBlock(BitConverter.GetBytes(v), 0, 8);
    Console.WriteLine("Encrypted: " + BitConverter.ToString(encBytes));
    var dec = aes.CreateDecryptor();
    Console.WriteLine("Decrypted: " + BitConverter.ToUInt64(dec.TransformFinalBlock(encBytes, 0, encBytes.Length), 0));
    for (int i = 0; i < 8; i++) {
        for (int x = 0; x < 250; x++) {
            encBytes[i]++;
            try {
                Console.WriteLine("Attacked: " + BitConverter.ToUInt64(dec.TransformFinalBlock(encBytes, 0, encBytes.Length), 0));
                return;
            } catch { }
        }
    }
}

输出:


值:6598637501946607785值

Value: 6598637501946607785 Value

-38-19-D1-D8-11-93-5B

(bytes): A9-38-19-D1-D8-11-93-5B

加密:

31-59- B0-25-FD-C5-13-D7-81-D8-F5-8A-33-2A-57-DD

31-59-B0-25-FD-C5-13-D7-81-D8-F5-8A-33-2A-57-DD

解密:6598637501946607785

Decrypted: 6598637501946607785

攻击:14174658352338201502

Attacked: 14174658352338201502

因此,如果这是您要发送的ID类型可以很容易地改变为另一个值。您需要在邮件外部进行身份验证。有时,消息结构不太可能落入到位,并且可以排序作为保护措施,但为什么依赖可能改变的东西?您需要能够依赖您的加密工作正常无论应用程序。

So, if that's the kind of ID you're sending, it could quite easily be changed to another value. You need to authenticate outside of your message. Sometimes, the message structure is unlikely to fall into place and can sorta act as a safeguard, but why rely on something that could possibly change? You need to be able to rely on your crypto working correctly regardless of the application.

这篇关于ASP.NET中的AES与VB.NET的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆