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
///
/// 将给定的消息(文件)隐藏在给定的载体(图像)中
///
///
///
///
public static string HideMessageBackBase64(string path, string message)
{
var payload = Encoding.UTF8.GetBytes(message);
return HideBytesBackBase64(path, payload);
}
///
/// 将给定的消息(文件)隐藏在给定的载体(图像)中
///
///
///
///
public static string HideMessageBackBase64(this Image carrierImage, string message)
{
var payload = Encoding.UTF8.GetBytes(message);
return HideBytesBackBase64(carrierImage, payload);
}
///
/// 将给定的消息(文件)隐藏在给定的载体(图像)中
///
///
///
///
public static string HideBytesBackBase64(string path, byte[] payload)
{
using var carrierImage = (Image)Image.Load(path);
return HideBytesBackBase64(carrierImage, payload);
}
///
/// 将给定的消息(文件)隐藏在给定的载体(图像)中
///
///
///
///
public static string HideBytesBackBase64(this Image carrierImage, byte[] payload)
{
var image = HideBytes(carrierImage, payload);
var base64Str = image.ToBase64String(PngFormat.Instance);
return base64Str;
}
///
/// 从stego图像(base64String)中提取字符串消息
///
///
///
public static string ExtractMessageFromBase64(string base64String)
{
base64String = base64String.StartsWith("data") ? base64String.Split(',')[1] : base64String;
byte[] data = Convert.FromBase64String(base64String);
using var image = (Image)Image.Load(data);
var payload = ExtractBytes(image);
var message = Encoding.UTF8.GetString(payload);
return message;
}
///
/// 从stego图像(base64String)中提取消息并将其作为字节数组返回
///
///
///
public static byte[] ExtractBytesFromBase64(string base64String)
{
base64String = base64String.StartsWith("data") ? base64String.Split(',')[1] : base64String;
byte[] data = Convert.FromBase64String(base64String);
using var image = (Image)Image.Load(data);
var payload = ExtractBytes(image);
return payload;
}
#endregion Base64
#region Hide
///
/// 将给定的消息隐藏在给定的载体(图像)中
///
///
///
///
public static Image HideMessage(string path, string message)
{
using var carrierImage = (Image)Image.Load(path);
return HideMessage(carrierImage, message);
}
///
/// 将给定的消息隐藏在给定的载体(图像)中
///
///
///
///
public static Image HideMessage(this Image carrierImage, string message)
{
var payload = Encoding.UTF8.GetBytes(message);
return HideBytes(carrierImage, payload);
}
///
/// 将给定的消息(文件)隐藏在给定的载体(图像)中
///
///
///
///
public static Image HideBytes(string path, byte[] payload)
{
using var carrierImage = (Image)Image.Load(path);
return HideBytes(carrierImage, payload);
}
///
/// 将给定的消息(文件)隐藏在给定的载体(图像)中
///
///
///
///
public static Image HideBytes(this Image 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
///
/// 从stego图像中提取字符串消息
///
///
///
public static string ExtractMessage(string path)
{
using var image = (Image)Image.Load(path);
var payload = ExtractBytes(image);
var message = Encoding.UTF8.GetString(payload);
return message;
}
///
/// 从stego图像中提取字符串消息
///
///
///
public static string ExtractMessage(this Image carrierImage)
{
var payload = ExtractBytes(carrierImage);
var message = Encoding.UTF8.GetString(payload);
return message;
}
///
/// 从stego图像中提取消息并将其作为字节数组返回
///
///
///
public static byte[] ExtractBytes(string path)
{
using var image = (Image)Image.Load(path);
var payload = ExtractBytes(image);
return payload;
}
///
/// 从stego图像中提取消息并将其作为字节数组返回
///
///
///
public static byte[] ExtractBytes(this Image 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
///
/// 计算完整消息(不仅是有效负载)和图像的LSB之间的汉明距离,并将它们作为整数返回(所有8字节最后一位置为偶数)
///
///
///
///
///
///
private static int CalculateHammingDistance(Image 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;
}
///
/// 收集从像素排序的给定载体图像的所有LSB
/// (0,0)到(xMax,yMax)以及颜色R,G,B,并将它们作为字符串返回
///
///
private static string CollectCarrierLsb(Image 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();
}
///
/// 从消息对象生成二进制位字符串
///
///
///
///
///
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();
}
///
/// 在任意字节值中插入新的LSB
///
///
///
///
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;
}
///
/// 从字节中提取LSB并将其作为字符串返回
///
///
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
///
/// 将文本字符串转换为二进制字符串模式
///
///
///
///
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();
}
///
/// 将 int 对象转换为其二进制字符串表示形式,并用左侧指定数量的零
///
///
///
///
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);
}
///
/// 将 uint 对象转换为其二进制字符串表示形式,并用左侧指定数量的零
///
///
///
///
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);
}
///
/// 将 byte 对象转换为其二进制字符串表示形式,并用左侧指定数量的零
///
///
///
///
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);
}
///
/// 将字节数组转换为其二进制字符串表示形式
///
///
///
private static string ByteArrayToBinary(byte[] array)
{
var sb = new StringBuilder();
foreach (var by in array) sb.Append(DecimalToBinary(by, 8));
return sb.ToString();
}
///
/// 将二进制字符串模式转换为其各自的UTF8文本表示
///
///
///
private static string BinaryToString(string str)
{
var temp = BinaryToByteArray(str);
var val = Encoding.UTF8.GetString(temp);
return val;
}
///
/// 将二进制字符串模式转换为字节变量
///
///
///
private static byte BinaryToByte(string binary)
{
var by = Convert.ToByte(binary, 2);
return by;
}
///
/// 将二进制字符串模式转换为字节数组
///
///
///
private static byte[] BinaryToByteArray(string binary)
{
return ConvertBitStringToByteArray(binary);
}
///
/// 将二进制字符串模式转换为 uint 变量
///
///
///
private static uint BinaryToUint(string binary)
{
var unsignedInt = Convert.ToUInt32(binary, 2);
return unsignedInt;
}
///
/// 将存储位值的uint变量转换为存储kB值的双变量
///
///
///
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
}