問題描述
關(guān)于使用 VB.NET 2005 使用 AES 加密 URL 鏈接以將用戶名傳遞到 ASP.NET 中的另一個網(wǎng)站的好鏈接或文章是什么?僅供參考:接收網(wǎng)站將有權(quán)訪問私鑰進行解密.
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
別這樣!編寫自己的加密系統(tǒng)很容易導(dǎo)致出錯.最好使用現(xiàn)有系統(tǒng),或者如果沒有,請讓知道密碼學(xué)的人來做.如果您必須自己做,請閱讀實用密碼學(xué).
請記住:我們已經(jīng)有足夠快但不安全的系統(tǒng)了."(布魯斯·施奈爾)——做正確的事,以后再擔(dān)心性能.
And please, remember: "We already have enough fast, insecure systems." (Bruce Schneier) -- Do things correct and worry about performance later.
也就是說,如果您堅持使用 AES 來推出自己的產(chǎn)品,這里有一些建議.
That said, if you are stuck on using AES to roll your own, here are a few pointers.
AES 是一種分組密碼.給定一個密鑰和一個明文塊,它將其轉(zhuǎn)換為特定的密文.這樣做的問題是,相同的數(shù)據(jù)塊每次都會使用相同的密鑰生成相同的密文.所以假設(shè)你發(fā)送這樣的數(shù)據(jù):
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.
所以,有密碼操作模式.主要思想是您將獲取一個塊的密文,并將其異或到下一個塊的密文中.這樣我們就做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啟動"密碼并確保它具有隨機數(shù)據(jù)來加密流的其余部分.所以現(xiàn)在 UserRoles 密文有隨機 IV XOR'd 的密文.問題解決了.
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 不敏感,可以與密文一起發(fā)送明文.使用足夠大的 IV——塊的大小在許多情況下應(yīng)該沒問題.
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 不提供完整性功能.任何人都可以修改您的密文,并且解密仍然有效.一般來說,它不太可能是有效數(shù)據(jù),但可能很難知道什么是有效數(shù)據(jù).例如,如果您要傳輸加密的 GUID,則很容易修改一些位并生成一個完全不同的位.這可能會導(dǎo)致應(yīng)用程序錯誤等等.
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),并將其包含在您傳輸?shù)臄?shù)據(jù)中.因此,如果我的消息是 (UserName, Roles),您將發(fā)送 (UserName, Roles, Hash(UserName, Roles)).現(xiàn)在如果有人通過翻轉(zhuǎn)來篡改密文,哈希將不再計算,您可以拒絕該消息.
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.
如果您需要從密碼生成密鑰,請使用內(nèi)置類:System.Security.Cryptography.PasswordDeriveBytes.這提供了加鹽和迭代,可以提高派生密鑰的強度并減少密鑰泄露時發(fā)現(xiàn)密碼的機會.
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.你還需要確保你有一個反重放系統(tǒng).如果您只是加密消息并傳遞它,那么任何收到消息的人都可以重新發(fā)送它.為避免這種情況,您應(yīng)該在消息中添加時間戳.如果時間戳與某個閾值不同,則拒絕該消息.您可能還想在其中包含一個一次性 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.
在包含計時信息時,務(wù)必確保進行哈希驗證.否則,如果您未檢測到此類蠻力嘗試,有人可能會篡改一些密文并可能生成有效的時間戳.
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 并將它們添加到您的輸出中.它還將執(zhí)行身份驗證步驟,確保未修改加密數(shù)據(jù).
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 愛麗絲;鮑勃;Eve;: PerformAct2 成功:檢測到篡改.System.Security.Cryptography.CryptographicException:消息哈希不正確.在AesDemo.Decrypt(字符串密碼,字節(jié)[]密碼鹽,字節(jié)[]密文)在C:Program.cs:行46 在 AesDemo.Main() 中C:Program.cs:行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
去掉隨機IV和哈希后,輸出的類型如下:
After removing the random IV and the hash, here's the type of output:
tZfHJSFTXYX8V38AqEfYVXU5Dl/meUVAond70yIKGHY=tZfHJSFTXYX8V38AqEfYVcf9a3U8vIEk1LuqGEyRZXM=
tZfHJSFTXYX8V38AqEfYVXU5Dl/meUVAond70yIKGHY= tZfHJSFTXYX8V38AqEfYVcf9a3U8vIEk1LuqGEyRZXM=
注意第一個塊如何對應(yīng)Alice; Bob; Eve;"是一樣的.確實是角落案例".
Notice how the first block, corresponding to "Alice; Bob; Eve;" is the same. "Corner case" indeed.
這是一個傳遞 64 位整數(shù)的簡單示例.只需加密,您就會受到攻擊.事實上,攻擊很容易完成,即使使用 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
(字節(jié)):A9-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
所以,如果這是您發(fā)送的那種 ID,它可以很容易地更改為另一個值.您需要在消息之外進行身份驗證.有時,消息結(jié)構(gòu)不太可能落實到位并且可以起到保護作用,但為什么要依賴可能改變的東西呢?無論應(yīng)用程序如何,您都需要能夠依賴您的加密貨幣正常工作.
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.
這篇關(guān)于ASP.NET 中的 AES 與 VB.NET的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!