iT邦幫忙

0

請教3DES加密,使用CBC模式和ISO9797-1的問題

  • 分享至 

  • xImage

各位大師好,
小弟目前想開發使用3DES,並以CBC模式和ISO9797-1的算法來加密,

https://ithelp.ithome.com.tw/upload/images/20210808/20115079XaMnLvsELm.jpg

加密規則如上方圖片所示,輸入data不限定是幾字元,
想得出與其一樣的輸出,但嘗試了很久,都沒辦法得到預期的加密結果(確定這軟體加密是無誤的)

預期輸出:4BFDF58BCD4494003B73B6DD1338F5E74D9656983984217E1E0ADDA7AB6C225F22A038536B54F95B
我的輸出:1FB0FACB9E5F9D9A

以下是我的程式碼,懇請各位高手指正一下,非常感謝。

string Key_MAC = "447FC2AA6EFFFEE5405A559E88DC958C";
            string input = "201801221009512692302018013521567";

            byte[] kMAC = Encoding.UTF8.GetBytes(Key_MAC);
            byte[] eIfd = Encoding.ASCII.GetBytes(input);

            // Split the 16 byte MAC key into two keys
            byte[] key1 = new byte[8];
            Array.Copy(kMAC, 0, key1, 0, 8);
            byte[] key2 = new byte[8];
            Array.Copy(kMAC, 8, key2, 0, 8);

            // Padd the data with Padding Method 2 (Bit Padding)
            System.IO.MemoryStream out_Renamed = new System.IO.MemoryStream();
            out_Renamed.Write(eIfd, 0, eIfd.Length);
            out_Renamed.WriteByte((byte)(0x80));
            while (out_Renamed.Length % 8 != 0)
            {
                out_Renamed.WriteByte((byte)0x00);
            }
            byte[] eIfd_padded = out_Renamed.ToArray();

            // Split the blocks
            byte[] d1 = new byte[8];
            byte[] d2 = new byte[8];
            byte[] d3 = new byte[8];
            byte[] d4 = new byte[8];
            byte[] d5 = new byte[8];
            Array.Copy(eIfd_padded, 0, d1, 0, 8);
            Array.Copy(eIfd_padded, 8, d2, 0, 8);
            Array.Copy(eIfd_padded, 16, d3, 0, 8);
            Array.Copy(eIfd_padded, 24, d4, 0, 8);
            Array.Copy(eIfd_padded, 32, d5, 0, 8);

            DES des1 = DES.Create();
            des1.BlockSize = 64;
            des1.Key = key1;
            des1.Mode = CipherMode.CBC;
            des1.Padding = PaddingMode.None;
            des1.IV = new byte[8];

            DES des2 = DES.Create();
            des2.BlockSize = 64;
            des2.Key = key2;
            des2.Mode = CipherMode.CBC;
            des2.Padding = PaddingMode.None;
            des2.IV = new byte[8];

            // MAC Algorithm 3
            // Initial Transformation 1
            byte[] h1 = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
            // Iteration on the rest of blocks
            // XOR
            byte[] int2 = new byte[8];
            for (int i = 0; i < 8; i++)
                int2[i] = (byte)(h1[i] ^ d2[i]);
            // Encrypt
            byte[] h2 = des1.CreateEncryptor().TransformFinalBlock(int2, 0, 8);
            // XOR
            byte[] int3 = new byte[8];
            for (int i = 0; i < 8; i++)
                int3[i] = (byte)(h2[i] ^ d3[i]);
            // Encrypt
            byte[] h3 = des1.CreateEncryptor().TransformFinalBlock(int3, 0, 8);
            // XOR
            byte[] int4 = new byte[8];
            for (int i = 0; i < 8; i++)
                int4[i] = (byte)(h3[i] ^ d4[i]);
            // Encrypt
            byte[] h4 = des1.CreateEncryptor().TransformFinalBlock(int4, 0, 8);
            // XOR
            byte[] int5 = new byte[8];
            for (int i = 0; i < 8; i++)
                int5[i] = (byte)(h4[i] ^ d5[i]);
            // Encrypt
            byte[] h5 = des1.CreateEncryptor().TransformFinalBlock(int5, 0, 8);

            // Output Transformation 3
            byte[] h5decrypt = des2.CreateDecryptor().TransformFinalBlock(h5, 0, 8);
            byte[] mIfd = des1.CreateEncryptor().TransformFinalBlock(h5decrypt, 0, 8);

            string result = "";
            for (int i = 0; i < mIfd.Length; i++)
            {
                result += mIfd[i].ToString("X2"); // hex format
            }

            label1.Text = result;
            //輸出為: 1FB0FACB9E5F9D9A
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

0
淺水員
iT邦大師 6 級 ‧ 2021-08-09 01:09:04
最佳解答

沒寫過 C# ,不過目前我看到的問題:

  1. Key_MAC 轉成 bytes,應該用 16 進位表示法來解釋,長度應該是 16 bytes 。(目前使用 Encoding.UTF8.GetBytes 是錯誤的,會產生 32 bytes)
  2. 範例中的 padding 是採用 method 1,而不是 method 2。另外 method 1 依據定義,可以直接用 PaddingMode.Zeros 試試看
  3. 3DES 應該是每個 block 都用 3DES 去算,然後才用 CBC 連接起來。所以不能夠用 DES-CBC 達成。也許要用 TripleDES 類別TripleDESCryptoServiceProvider
淺水員 iT邦大師 6 級 ‧ 2021-08-09 03:21:25 檢舉

剪剪貼貼改一下,有跑出來。

參考資料:

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

public class Test
{
    public static void Main()
    {

        string input = "201801221009512692302018013521567";
        string Key_MAC = "447FC2AA6EFFFEE5405A559E88DC958C";
        string IVHex = "0000000000000000";

        byte[] data = Encoding.ASCII.GetBytes(input);
        byte[] key = StringToByteArray(Key_MAC);
        byte[] iv = StringToByteArray(IVHex);

        byte[] result = EncryptTextToMemory(data, key, iv);
        Console.WriteLine(ByteArrayToString(result));
    }

    public static byte[] EncryptTextToMemory(byte[] toEncrypt,  byte[] Key, byte[] IV)
    {
        try
        {
        // Create a MemoryStream.
        MemoryStream mStream = new MemoryStream();

        //這邊有修改過,因為預設的 padding 不是 zero
        TripleDESCryptoServiceProvider dec = new TripleDESCryptoServiceProvider();
        dec.Padding = PaddingMode.Zeros;
        // Create a CryptoStream using the MemoryStream
        // and the passed key and initialization vector (IV).
        CryptoStream cStream = new CryptoStream(
            mStream,
            dec.CreateEncryptor(Key, IV),
            CryptoStreamMode.Write
        );

        // Convert the passed string to a byte array.

        // 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 byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }
    public static string ByteArrayToString(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
            hex.AppendFormat("{0:x2}", b);
        return hex.ToString();
    }
}
hank444tw iT邦新手 5 級 ‧ 2021-08-09 21:51:55 檢舉

這一類的加密算法真有點複雜,
感謝您的指正~

淺水員 iT邦大師 6 級 ‧ 2021-08-12 22:07:29 檢舉

閒著自己用 javascript 全程刻一個當練習
可以純前端網頁計算而不用透過伺服器
DEMO:DES、3DES 計算機
原始碼:https://github.com/ren1244/des-calculator/

我要發表回答

立即登入回答