当前位置:   article > 正文

AES-128-ECB/CBC 查表法 C#实现_c# aes ecb 128

c# aes ecb 128

遇到一段汇编代码,没认出来是查表法的AES。这里完全用字节处理,来实现AES加密计算,来加深一下对查表法AES的印象。

由于采用对字节的操作,会造成很多无畏的内存读写,运算速度肯定没有对uint(4字节)来的快。本来反汇编出来的就是uint的,这边只是想学习具体细节,才转换成字节的。后续有空了,再把整理后的uint操作的代码贴出来吧。

主要参考了:

查表法的理论计算过程

部分代码参考

部分代码参考2

字节操作函数

  1. /// <summary>
  2. /// 字节数组->uint,大端模式
  3. /// </summary>
  4. /// <param name="buf"></param>
  5. /// <param name="index"></param>
  6. /// <returns></returns>
  7. public static uint ReadUint_BE(byte[] buf, int index)
  8. {
  9. uint d1 = buf[index + 3]; // buf的高位,成为retData的尾端
  10. uint d2 = (uint)(buf[index + 2] << 8);
  11. uint d3 = (uint)(buf[index + 1] << 16);
  12. uint d4 = (uint)(buf[index] << 24);
  13. return d1 | d2 | d3 | d4;
  14. }
  15. public static void WriteUint_BE(byte[] buf, int index, uint data)
  16. {
  17. buf[index + 3] = (byte)(data); // data低8位(尾端)写到buf的高位
  18. buf[index + 2] = (byte)(data >> 8);
  19. buf[index + 1] = (byte)(data >> 16);
  20. buf[index + 0] = (byte)(data >> 24); // data高8位写到buf的低位
  21. }
  22. /// <summary>
  23. /// 字节数组->uint,小端模式
  24. /// </summary>
  25. /// <param name="buf"></param>
  26. /// <param name="index"></param>
  27. /// <returns></returns>
  28. public static uint ReadUint_LE(byte[] buf, int index)
  29. {
  30. uint d1 = buf[index]; // buf的低位,成为retData的尾端
  31. uint d2 = (uint)(buf[index + 1] << 8);
  32. uint d3 = (uint)(buf[index + 2] << 16);
  33. uint d4 = (uint)(buf[index + 3] << 24);
  34. return d1 | d2 | d3 | d4;
  35. }
  36. public static void WriteUint_LE(byte[] buf, int index, uint data)
  37. {
  38. buf[index] = (byte)data; // data低8位(尾端)写道buf的低位
  39. buf[index + 1] = (byte)(data >> 8);
  40. buf[index + 2] = (byte)(data >> 16);
  41. buf[index + 3] = (byte)(data >> 24);
  42. }
  43. public static byte HIBYTE(uint data) => (byte)((data >> 24) & 0xff);
  44. public static byte BYTE2(uint data) => (byte)((data >> 16) & 0xff);
  45. public static byte BYTE1(uint data) => (byte)((data >> 8) & 0xff);
  46. public static byte LOWBYTE(uint data) => (byte)(data & 0xff);

生成SBOX

