C# and Blowfish加解密實做

blowfish是資料可逆的對稱金鑰加密法, 金鑰長度可變動。

User只提供5筆用blowfish加密的密文和解密後的明文資料, 網路上少有討論, 找到的案例也都加解失敗, 好不容易找到有個對岸同胞的網站, 提供的加解密測試能用,  猜想應該是文字檔的編碼差異。最後抓Simias.Encryption版本來改(https://github.com/b1thunt3r/blowfish-csharp/tree/master/Simias.Encryption)。

此次作業主要有

加密

  1. 用正確編碼讀檔後轉binary
  2. binary字串補不足8位元的部份(padding), 再丟進Simias.Encryption加密
  3. 加密後字串轉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();