blowfish是資料可逆的對稱金鑰加密法, 金鑰長度可變動。
User只提供5筆用blowfish加密的密文和解密後的明文資料, 網路上少有討論, 找到的案例也都加解失敗, 好不容易找到有個對岸同胞的網站, 提供的加解密測試能用, 猜想應該是文字檔的編碼差異。最後抓Simias.Encryption版本來改(https://github.com/b1thunt3r/blowfish-csharp/tree/master/Simias.Encryption)。
此次作業主要有
加密
- 用正確編碼讀檔後轉binary
- binary字串補不足8位元的部份(padding), 再丟進Simias.Encryption加密
- 加密後字串轉Hex
解密就反過來
Padding處理:
以此例, blowfish加密法需要8位元為一組, 最後一段不足的要自行塞字元到8位元, 但客戶提供的內容, 每筆資料補的內容都不一樣, 沒辦法直接判斷, 且資料長度也不一樣, 無法直接截取固定長度, 後來用char判斷。
範例來源:http://csharpindepth.com/Articles/General/Strings.aspx
string是由char組成, 每個char可對應到一個ASCII Code, 英數字及符號為33到127內, 大過127可能為中文字或無法辦識字元, 小於32的為non printable characters:
小於32的non printable characters:string[] LowNames =
{
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
};
完整加密
long m_originalLength = 0;
--讀取明文內容檔案
using (FileStream originalStream = new FileStream(@"D:\src\blows\blows_import\blows_import\blows20180516_decrypted.txt", FileMode.Open, FileAccess.Read))
{
--決定讀檔編碼, 用key產生加密物件
Simias.Encryption.Blowfish alg = new Simias.Encryption.Blowfish(Encoding.UTF8.GetBytes("abcdefghijk"));
--轉二進位(binary)的容器
Byte[] bytes = new byte[originalStream.Length];
--轉二進位(binary)
originalStream.Read(bytes, 0, bytes.Length);
originalStream.Close();
--blowfish以8位元為單位, 位元數要整除8,此案例直接補一個0x01(測試資料長度為47), 實際上要和對方確認padding內容
--產生一個內容為0x01的byte array
byte[] adda01 = new byte[] { 0x01 };
int newSize = bytes.Length + adda01.Length;
--memorystream可merge兩個byte的array
var ms = new MemoryStream(new byte[newSize], 0, newSize, true, true);
ms.Write(bytes, 0, bytes.Length);
ms.Write(adda01, 0, adda01.Length);
byte[] merged = ms.GetBuffer();
--加密
alg.Encipher(merged, merged.Length);
--byte(8bit)轉Hex,需手動replace掉-
string str = BitConverter.ToString(merged).Replace("-", "").ToLower();
--確認寫檔編碼, 寫檔
FileStream fs = new FileStream(@"D:\src\blows\blows_import\blows_import\blows20180516_encrypted.txt", FileMode.Create);
StreamWriter sw = new StreamWriter(fs, new UTF8Encoding());
sw.Write(str, 0, str.Length-(int)diff_len);
sw.Close();
fs.Close();
}
完整解密
--讀取密文內容檔案
string entext = File.ReadAllText(@"D:\src\blows\blows_import\blows_import\blows20180516_encrypted.txt");
--因密文為Hex, 轉為byte
//運算後的位元組長度:16進位數字字串長/2
byte[] bytes = new byte[entext.Length / 2];
for (int i = 0; i < entext.Length; i = i + 2)
{
//每2位16進位數字轉換為一個10進位整數
bytes[i / 2] = Convert.ToByte(entext.Substring(i, 2), 16);
}
--決定讀檔編碼, 用key產生加密物件
Simias.Encryption.Blowfish alg = new Simias.Encryption.Blowfish(Encoding.Default.GetBytes("abcdefghijk"));
--解密
alg.Decipher(bytes, bytes.Length);
--確定編碼, 讀取byte轉utf8的string
string str = Encoding.UTF8.GetString(bytes);
--因客戶未提供實際padding內容, 直接轉char判斷是否為non printable words
string[] LowNames =
{
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
};
Console.WriteLine(str);
int a = 0;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (char c in str)
{
if (c < 32)
{
Console.WriteLine("<{0}> U+{1:x4}", LowNames[c], (int)c);
}
else if (c > 127)
{
Console.WriteLine("(Possibly non-printable) U+{0:x4}", (int)c);
sb.Append(c);
}
else
{
Console.WriteLine("{0} U+{1:x4}", c, (int)c);
sb.Append(c);
}
}
Console.WriteLine(sb);
--決定編碼方法後寫檔
FileStream fs = new FileStream(@"D:\src\blows\blows_import\blows_import\blows20180516_decrypted.txt", FileMode.Create);
StreamWriter sw = new StreamWriter(fs, new UTF8Encoding());
sw.Write(str, 0, str.Length);
sw.Close();
fs.Close();