为了避免位移运算,这里对sbox进行偏移,得到4个uint[]的sbox

  1. public static byte[] create_sbox()
  2. {
  3. byte[] a = { 0xf1, 0xe3, 0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8 };
  4. byte b = 0x63, x1, x, y, z, i = 0x00;
  5. byte[] sbox = new byte[0x100];
  6. int j, k;
  7. do
  8. {
  9. z = 0x00;
  10. x = calc_inverse(i);
  11. for (j = 0; j < 8; j++)
  12. {
  13. z = (byte)(z >> 1);
  14. y = (byte)(a[j] & x);
  15. x1 = 0x00;
  16. for (k = 0; k < 8; k++)
  17. {
  18. x1 ^= (byte)(y & 0x80);
  19. y = (byte)(y << 1);
  20. }
  21. z ^= x1;
  22. }
  23. z ^= b;
  24. sbox[i] = z;
  25. i++;
  26. } while (i != 0x00);
  27. return sbox;
  28. }
  29. public static void create_shift_sbox(byte[] oriSbox, out uint[] sbox0, out uint[] sbox1, out uint[] sbox2, out uint[] sbox3)
  30. {
  31. sbox0 = new uint[256];
  32. for (int idx = 0; idx < 0x100; idx++)
  33. sbox0[idx] = oriSbox[idx];
  34. sbox1 = new uint[256];
  35. for (int idx = 0; idx < 0x100; idx++)
  36. sbox1[idx] = (uint)(oriSbox[idx] << 8);
  37. sbox2 = new uint[256];
  38. for (int idx = 0; idx < 0x100; idx++)
  39. sbox2[idx] = (uint)(oriSbox[idx] << 0x10);
  40. sbox3 = new uint[256];
  41. for (int idx = 0; idx < 0x100; idx++)
  42. sbox3[idx] = (uint)(oriSbox[idx] << 0x18);
  43. }
  44. /// <summary>
  45. /// 计算逆元
  46. /// </summary>
  47. private static byte calc_inverse(byte tar)
  48. {
  49. if (tar == 0x00)
  50. return 0x00;
  51. for (byte i = 0x01; i != 0x00; i++)
  52. if (gf_mul(tar, i) == 0x01)
  53. return i;
  54. throw new Exception("不对劲,没有正常return");
  55. }
  56. /// <summary>
  57. /// 乘法运算
  58. /// </summary>
  59. /// <param name="a"></param>
  60. /// <param name="b"></param>
  61. /// <returns></returns>
  62. private static byte gf_mul(byte a, byte b)
  63. {
  64. byte[] data = new byte[8];
  65. data[0] = a;
  66. data[1] = (byte)(data[0] << 1);
  67. if ((data[0] & 0x80) == 0x80) data[1] ^= 0x1b;
  68. data[2] = (byte)(data[1] << 1);
  69. if ((data[1] & 0x80) == 0x80) data[2] ^= 0x1b;
  70. data[3] = (byte)(data[2] << 1);
  71. if ((data[2] & 0x80) == 0x80) data[3] ^= 0x1b;
  72. data[4] = (byte)(data[3] << 1);
  73. if ((data[3] & 0x80) == 0x80) data[4] ^= 0x1b;
  74. data[5] = (byte)(data[4] << 1);
  75. if ((data[4] & 0x80) == 0x80) data[5] ^= 0x1b;
  76. data[6] = (byte)(data[5] << 1);
  77. if ((data[5] & 0x80) == 0x80) data[6] ^= 0x1b;
  78. data[7] = (byte)(data[6] << 1);
  79. if ((data[6] & 0x80) == 0x80) data[7] ^= 0x1b;
  80. byte dat = 0x00;
  81. if ((b & 0x01) == 0x01) dat ^= data[0];
  82. if ((b & 0x02) == 0x02) dat ^= data[1];
  83. if ((b & 0x04) == 0x04) dat ^= data[2];
  84. if ((b & 0x08) == 0x08) dat ^= data[3];
  85. if ((b & 0x10) == 0x10) dat ^= data[4];
  86. if ((b & 0x20) == 0x20) dat ^= data[5];
  87. if ((b & 0x40) == 0x40) dat ^= data[6];
  88. if ((b & 0x80) == 0x80) dat ^= data[7];
  89. return dat;
  90. }

生成TBOX

  1. /// <summary>
  2. /// 2 1 1 3
  3. /// 3 2 1 1
  4. /// 1 3 2 1
  5. /// 1 1 3 2
  6. /// </summary>
  7. /// <param name="sbox"></param>
  8. /// <param name="tbox0">2 1 1 3</param>
  9. /// <param name="tbox1">3 2 1 1</param>
  10. /// <param name="tbox2">1 3 2 1</param>
  11. /// <param name="tbox3">1 1 3 2</param>
  12. public static void create_tbox(byte[] sbox, out uint[] tbox0, out uint[] tbox1, out uint[] tbox2, out uint[] tbox3)
  13. {
  14. tbox0 = new uint[256];
  15. tbox1 = new uint[256];
  16. tbox2 = new uint[256];
  17. tbox3 = new uint[256];
  18. for (int i = 0; i < 0x100; i++)
  19. {
  20. //byte mul1 = gf_mul(sbox[i], 1);
  21. byte mul1 = sbox[i];
  22. byte mul2 = gf_mul(sbox[i], 2);
  23. byte mul3 = gf_mul(sbox[i], 3);
  24. tbox0[i] = (uint)((mul2 << 0x18) | (mul1 << 0x10) | (mul1 << 8) | mul3);
  25. tbox1[i] = (uint)((mul3 << 0x18) | (mul2 << 0x10) | (mul1 << 8) | mul1);
  26. tbox2[i] = (uint)((mul1 << 0x18) | (mul3 << 0x10) | (mul2 << 8) | mul1);
  27. tbox3[i] = (uint)((mul1 << 0x18) | (mul1 << 0x10) | (mul3 << 8) | mul2);
  28. }
  29. }

