当前位置:  开发笔记 > 后端 > 正文

使用VB.NET的ASP.NET中的AES

如何解决《使用VB.NET的ASP.NET中的AES》经验,为你挑选了1个好方法。

什么是使用AES加密URL链接以使用VB.NET 2005将用户名传递到ASP.NET中的另一个网站的好链接或文章?仅供参考:接收网站将有权访问私钥以进行解密.



1> MichaelGG..:

第一

不要这样做!编写自己的加密系统很容易导致出错.最好使用现有系统,如果没有,请让知道密码学的人去做.如果您必须自己动手,请阅读Practical Cryptography.

请记住:"我们已经拥有足够快速,不安全的系统." (布鲁斯施奈尔) - 做正确的事情并担心以后的表现.

也就是说,如果你坚持使用AES来推销自己的,那么这里有一些指示.

初始化向量

AES是分组密码.给定一个密钥和一个明文块,它将其转换为特定的密文.这样做的问题是,每次使用相同的密钥生成相同的密钥时,相同的数据块.所以假设你发送这样的数据:

用户=加密(用户名)&角色=加密(的UserRole)

它们是两个独立的块,UserRoles加密每次都有相同的密文,无论名称如何.我需要的只是管理员的密文,我可以使用我的密码用户名将其删除.哎呀.

因此,存在密码操作模式.主要思想是你将获取一个块的密文,并将其XOR转换为下一个块的密文.这样我们就会进行加密(UserRoles,用户名),并且用户名密文会受到UserRoles的影响.

问题是第一个块仍然容易受到攻击 - 只要看到某个人的密文,我就会知道他们的角色.输入初始化向量.IV"启动"密码并确保其具有随机数据以加密流的其余部分.所以现在UserRoles密文具有随机IV异或的密文.问题解决了.

因此,请确保为每条消息生成随机IV.IV不敏感,可以用密文发送明文.使用足够大的IV - 在许多情况下,块的大小应该没问题.

廉正

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

修复是在明文上运行哈希算法(使用SHA256或SHA512),并将其包含在您传输的数据中.因此,如果我的消息是(UserName,Roles),您将发送(UserName,Roles,Hash(UserName,Roles)).现在,如果有人通过翻转来篡改密文,则哈希将不再计算,您可以拒绝该消息.

密钥推导

如果需要从密码生成密钥,请使用内置类:System.Security.Cryptography.PasswordDeriveBytes.这提供了salting和迭代,这可以提高派生密钥的强度,并减少密钥泄露时发现密码的机会.

定时/重播

编辑:很抱歉没有提到这个:P.您还需要确保拥有防重播系统.如果您只是加密邮件并传递它,那么获取邮件的任何人都可以重新发送邮件.为避免这种情况,您应该为邮件添加时间戳.如果时间戳与特定阈值不同,请拒绝该消息.您可能还希望包含一次性ID(可能是IV)并拒绝来自使用相同ID的其他IP的时间有效消息.

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

示例代码

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

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

class AesDemo {

    const int HASH_SIZE = 32; //SHA256

    /// Performs encryption with random IV (prepended to output), and includes hash of plaintext for verification.
    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; 鲍勃; Eve;:PerformAct2成功:检测到篡改.System.Security.Cryptography.CryptographicException:消息哈希不正确.at AesDemo.Decrypt(String password,Byte [] passwordSalt,Byte [] cipherText)在C:\ Program.cs:第46行,AesDemo.Main()在C:\ Program.cs:第100行

删除随机IV和哈希后,这是输出的类型:

tZfHJSFTXYX8V38AqEfYVXU5Dl/meUVAond70yIKGHY = tZfHJSFTXYX8V38AqEfYVcf9a3U8vIEk1LuqGEyRZXM =

注意第一个块是如何对应的"Alice; Bob; Eve;" 是一样的.确实是"角落案".

没有散​​列的示例

这是传递64位整数的简单示例.只是加密,你就会受到攻击.事实上,即使使用CBC填充,攻击也很容易完成.

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价值

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

加密:

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

解密:6598637501946607785

遭到攻击:14174658352338201502

因此,如果这是您要发送的ID类型,则可以很容易地将其更改为其他值.您需要在邮件之外进行身份验证.有时,消息结构不太可能落实到位,并且可以作为一种保护措施,但为什么要依赖可能发生变化的事情呢?无论应用程序如何,您都需要能够依赖加密才能正常工作.


我几乎不认为IV是加密系统的"极端情况".
推荐阅读
Chloemw
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有