2009年3月7日 星期六

AS3 作 3DES 編碼,與 .NET 互通資料

使用 AS3 想要進行資料加密的工作時,可以參考 as3crypto:

http://code.google.com/p/as3crypto/

以及其範例:

http://crypto.hurlant.com/demo/


假設,我們要在 .NET 中將一段明碼編碼:



會得到:

Key(Base64): Lns8zSrn2h4VGb5y1NBBHPcNDGjTHjna
IV(Hex): 41b362c1231999fc
編碼結果:GAUNI2sJCx32yFiF1J0AC5YPbbXcizYl

然後將這些資料貼進 Demo 的網頁後:



按下 Decrypt,就可以解出原來的明碼 (我就不重複貼圖了)





另外一個情況是,反過來,當 Flash 透過 3DES 進行編碼後,要提供資料給 .NET 的話:



則會提供:

Key(Base64): Lns8zSrn2h4VGb5y1NBBHPcNDGjTHjna
IV(Base64): 0sqxgBNq7iA=
編碼結果:XcMt/RnEoozLmBLUpLNRTPVMjRykzDxR9wvrKV9Zwwo=

範例程式的 Key Format 下拉選單好像有點問題,無法將 Hex 資料轉成 Base64,懶得解 bug,所以這裡的 Key(Base64) 可以直接用剛剛由 .NET 產生的繼續使用即可。

IV(Base64) 的部分,因為原範例程式只有顯示 IV(Hex),所以這部分我有修改原範例程式,多顯示一種格式的資料,便於提供給 .NET 使用。


將資料貼進 .NET 之後,按下 Decrypt 就順利解出來囉:






.NET 的原始碼,主架構可以由 MSDN 的範例找到,
http://msdn.microsoft.com/zh-tw/library/system.security.cryptography.tripledescryptoserviceprovider(VS.80).aspx

為了能編中文,所以我將原本的 ASCIIEncoding() 改為 System.Text.Encoding.UTF8。

並且設定了 填補模式 為 PKCS7(在 AS3 中用的是 PKCS#5,放心,有通!):
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
tDESalg.Padding = PaddingMode.PKCS7;

完整程式碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Security.Cryptography;
using System.IO;

namespace TripleDES
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void btnEncrypt_Click(object sender, EventArgs e)
{
try
{
// Create a new TripleDESCryptoServiceProvider object
// to generate a key and initialization vector (IV).
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();

// Create a string to encrypt.
string sData = txtSource.Text;

if (txtKey.Text == "")
{
txtKey.Text = Convert.ToBase64String(tDESalg.Key);
}
else
{
tDESalg.Key = Convert.FromBase64String(txtKey.Text);
}

if (txtIV.Text == "")
{
txtIV.Text = Convert.ToBase64String(tDESalg.IV);
}
else
{
tDESalg.IV = Convert.FromBase64String(txtIV.Text);
}

String hexIV = "";
for (int i = 0; i < tDESalg.IV.Length; i++)
{
hexIV += Convert.ToString(tDESalg.IV[i], 16);
}
txtIV2.Text = hexIV;

// Encrypt the string to an in-memory buffer.
byte[] Data = EncryptTextToMemory(sData, tDESalg.Key, tDESalg.IV);

txtResult.Text = Convert.ToBase64String(Data);
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}

}

private void btnDecrypt_Click(object sender, EventArgs e)
{
try
{

String hexIV = txtIV2.Text;
hexIV = System.Text.RegularExpressions.Regex.Replace(hexIV, "/\\s|:/gm", "");
if ((hexIV.Length & 1) == 1) hexIV = "0" + hexIV;
byte[] iv = new byte[hexIV.Length/2];
for (int i = 0; i < iv.Length; i++) {
iv[i] = Convert.ToByte(hexIV.Substring(i * 2, 2), 16);
}


txtResult2.Text = DecryptTextFromMemory(Convert.FromBase64String(txtResult.Text)
, Convert.FromBase64String(txtKey.Text)
, Convert.FromBase64String(txtIV.Text));
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}

public static byte[] EncryptTextToMemory(string Data, byte[] Key, byte[] IV)
{
try
{
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
tDESalg.Padding = PaddingMode.PKCS7;


// Create a MemoryStream.
MemoryStream mStream = new MemoryStream();

// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream cStream = new CryptoStream(mStream,
tDESalg.CreateEncryptor(Key, IV),
CryptoStreamMode.Write);

// Convert the passed string to a byte array.
//byte[] toEncrypt = new ASCIIEncoding().GetBytes(Data);
byte[] toEncrypt = System.Text.Encoding.UTF8.GetBytes(Data);

// Write the byte array to the crypto stream and flush it.
cStream.Write(toEncrypt, 0, toEncrypt.Length);
cStream.FlushFinalBlock();

// Get an array of bytes from the
// MemoryStream that holds the
// encrypted data.
byte[] ret = mStream.ToArray();

// Close the streams.
cStream.Close();
mStream.Close();

// Return the encrypted buffer.
return ret;
}
catch (CryptographicException e)
{
Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);
return null;
}

}

public static string DecryptTextFromMemory(byte[] Data, byte[] Key, byte[] IV)
{
try
{
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
tDESalg.Padding = PaddingMode.PKCS7;

// Create a new MemoryStream using the passed
// array of encrypted data.
MemoryStream msDecrypt = new MemoryStream(Data);

// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
tDESalg.CreateDecryptor(Key, IV),
CryptoStreamMode.Read);

// Create buffer to hold the decrypted data.
byte[] fromEncrypt = new byte[Data.Length];

// Read the decrypted data out of the crypto stream
// and place it into the temporary buffer.
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);

//Convert the buffer into a string and return it.
return System.Text.Encoding.UTF8.GetString(fromEncrypt);
}
catch (CryptographicException e)
{
Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);
return null;
}
}



}


}


至於我自己改寫的 Flex 範例,原則上只是因為在原範例中,IV 是編碼後所自動產生的,並可用於解碼,我將範例改成可以使用我們所填入文字欄位提供的 IV 值作編碼;以及上面我提過的有多顯示 IV(Base64) 的欄位。我就不再轉貼落落長的原始碼了。

3 則留言:

SimpleMinded 提到...

Hi, I can only decrypt if the encryption is done on the same page. How do we allow it to be decrypted on a separate tier?

Ben Chang 提到...

你的 separate tier 指的是?都在 client 端的 flash?還是分在 asp.net 與 flash?

在我們實作過程中,只遇到不同技術對於一些加解密所需參數的定義有所不同,也就是我提到的:

----------------------
並且設定了 填補模式 為 PKCS7(在 AS3 中用的是 PKCS#5,放心,有通!):
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
tDESalg.Padding = PaddingMode.PKCS7;
----------------------

總之,解密時所需的所有參數,要去看看加密過程中是否有產生,然後可以提供給解密過程使用,譬如 IV 值

SimpleMinded 提到...

是分在 asp.net 與 flash.
flash - Encrypt
asp.net - Decrypt