秘钥拓展

  1. /// <summary>
  2. /// Rcon
  3. /// </summary>
  4. public static uint[] ROUND_PARAMS_B = new uint[10]
  5. {
  6. 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36,
  7. };
  8. public static byte[] create_round_keys_bytes(byte[] keys)
  9. {
  10. byte[] expKey = new byte[0xb0];
  11. for (int i = 0; i < 0x10; i += 4) // mem copy
  12. {
  13. // 大小端转换
  14. expKey[i] = keys[i + 3];
  15. expKey[i + 1] = keys[i + 2];
  16. expKey[i + 2] = keys[i + 1];
  17. expKey[i + 3] = keys[i];
  18. }
  19. for (int i = 0x10, rounds = 0; i < 0xb0; i += 0x10, rounds++)
  20. {
  21. // 前一个4字节uint
  22. byte b0 = expKey[i - 4];
  23. byte b1 = expKey[i - 3];
  24. byte b2 = expKey[i - 2];
  25. byte b3 = expKey[i - 1];
  26. expKey[i] = (byte)(expKey[i - 0x10] ^ SBOX0[b3]);
  27. expKey[i + 1] = (byte)(expKey[i - 0x10 + 1] ^ SBOX0[b0]);
  28. expKey[i + 2] = (byte)(expKey[i - 0x10 + 2] ^ SBOX0[b1]);
  29. expKey[i + 3] = (byte)(expKey[i - 0x10 + 3] ^ SBOX0[b2] ^ ROUND_PARAMS_B[rounds]);
  30. // 分组后12B,循环处理
  31. for (int j = 4; j < 0x10; j++)
  32. expKey[i + j] = (byte)(expKey[i + j - 4] ^ expKey[i + j - 0x10]);
  33. }
  34. return expKey;
  35. }

