| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- using System.Text;
- using System.Text.RegularExpressions;
- using SixLabors.ImageSharp;
- using SixLabors.ImageSharp.Formats.Png;
- using SixLabors.ImageSharp.PixelFormats;
- namespace VberZero.Tools;
- public static class ImageLSB_Encoder
- {
- #region 隐藏、提取 消息
- #region Base64
- /// <summary>
- /// 将给定的消息(文件)隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="path"></param>
- /// <param name="message"></param>
- /// <returns></returns>
- public static string HideMessageBackBase64(string path, string message)
- {
- var payload = Encoding.UTF8.GetBytes(message);
- return HideBytesBackBase64(path, payload);
- }
- /// <summary>
- /// 将给定的消息(文件)隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="carrierImage"></param>
- /// <param name="message"></param>
- /// <returns></returns>
- public static string HideMessageBackBase64(this Image<Rgba32> carrierImage, string message)
- {
- var payload = Encoding.UTF8.GetBytes(message);
- return HideBytesBackBase64(carrierImage, payload);
- }
- /// <summary>
- /// 将给定的消息(文件)隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="path"></param>
- /// <param name="payload"></param>
- /// <returns></returns>
- public static string HideBytesBackBase64(string path, byte[] payload)
- {
- using var carrierImage = (Image<Rgba32>)Image.Load(path);
- return HideBytesBackBase64(carrierImage, payload);
- }
- /// <summary>
- /// 将给定的消息(文件)隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="carrierImage"></param>
- /// <param name="payload"></param>
- /// <returns></returns>
- public static string HideBytesBackBase64(this Image<Rgba32> carrierImage, byte[] payload)
- {
- var image = HideBytes(carrierImage, payload);
- var base64Str = image.ToBase64String(PngFormat.Instance);
- return base64Str;
- }
- /// <summary>
- /// 从stego图像(base64String)中提取字符串消息
- /// </summary>
- /// <param name="base64String"></param>
- /// <returns></returns>
- public static string ExtractMessageFromBase64(string base64String)
- {
- base64String = base64String.StartsWith("data") ? base64String.Split(',')[1] : base64String;
- byte[] data = Convert.FromBase64String(base64String);
- using var image = (Image<Rgba32>)Image.Load(data);
- var payload = ExtractBytes(image);
- var message = Encoding.UTF8.GetString(payload);
- return message;
- }
- /// <summary>
- /// 从stego图像(base64String)中提取消息并将其作为字节数组返回
- /// </summary>
- /// <param name="base64String"></param>
- /// <returns></returns>
- public static byte[] ExtractBytesFromBase64(string base64String)
- {
- base64String = base64String.StartsWith("data") ? base64String.Split(',')[1] : base64String;
- byte[] data = Convert.FromBase64String(base64String);
- using var image = (Image<Rgba32>)Image.Load(data);
- var payload = ExtractBytes(image);
- return payload;
- }
- #endregion Base64
- #region Hide
- /// <summary>
- /// 将给定的消息隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="path"></param>
- /// <param name="message"></param>
- /// <returns></returns>
- public static Image<Rgba32> HideMessage(string path, string message)
- {
- using var carrierImage = (Image<Rgba32>)Image.Load(path);
- return HideMessage(carrierImage, message);
- }
- /// <summary>
- /// 将给定的消息隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="carrierImage"></param>
- /// <param name="message"></param>
- /// <returns></returns>
- public static Image<Rgba32> HideMessage(this Image<Rgba32> carrierImage, string message)
- {
- var payload = Encoding.UTF8.GetBytes(message);
- return HideBytes(carrierImage, payload);
- }
- /// <summary>
- /// 将给定的消息(文件)隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="path"></param>
- /// <param name="payload"></param>
- /// <returns></returns>
- public static Image<Rgba32> HideBytes(string path, byte[] payload)
- {
- using var carrierImage = (Image<Rgba32>)Image.Load(path);
- return HideBytes(carrierImage, payload);
- }
- /// <summary>
- /// 将给定的消息(文件)隐藏在给定的载体(图像)中
- /// </summary>
- /// <param name="carrierImage"></param>
- /// <param name="payload"></param>
- /// <returns></returns>
- public static Image<Rgba32> HideBytes(this Image<Rgba32> carrierImage, byte[] payload)
- {
- //var stegoImage = carrierImage.Clone();
- var stegoImage = carrierImage.Clone();
- //获取消息对象的二进制字符串及其长度
- var completeMessage = GenerateMessageBitPattern(payload);
- var completeMessageLength = (uint)completeMessage.Length;
- //如果消息太大,则引发异常
- var carrierCapacity = CalculateCapacity(carrierImage, "bits");
- if (completeMessageLength > carrierCapacity) throw new Exception("MessageTooBigException");
- var pixelX = 0;
- var pixelY = 0;
- var messageBitCounter = 0;
- while (messageBitCounter < completeMessageLength)
- {
- if (pixelY >= stegoImage.Height)
- {
- break;
- }
- // 获取当前像素
- var pixel = stegoImage[pixelX, pixelY];
- //选择使用像素的三个LSB中的R、G、B
- byte color;
- switch (messageBitCounter % 3)
- {
- case 0:
- color = InsertMessageBit(pixel.R, completeMessage[messageBitCounter].ToString());
- stegoImage[pixelX, pixelY] = new Rgba32(color, pixel.G, pixel.B, pixel.A);
- break;
- case 1:
- color = InsertMessageBit(pixel.G, completeMessage[messageBitCounter].ToString());
- stegoImage[pixelX, pixelY] = new Rgba32(pixel.R, color, pixel.B, pixel.A);
- break;
- case 2:
- color = InsertMessageBit(pixel.B, completeMessage[messageBitCounter].ToString());
- stegoImage[pixelX, pixelY] = new Rgba32(pixel.R, pixel.G, color, pixel.A);
- // 获取下一个像素
- pixelX++;
- if (pixelX >= stegoImage.Width)
- {
- pixelX = 0;
- pixelY++;
- }
- break;
- }
- messageBitCounter++;
- }
- return stegoImage;
- }
- #endregion Hide
- #region Extract
- /// <summary>
- /// 从stego图像中提取字符串消息
- /// </summary>
- /// <param name="path"></param>
- /// <returns></returns>
- public static string ExtractMessage(string path)
- {
- using var image = (Image<Rgba32>)Image.Load(path);
- var payload = ExtractBytes(image);
- var message = Encoding.UTF8.GetString(payload);
- return message;
- }
- /// <summary>
- /// 从stego图像中提取字符串消息
- /// </summary>
- /// <param name="carrierImage"></param>
- /// <returns></returns>
- public static string ExtractMessage(this Image<Rgba32> carrierImage)
- {
- var payload = ExtractBytes(carrierImage);
- var message = Encoding.UTF8.GetString(payload);
- return message;
- }
- /// <summary>
- /// 从stego图像中提取消息并将其作为字节数组返回
- /// </summary>
- /// <param name="path"></param>
- /// <returns></returns>
- public static byte[] ExtractBytes(string path)
- {
- using var image = (Image<Rgba32>)Image.Load(path);
- var payload = ExtractBytes(image);
- return payload;
- }
- /// <summary>
- /// 从stego图像中提取消息并将其作为字节数组返回
- /// </summary>
- /// <param name="stegoImage"></param>
- /// <returns></returns>
- public static byte[] ExtractBytes(this Image<Rgba32> stegoImage)
- {
- //StringBuilder messageNameBuilder = new StringBuilder();
- var payloadSizeBuilder = new StringBuilder();
- var payloadBuilder = new StringBuilder();
- Rgba32 pixel;
- var pixelX = 0;
- var pixelY = 0;
- var messageBitCounter = 0;
- // 提取有效载荷的大小
- while (messageBitCounter < 24)
- {
- if (pixelY >= stegoImage.Height)
- {
- break;
- }
- pixel = stegoImage[pixelX, pixelY];
- switch (messageBitCounter % 3)
- {
- case 0:
- payloadSizeBuilder.Append(ExtractLsbAsString(pixel.R));
- break;
- case 1:
- payloadSizeBuilder.Append(ExtractLsbAsString(pixel.G));
- break;
- case 2:
- payloadSizeBuilder.Append(ExtractLsbAsString(pixel.B));
- pixelX++;
- if (pixelX >= stegoImage.Width)
- {
- pixelX = 0;
- pixelY++;
- }
- break;
- }
- messageBitCounter++;
- }
- // 组成有效载荷的大小
- var payloadSizeString = payloadSizeBuilder.ToString();
- var payloadSize = BinaryToUint(payloadSizeString);
- // 提取有效载荷
- while (messageBitCounter < payloadSize + 24)
- {
- if (pixelY >= stegoImage.Height)
- {
- break;
- }
- pixel = stegoImage[pixelX, pixelY];
- switch (messageBitCounter % 3)
- {
- case 0:
- payloadBuilder.Append(ExtractLsbAsString(pixel.R));
- break;
- case 1:
- payloadBuilder.Append(ExtractLsbAsString(pixel.G));
- break;
- case 2:
- payloadBuilder.Append(ExtractLsbAsString(pixel.B));
- pixelX++;
- if (pixelX >= stegoImage.Width)
- {
- pixelX = 0;
- pixelY++;
- }
- break;
- }
- messageBitCounter++;
- }
- var payloadString = payloadBuilder.ToString();
- var payload = ConvertBitStringToByteArray(payloadString);
- return payload;
- }
- #endregion Extract
- #endregion 隐藏、提取 消息
- #region Private
- /// <summary>
- /// 计算完整消息(不仅是有效负载)和图像的LSB之间的汉明距离,并将它们作为整数返回(所有8字节最后一位置为偶数)
- /// </summary>
- /// <param name="carrierImage"></param>
- /// <param name="message"></param>
- /// <exception cref="ArgumentException"></exception>
- /// <exception cref="ArgumentOutOfRangeException"></exception>
- /// <returns></returns>
- private static int CalculateHammingDistance(Image<Rgba32> carrierImage, string message)
- {
- // 获取有关载体的所有必要信息
- uint carrierWidth = (uint)carrierImage.Width;
- uint carrierHeight = (uint)carrierImage.Height;
- uint maxCarrierPixels = (carrierWidth * carrierHeight);
- uint capacity = (3 * maxCarrierPixels);
- if (message.Length > capacity)
- {
- throw new Exception("MessageTooBigException");
- }
- // 收集加扰载体的LSB
- string carrierLsb = CollectCarrierLsb(carrierImage);
- // 计算Hamming距离
- int hammingDistance = 0;
- int lsbCounter = 0;
- foreach (char bit in message)
- {
- // 如果数组的索引达到2 ^ 31,则需要重新设置。这是因为数组只能通过int值访问
- if (lsbCounter == int.MaxValue)
- {
- carrierLsb = carrierLsb.Substring(lsbCounter);
- lsbCounter = 0;
- }
- // 如果消息位和LSB不匹配,则增加汉明距离
- if (!Equals(bit, carrierLsb[lsbCounter]))
- {
- hammingDistance++;
- }
- lsbCounter++;
- }
- return hammingDistance;
- }
- /// <summary>
- /// 收集从像素排序的给定载体图像的所有LSB
- /// (0,0)到(xMax,yMax)以及颜色R,G,B,并将它们作为字符串返回
- /// </summary>
- /// <param name="carrierImage"></param>
- private static string CollectCarrierLsb(Image<Rgba32> carrierImage)
- {
- StringBuilder sb = new StringBuilder();
- int width = carrierImage.Width;
- int height = carrierImage.Height;
- // 在整个图像上循环
- for (int y = 0; y < height; y++)
- {
- for (int x = 0; x < width; x++)
- {
- var pixel = carrierImage[x, y];
- sb.Append(ExtractLsbAsString(pixel.R));
- sb.Append(ExtractLsbAsString(pixel.G));
- sb.Append(ExtractLsbAsString(pixel.B));
- }
- }
- return sb.ToString();
- }
- /// <summary>
- /// 从消息对象生成二进制位字符串
- /// </summary>
- /// <param name="payload"></param>
- /// <exception cref="ArgumentException"></exception>
- /// <exception cref="ArgumentOutOfRangeException"></exception>
- /// <returns></returns>
- private static string GenerateMessageBitPattern(byte[] payload)
- {
- //将数据转换为二进制字符串
- var payloadSize = (uint)payload.Length * 8;
- var payloadBinary = ByteArrayToBinary(payload);
- var payloadSizeBinary = DecimalToBinary(payloadSize, 24);
- var sb = new StringBuilder();
- sb.Append(payloadSizeBinary);
- sb.Append(payloadBinary);
- return sb.ToString();
- }
- /// <summary>
- /// 在任意字节值中插入新的LSB
- /// </summary>
- /// <param name="carrierByte"></param>
- /// <param name="msgBit"></param>
- /// <returns></returns>
- private static byte InsertMessageBit(byte carrierByte, string msgBit)
- {
- var sb = new StringBuilder(DecimalToBinary(carrierByte, 8));
- sb.Remove(sb.Length - 1, 1);
- sb.Append(msgBit);
- var stegoByte = BinaryToByte(sb.ToString());
- return stegoByte;
- }
- /// <summary>
- /// 从字节中提取LSB并将其作为字符串返回
- /// </summary>
- /// <param name="inputByte"></param>
- private static string ExtractLsbAsString(byte inputByte)
- {
- return inputByte % 2 == 0 ? "0" : "1";
- }
- private static uint CalculateCapacity(Image carrier, string unit)
- {
- var width = (uint)carrier.Width;
- var height = (uint)carrier.Height;
- switch (unit)
- {
- case "bit":
- return 3 * width * height;
- case "byte":
- return 3 * width * height / 8;
- case "kB":
- return 3 * width * height / 8192;
- case "mB":
- return 3 * width * height / 8192 / 1024;
- case null:
- return 3 * width * height;
- default:
- return 3 * width * height;
- }
- }
- #endregion Private
- #region Converter
- /// <summary>
- /// 将文本字符串转换为二进制字符串模式
- /// </summary>
- /// <param name="str"></param>
- /// <param name="zeroPadding"></param>
- /// <returns></returns>
- private static string StringToBinary(string str, int zeroPadding)
- {
- var encoding = new UTF8Encoding();
- var buffer = encoding.GetBytes(str);
- if (buffer.Length > 64) throw new Exception("MessageNameTooBigException");
- if (buffer.Length < 64) Array.Resize(ref buffer, zeroPadding);
- var sb = new StringBuilder();
- foreach (var element in buffer) sb.Append(Convert.ToString(element, 2).PadLeft(8, '0'));
- return sb.ToString();
- }
- /// <summary>
- /// 将 int 对象转换为其二进制字符串表示形式,并用左侧指定数量的零
- /// </summary>
- /// <param name="decimalNumber"></param>
- /// <param name="zeroPadding"></param>
- /// <returns></returns>
- private static string DecimalToBinary(int decimalNumber, int zeroPadding = 0)
- {
- if (zeroPadding != 0) return Convert.ToString(decimalNumber, 2).PadLeft(zeroPadding, '0');
- return Convert.ToString(decimalNumber, 2);
- }
- /// <summary>
- /// 将 uint 对象转换为其二进制字符串表示形式,并用左侧指定数量的零
- /// </summary>
- /// <param name="decimalNumber"></param>
- /// <param name="zeroPadding"></param>
- /// <returns></returns>
- private static string DecimalToBinary(uint decimalNumber, int zeroPadding = 0)
- {
- if (zeroPadding != 0) return Convert.ToString(decimalNumber, 2).PadLeft(zeroPadding, '0');
- return Convert.ToString(decimalNumber, 2);
- }
- /// <summary>
- /// 将 byte 对象转换为其二进制字符串表示形式,并用左侧指定数量的零
- /// </summary>
- /// <param name="decimalNumber"></param>
- /// <param name="zeroPadding"></param>
- /// <returns></returns>
- private static string DecimalToBinary(byte decimalNumber, int zeroPadding = 0)
- {
- if (zeroPadding != 0) return Convert.ToString(decimalNumber, 2).PadLeft(zeroPadding, '0');
- return Convert.ToString(decimalNumber, 2);
- }
- /// <summary>
- /// 将字节数组转换为其二进制字符串表示形式
- /// </summary>
- /// <param name="array"></param>
- /// <returns></returns>
- private static string ByteArrayToBinary(byte[] array)
- {
- var sb = new StringBuilder();
- foreach (var by in array) sb.Append(DecimalToBinary(by, 8));
- return sb.ToString();
- }
- /// <summary>
- /// 将二进制字符串模式转换为其各自的UTF8文本表示
- /// </summary>
- /// <param name="str"></param>
- /// <returns></returns>
- private static string BinaryToString(string str)
- {
- var temp = BinaryToByteArray(str);
- var val = Encoding.UTF8.GetString(temp);
- return val;
- }
- /// <summary>
- /// 将二进制字符串模式转换为字节变量
- /// </summary>
- /// <param name="binary"></param>
- /// <returns></returns>
- private static byte BinaryToByte(string binary)
- {
- var by = Convert.ToByte(binary, 2);
- return by;
- }
- /// <summary>
- /// 将二进制字符串模式转换为字节数组
- /// </summary>
- /// <param name="binary"></param>
- /// <returns></returns>
- private static byte[] BinaryToByteArray(string binary)
- {
- return ConvertBitStringToByteArray(binary);
- }
- /// <summary>
- /// 将二进制字符串模式转换为 uint 变量
- /// </summary>
- /// <param name="binary"></param>
- /// <returns></returns>
- private static uint BinaryToUint(string binary)
- {
- var unsignedInt = Convert.ToUInt32(binary, 2);
- return unsignedInt;
- }
- /// <summary>
- /// 将存储位值的uint变量转换为存储kB值的双变量
- /// </summary>
- /// <param name="bits"></param>
- /// <returns></returns>
- private static double BitsToKiloBytes(uint bits)
- {
- // ReSharper disable once PossibleLossOfFraction
- float bytes = bits / 8;
- var kiloBytes = bytes / 1024;
- return Math.Round(kiloBytes, 5);
- }
- private static byte[] ConvertBitStringToByteArray(string source)
- {
- var data =
- Regex.Matches(source, ".{8}")
- .Select(m => Convert.ToByte(m.Groups[0].Value, 2))
- .ToArray();
- return data;
- }
- #endregion Converter
- }
|