AES在ASP.NET与VB.NET [英] AES in ASP.NET with VB.NET

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

问题描述

使用AES加密URL链接有什么好的链接或文章,可以使用VB.NET 2005在ASP.NET中将用户名传递给另一个网站?
FYI:接收网站将访问私钥以进行解密。

解决方案

首先



不要这样做!编写自己的加密系统很容易导致错误。最好使用一个现有的系统,否则,让一个知道加密技术的人去做。如果您必须自行执行,请阅读实用加密技术

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



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



初始化向量



AES是块密码。给出一个密钥和一个明文块,它将其转换成一个特定的密文。这样做的问题是,相同的数据块每次都会产生相同的密文。所以假设你发送这样的数据:



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



它们是两个单独的块,每次UserRoles加密将具有相同的密文,无论名称如何。我需要的只是一个管理员的密文,我可以把它放在我的密码用户名上。糟糕。



所以,密码操作模式。主要的想法是你将获取一个块的密文,并将其XOR转换成下一个块的密文。这样我们将进行加密(UserRoles,用户名),用户名密码受UserRoles影响。



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



所以,确保为每个消息生成一个随机的IV。 IV不敏感,可以使用密文发送明文。使用IV足够大 - 许多情况下块的大小应该很好。



诚信



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



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



关键派生



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



计时/重播



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



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



示例代码



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

  using System; 
使用System.Security.Cryptography;
使用System.Text;

class AesDemo {

const int HASH_SIZE = 32; // SHA256

///< summary>使用随机IV执行加密(预先输出),并且包括用于验证的明文的散列。< / summary>
public static byte [] Encrypt(string password,byte [] passwordSalt,byte [] plainText){
//构造带有散列的消息
var msg = new byte [HASH_SIZE + plainText.Length ]。
var hash = computeHash(plainText,0,plainText.Length);
BufferBlockCopy(hash,0,msg,0,HASH_SIZE);
Buffer.BlockCopy(plainText,0,msg,HASH_SIZE,plainText.Length);

//使用(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);
BufferBlockCopy(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];
BufferBlockCopy(cipherText,0,iv,0,iv.Length);
aes.IV = iv; //可能会将权限复制到字节数组,但不能保证

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

//验证哈希
var哈希= computeHash(decBytes,HASH_SIZE,decBytes.Length - HASH_SIZE);
var existingHash = new byte [HASH_SIZE];
BufferBlockCopy(decBytes,0,existingHash,0,HASH_SIZE);
if(!compareBytes(existingHash,hash)){
throw new CryptographicException(Message hash incorrect);
}

//哈希有效,我们完成
var res = new byte [decBytes.Length - HASH_SIZE];
Buffer.BlockCopy(decBytes,HASH_SIZE,res,0,res.Length);
return res;
$

$ b static bool compareBytes(byte [] a1,byte [] a2){
if(a1.Length!= a2 .Length)返回false; (int i = 0; i if(a1 [i]!= a2 [i])return false;
}
返回true;
}

static Aes createAes(string password,byte [] salt){
//如果密码安全,则可能不需要盐
if(password.Length < 8)抛出新的ArgumentException(密码必须至少为8个字符,密码);
if(salt.Length< 8)throw new ArgumentException(Salt必须至少为8个字节,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);
返回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));

//现在检查篡改
尝试{
ct1 [30] ++;
解密(密码,盐,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爱丽丝;
Bob; Eve;:PerformAct2成功:
检测到篡改。
System.Security.Cryptography.CryptographicException:
消息散列不正确。 at
AesDemo.Decrypt(String password,
Byte [] passwordSalt,Byte []
cipherText)in
C:\Program.cs:line
46 at AesDemo.Main()in
C:\Program.cs:line
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)); (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值



(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



解密:6598637501946607785



被攻击:14174658352338201502


所以,如果这是你发送的那种ID,可以很容易地改变为另一个值。您需要在您的消息之外进行身份验证。有时候,信息结构不太可能发生变化,可以将其作为一个保障措施,但为什么要依赖可能会改变的东西呢?无论应用如何,您都需要依靠您的密码才能正常工作。


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.

解决方案

First

Don't do it! Writing your own crypto system can easily lead to making mistakes. It's best to use an existing system, or if not, get someone who knows cryptography to do it. If you have to do it yourself, read Practical Cryptography.

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

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

Initialization Vector

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)

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.

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.

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.

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.

Integrity

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.

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.

Key derivation

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.

Timing/replay

Edit: 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.

Sample code

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());
        }
    }
}

Output:

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

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

tZfHJSFTXYX8V38AqEfYVXU5Dl/meUVAond70yIKGHY= tZfHJSFTXYX8V38AqEfYVcf9a3U8vIEk1LuqGEyRZXM=

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

Example without hashing

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 { }
        }
    }
}

Output:

Value: 6598637501946607785 Value

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

Encrypted:

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

Decrypted: 6598637501946607785

Attacked: 14174658352338201502

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.

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

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