分组加密

  1. /// <summary>
  2. /// 4个SBOX,分别对应:
  3. /// SBOX0[i]: SBOX_ori[i]
  4. /// SBOX1[i]: SBOX_ori[i] << 8
  5. /// SBOX2[i]: SBOX_ori[i] << 0x10
  6. /// SBOX3[i]: SBOX_ori[i] << 0x18
  7. /// </summary>
  8. public static uint[] SBOX0;
  9. public static uint[] SBOX1;
  10. public static uint[] SBOX2;
  11. public static uint[] SBOX3;
  12. /// <summary>
  13. /// 4个TBOX,根据矩阵运算得到
  14. /// </summary>
  15. public static uint[] TBOX0;
  16. public static uint[] TBOX1;
  17. public static uint[] TBOX2;
  18. public static uint[] TBOX3;
  19. public static void init_box()
  20. {
  21. byte[] oriSbox = create_sbox();
  22. create_shift_sbox(oriSbox, out SBOX0, out SBOX1, out SBOX2, out SBOX3);
  23. create_tbox(oriSbox, out TBOX0, out TBOX1, out TBOX2, out TBOX3);
  24. }
  25. /// <summary>
  26. /// 分组加密运算,aes-128,没个分组计算10轮
  27. /// </summary>
  28. public static void round_encrypt(byte[] data, byte[] expKey)
  29. {
  30. byte A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15;
  31. byte D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15;
  32. uint u0 = ReadUint_LE(expKey, 0) ^ ReadUint_BE(data, 0);
  33. uint u1 = ReadUint_LE(expKey, 4) ^ ReadUint_BE(data, 4);
  34. uint u2 = ReadUint_LE(expKey, 8) ^ ReadUint_BE(data, 8);
  35. uint u3 = ReadUint_LE(expKey, 0xc) ^ ReadUint_BE(data, 0xc);
  36. uint wa0, wa1, wa2, wa3;
  37. int halfRound = (0xa >> 1) - 1; // 一重循环做两次
  38. for (int i = 0x10; ; i += 0x20)
  39. {
  40. // 因为大小端的原因,这里的高位字节,其实是字节数组中的低位,高尾端模式:HI在低位。
  41. A0 = HIBYTE(u0);
  42. A1 = BYTE2(u0);
  43. A2 = BYTE1(u0);
  44. A3 = LOWBYTE(u0);
  45. A4 = HIBYTE(u1);
  46. A5 = BYTE2(u1);
  47. A6 = BYTE1(u1);
  48. A7 = LOWBYTE(u1);
  49. A8 = HIBYTE(u2);
  50. A9 = BYTE2(u2);
  51. A10 = BYTE1(u2);
  52. A11 = LOWBYTE(u2);
  53. A12 = HIBYTE(u3);
  54. A13 = BYTE2(u3);
  55. A14 = BYTE1(u3);
  56. A15 = LOWBYTE(u3);
  57. wa0 = TBOX0[A0] ^ TBOX1[A5] ^ TBOX2[A10] ^ TBOX3[A15] ^ ReadUint_LE(expKey, i);
  58. wa1 = TBOX0[A4] ^ TBOX1[A9] ^ TBOX2[A14] ^ TBOX3[A3] ^ ReadUint_LE(expKey, i + 4);
  59. wa2 = TBOX0[A8] ^ TBOX1[A13] ^ TBOX2[A2] ^ TBOX3[A7] ^ ReadUint_LE(expKey, i + 8);
  60. wa3 = TBOX0[A12] ^ TBOX1[A1] ^ TBOX2[A6] ^ TBOX3[A11] ^ ReadUint_LE(expKey, i + 0xc);
  61. // 还是要注意大小端。小端模式下:尾部LO在低位0
  62. D0 = HIBYTE(wa0);
  63. D1 = BYTE2(wa0);
  64. D2 = BYTE1(wa0);
  65. D3 = LOWBYTE(wa0);
  66. D4 = HIBYTE(wa1);
  67. D5 = BYTE2(wa1);
  68. D6 = BYTE1(wa1);
  69. D7 = LOWBYTE(wa1);
  70. D8 = HIBYTE(wa2);
  71. D9 = BYTE2(wa2);
  72. D10 = BYTE1(wa2);
  73. D11 = LOWBYTE(wa2);
  74. D12 = HIBYTE(wa3);
  75. D13 = BYTE2(wa3);
  76. D14 = BYTE1(wa3);
  77. D15 = LOWBYTE(wa3);
  78. if (halfRound == 0) break; // 为了循环能写简单一点、为了能精简汇编代码,原来的9轮循环,改成了现在2*4 + 1轮循环。实际上感觉这是汇编代码优化后的结果。不是刻意写成这样的。
  79. u0 = TBOX0[D0] ^ TBOX1[D5] ^ TBOX2[D10] ^ TBOX3[D15] ^ ReadUint_LE(expKey, i + 0x10);
  80. u1 = TBOX0[D4] ^ TBOX1[D9] ^ TBOX2[D14] ^ TBOX3[D3] ^ ReadUint_LE(expKey, i + 0x14);
  81. u2 = TBOX0[D8] ^ TBOX1[D13] ^ TBOX2[D2] ^ TBOX3[D7] ^ ReadUint_LE(expKey, i + 0x18);
  82. u3 = TBOX0[D12] ^ TBOX1[D1] ^ TBOX2[D6] ^ TBOX3[D11] ^ ReadUint_LE(expKey, i + 0x1c);
  83. halfRound = halfRound - 1;
  84. }
  85. uint v39 = SBOX0[D15] ^ SBOX1[D10] ^ SBOX2[D5] ^ SBOX3[D0] ^ ReadUint_LE(expKey, 0xa0);
  86. uint v40 = SBOX0[D3] ^ SBOX1[D14] ^ SBOX2[D9] ^ SBOX3[D4] ^ ReadUint_LE(expKey, 0xa4);
  87. uint v42 = SBOX0[D7] ^ SBOX1[D2] ^ SBOX2[D13] ^ SBOX3[D8] ^ ReadUint_LE(expKey, 0xa8);
  88. uint v35 = SBOX0[D11] ^ SBOX1[D6] ^ SBOX2[D1] ^ SBOX3[D12] ^ ReadUint_LE(expKey, 0xac);
  89. data[0] = HIBYTE(v39);
  90. data[1] = BYTE2(v39);
  91. data[2] = BYTE1(v39);
  92. data[3] = LOWBYTE(v39);
  93. data[4] = HIBYTE(v40);
  94. data[5] = BYTE2(v40);
  95. data[6] = BYTE1(v40);
  96. data[7] = LOWBYTE(v40);
  97. data[8] = HIBYTE(v42);
  98. data[9] = BYTE2(v42);
  99. data[10] = BYTE1(v42);
  100. data[11] = LOWBYTE(v42);
  101. data[12] = HIBYTE(v35);
  102. data[13] = BYTE2(v35);
  103. data[14] = BYTE1(v35);
  104. data[15] = LOWBYTE(v35);
  105. }

