SecurityUtils.RC4.cs 4.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. using System.Text;
  2. namespace XGame.Framework.Utils
  3. {
  4. /// <summary>
  5. /// 安全性工具类
  6. /// </summary>
  7. public static partial class SecurityUtils
  8. {
  9. /// <summary>
  10. /// rc4 密钥字节数组长度
  11. /// </summary>
  12. private const short RC4_KEY_LENGTH = 256;
  13. /// <summary>
  14. /// 用于密钥索引计算
  15. /// 位操作( index & 255 )比余数快一点( index % 256 )
  16. /// </summary>
  17. private const byte RC4_KEY_LIMIT = 0xFF;//255
  18. /// <summary>
  19. /// 状态向量S
  20. /// </summary>
  21. private static readonly byte[] RC4_KEY_INDEXS = new byte[RC4_KEY_LENGTH];
  22. /// <summary>
  23. /// RC4算法的特点是算法简单,运行速度快,而且密钥长度是可变的,可变范围为1-256字节(8-2048比特),
  24. /// RC4加密算法中的几个关键变量:
  25. /// 1、密钥流:RC4算法的关键是根据明文和密钥生成相应的密钥流,密钥流的长度和明文的长度是对应的,也就是说明文的长度是500字节,那么密钥流也是500字节。当然,加密生成的密文也是500字节,因为密文第i字节=明文第i字节^密钥流第i字节;
  26. /// 2、状态向量S:长度为256,S[0],S[1].....S[255]。每个单元都是一个字节,算法运行的任何时候,S都包括0-255的8比特数的排列组合,只不过值的位置发生了变换;
  27. /// 3、临时向量T:长度也为256,每个单元也是一个字节。如果密钥的长度是256字节,就直接把密钥的值赋给T,否则,轮转地将密钥的每个字节赋给T;
  28. /// 4、密钥K:长度为1-256字节,注意密钥的长度keylen与明文长度、密钥流的长度没有必然关系,通常密钥的长度趣味16字节(128比特)。
  29. /// RC4步骤:1、初始化S和T 2、初始排列S 3、产生密钥流
  30. /// 本质上是打乱密钥顺序的异或加密
  31. /// </summary>
  32. /// <param name="pwd">密钥</param>
  33. /// <param name="input">输入数组</param>
  34. /// <param name="offset">数据偏移值</param>
  35. /// <param name="length">加密的数据长度</param>
  36. /// <param name="output">输出,长度和input一致</param>
  37. public static void RC4(byte[] pwd, byte[] input, int offset, int length, byte[] output)
  38. {
  39. Assert.IsFalse(pwd == null || pwd.Length == 0, "RC4密钥不能为空.");
  40. Assert.IsFalse(input == null || input.Length == 0, "RC4输入数据内容不能为空.");
  41. Assert.IsFalse(output == null || output.Length == 0, "RC4输出数据不能为空.");
  42. Assert.IsTrue(input.Length == output.Length, $"RC4输入输出数据长度必须一致.Input:{input.Length} Output:{output.Length}");
  43. Assert.IsFalse(offset < 0 || length <= 0 || offset + length > input.Length, $"RC4数据偏移值错误.offset:{offset} length:{length} inputL:{input.Length}");
  44. int a, i, j, k;
  45. var pwdLength = pwd.Length;
  46. //打乱密码,密码,密码箱长度
  47. for (i = 0; i < RC4_KEY_LENGTH; i++)
  48. { // 先重置索引
  49. RC4_KEY_INDEXS[i] = (byte)i;
  50. }
  51. for (j = i = 0; i < RC4_KEY_LENGTH; i++)
  52. {
  53. j = (j + RC4_KEY_INDEXS[i] + pwd[i % pwdLength]) & RC4_KEY_LIMIT; // 位操作( &255 )比余数快一点( %256 )
  54. Swap(RC4_KEY_INDEXS, i, j);
  55. }
  56. for (j = i = 0; i < length; i++)
  57. {
  58. a = (i + 1) & RC4_KEY_LIMIT;
  59. j = (j + RC4_KEY_INDEXS[a]) & RC4_KEY_LIMIT;
  60. Swap(RC4_KEY_INDEXS, a, j);
  61. k = RC4_KEY_INDEXS[(RC4_KEY_INDEXS[a] + RC4_KEY_INDEXS[j]) & RC4_KEY_LIMIT];
  62. output[i + offset] = (byte)(input[i + offset] ^ k);
  63. }
  64. }
  65. private static void Swap(byte[] s, int i, int j)
  66. {
  67. var c = s[i];
  68. s[i] = s[j];
  69. s[j] = c;
  70. }
  71. /// <summary>
  72. /// 加密字节数组
  73. /// 加密直接替换原数组内容
  74. /// </summary>
  75. /// <param name="pwd">密钥</param>
  76. /// <param name="bytes">加密源&输出</param>
  77. public static void RC4(byte[] pwd, byte[] bytes)
  78. {
  79. RC4(pwd, bytes, 0, bytes.Length, bytes);
  80. }
  81. /// <summary>
  82. /// 十六进制字符串加密
  83. /// 返回十六进制字符串
  84. /// </summary>
  85. /// <param name="pwd">密钥,不强制使用十六进制</param>
  86. /// <param name="hexString">必须是十六进制的字符串</param>
  87. /// <returns></returns>
  88. public static string RC4(string pwd, string hexString)
  89. {
  90. byte[] key = Encoding.UTF8.GetBytes(pwd);
  91. byte[] bytes = hexString.ToHexBytes();
  92. RC4(key, bytes, 0, bytes.Length, bytes);
  93. return bytes.ToHexString();
  94. }
  95. }
  96. }