ZeroWidthCodec.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. namespace Masuit.Tools.Security;
  7. /// <summary>
  8. /// 零宽字符串编解码器
  9. /// </summary>
  10. public static class ZeroWidthCodec
  11. {
  12. /// <summary>
  13. /// 注入零宽字符串
  14. /// </summary>
  15. /// <param name="s"></param>
  16. /// <param name="hidden"></param>
  17. /// <returns></returns>
  18. public static string InjectZeroWidthString(this string s, string hidden)
  19. {
  20. return Encrypt(s, hidden);
  21. }
  22. /// <summary>
  23. /// 编码为零宽字符串
  24. /// </summary>
  25. /// <param name="s"></param>
  26. /// <returns></returns>
  27. public static string EncodeToZeroWidthText(this string s)
  28. {
  29. return Encode(s);
  30. }
  31. /// <summary>
  32. /// 解码零宽字符串
  33. /// </summary>
  34. /// <param name="s"></param>
  35. /// <returns></returns>
  36. public static string DecodeZeroWidthString(this string s)
  37. {
  38. return Decrypt(s);
  39. }
  40. /// <summary>
  41. /// 注入零宽字符串
  42. /// </summary>
  43. /// <param name="pub">显示字符串</param>
  44. /// <param name="hidden">隐式字符串</param>
  45. /// <returns></returns>
  46. public static string Encrypt(string pub, string hidden)
  47. {
  48. // 将字符串拆分成单个字符
  49. var pubMsg = pub.ToCharArray();
  50. var hiddenMsg = Str2Bin(Encoding.UTF8.GetBytes(hidden));// 将需要隐藏的字符串转换成二进制格式
  51. hiddenMsg = Bin2Hidden(hiddenMsg);// 将二进制转换成隐藏字符格式
  52. hiddenMsg = $"\uFEFF{hiddenMsg}\uFEFF";// 用边界字符包装隐藏的字符
  53. if (pubMsg.Length == 1)
  54. {
  55. return pub + hiddenMsg;
  56. }
  57. // 将编码的隐藏消息注入到公共字符串的大约中间位置
  58. int half = pub.Length / 2;
  59. var chars = new List<char>();
  60. for (var i = 0; i < pubMsg.Length; i++)
  61. {
  62. if (i == half)
  63. {
  64. chars.AddRange(hiddenMsg);
  65. }
  66. chars.Add(pubMsg[i]);
  67. }
  68. return string.Concat(chars);
  69. }
  70. /// <summary>
  71. /// 编码为零宽字符串
  72. /// </summary>
  73. /// <param name="str"></param>
  74. /// <returns></returns>
  75. public static string Encode(string str)
  76. {
  77. var encodeText = new StringBuilder(string.Join(" ", Encoding.UTF8.GetBytes(str).Select(byt => Convert.ToString(byt, 2).PadLeft(8, '0'))))
  78. .Replace('\u0030', '\u200B')
  79. .Replace('\u0031', '\u200C')
  80. .Replace('\u0020', '\u200D')
  81. .ToString();
  82. return encodeText;
  83. }
  84. /// <summary>
  85. /// 解码零宽字符串
  86. /// </summary>
  87. /// <param name="pub"></param>
  88. /// <returns></returns>
  89. public static string Decrypt(string pub)
  90. {
  91. var unwrapped = Unwrap(pub);
  92. var message = Bin2Str(unwrapped == "false" ? Hidden2Bin(pub) : Hidden2Bin(unwrapped));
  93. return message.Length < 2 ? "No private message was found in that text." : message;
  94. }
  95. private static string Str2Bin(byte[] text)
  96. {
  97. return string.Join(" ", text.Select(byt => Convert.ToString(byt, 2).PadLeft(8, '0')));
  98. }
  99. private static string Unwrap(string text)
  100. {
  101. var tmp = text.Split("\uFEFF".ToCharArray());
  102. return tmp.Length == 1 ? "false" : tmp[1];
  103. }
  104. private static string Bin2Str(string bin)
  105. {
  106. bin = Regex.Replace(bin, "[^0-1]", " ");
  107. return Encoding.UTF8.GetString(Enumerable.Range(0, bin.Split().Length).Select(vw => Convert.ToByte(Regex.Replace(bin.Split()[vw], @"\s", ""), 2)).ToArray());
  108. }
  109. private static string Bin2Hidden(string text)
  110. {
  111. text = text.Replace(" ", "\u2060"); // Unicode Character 'WORD JOINER' (U+2060) 0xE2 0x81 0xA0
  112. text = text.Replace("0", "\u200B"); // Unicode Character 'ZERO WIDTH SPACE' (U+200B) 0xE2 0x80 0x8B
  113. text = text.Replace("1", "\u200C"); // Unicode Character 'ZERO WIDTH NON-JOINER' (U+200C) 0xE2 0x80 0x8C
  114. return text;
  115. }
  116. private static string Hidden2Bin(string text)
  117. {
  118. text = text.Replace("\u2060", " "); // Unicode Character 'WORD JOINER' (U+2060) 0xE2 0x81 0xA0
  119. text = text.Replace("\u200B", "0"); // Unicode Character 'ZERO WIDTH SPACE' (U+200B) 0xE2 0x80 0x8B
  120. text = text.Replace("\u200C", "1"); // Unicode Character 'ZERO WIDTH NON-JOINER' (U+200C) 0xE2 0x80 0x8C
  121. return text;
  122. }
  123. }