加密部分

这里采用in-place计算,加密结果直接写回到data。

  1. /// <summary>
  2. /// ECB模式
  3. /// </summary>
  4. /// <param name="data">数据部分保证16字节对齐</param>
  5. /// <param name="key">秘钥长度为16字节</param>
  6. public static void aes_128_ecb_encrypt(byte[] data, byte[] key)
  7. {
  8. byte[] expKey = create_round_keys_bytes(key);
  9. byte[] buf = new byte[0x10];
  10. for (int i = 0; i < data.Length; i += 0x10)
  11. {
  12. Array.Copy(data, i, buf, 0, 0x10);
  13. round_encrypt(buf, expKey);
  14. Array.Copy(buf, 0, data, i, 0x10);
  15. }
  16. }
  17. /// <summary>
  18. /// CBC模式
  19. /// </summary>
  20. /// <param name="data">数据部分保证16字节对齐</param>
  21. /// <param name="key">秘钥长度为16字节</param>
  22. public static void aes_128_cbc_encrypt(byte[] data, byte[] key, byte[] iv)
  23. {
  24. byte[] expKey = create_round_keys_bytes(key);
  25. byte[] buf = new byte[0x10];
  26. for (int i = 0; i < data.Length; i += 0x10)
  27. {
  28. Array.Copy(data, i, buf, 0, 0x10);
  29. for (int j = 0; j < 16; j++)
  30. buf[j] = (byte)(iv[j] ^ buf[j]);
  31. round_encrypt(buf, expKey);
  32. Array.Copy(buf, 0, data, i, 0x10);
  33. Array.Copy(buf, 0, iv, i, 0x10);
  34. }
  35. }

测试程序

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. MyAes.init_box();
  6. byte[] keys = ByteUtils.FromHexString("2E82BFAFF4E0C26D1D032A8F90803EAE");
  7. byte[] iv = ByteUtils.FromHexString("7315254F862899A133B3A832C2F78E0E");
  8. // ecb测试
  9. byte[] testData = new byte[0x10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
  10. Console.WriteLine(ByteUtils.ToHexString(testData));
  11. Console.WriteLine(ByteUtils.ToHexString(keys));
  12. MyAes.aes_128_ecb_encrypt(testData, keys);
  13. Console.WriteLine(ByteUtils.ToHexString(testData));
  14. Console.WriteLine();
  15. // cbc测试
  16. testData = new byte[0x10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
  17. Console.WriteLine(ByteUtils.ToHexString(testData));
  18. Console.WriteLine(ByteUtils.ToHexString(keys));
  19. Console.WriteLine(ByteUtils.ToHexString(iv));
  20. MyAes.aes_128_cbc_encrypt(testData, keys, iv);
  21. Console.WriteLine(ByteUtils.ToHexString(testData));
  22. Console.ReadKey();
  23. }
  24. }

测试结果

 实际计算结果,和采用公共加密库计算得到的结果一致。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/51814
推荐阅读
相关标签
  

闽ICP备14008679号