本文概述
- 1.创建所需的帮助程序类
- 2.隐藏和检索图像上的信息
【使用C#在图像上进行隐写术(隐藏信息)入门】在本文中, 我们将向你展示如何在C#中隐藏图像文件(JPG, PNG等)中的加密信息。
1.创建所需的帮助程序类你将需要创建2个类, 并将它们添加到C#项目中。第一个是SteganographyHelper。此类负责隐藏位图上的信息并进行检索。它也有一个帮助程序方法来创建图像版本, 而无需索引像素:
using System;
using System.Drawing;
class SteganographyHelper{enum State{HIDING, FILL_WITH_ZEROS};
/// <
summary>
/// Creates a bitmap from an image without indexed pixels/// <
/summary>
/// <
param name="src">
<
/param>
/// <
returns>
<
/returns>
public static Bitmap CreateNonIndexedImage(Image src){Bitmap newBmp = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics gfx = Graphics.FromImage(newBmp)){gfx.DrawImage(src, 0, 0);
}return newBmp;
}public static Bitmap MergeText(string text, Bitmap bmp){State s = State.HIDING;
int charIndex = 0;
int charValue = http://www.srcmini.com/0;
long colorUnitIndex = 0;
int zeros = 0;
int R = 0, G = 0, B = 0;
for (int i = 0;
i <
bmp.Height;
i++){for (int j = 0;
j <
bmp.Width;
j++){Color pixel = bmp.GetPixel(j, i);
pixel = Color.FromArgb(pixel.R - pixel.R % 2, pixel.G - pixel.G % 2, pixel.B - pixel.B % 2);
R = pixel.R;
G = pixel.G;
B = pixel.B;
for (int n = 0;
n <
3;
n++){if (colorUnitIndex % 8 == 0){if (zeros == 8){if ((colorUnitIndex - 1) % 3 <
2){bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
}return bmp;
}if (charIndex >
= text.Length){s = State.FILL_WITH_ZEROS;
}else{charValue = text[charIndex++];
}}switch (colorUnitIndex % 3){case 0:{if (s == State.HIDING){R += charValue % 2;
charValue /= 2;
}}break;
case 1:{if (s == State.HIDING){G += charValue % 2;
charValue /= 2;
}}break;
case 2:{if (s == State.HIDING){B += charValue % 2;
charValue /= 2;
}bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
}break;
}colorUnitIndex++;
if (s == State.FILL_WITH_ZEROS){zeros++;
}}}}return bmp;
}public static string ExtractText(Bitmap bmp){int colorUnitIndex = 0;
int charValue = 0;
string extractedText = String.Empty;
for (int i = 0;
i <
bmp.Height;
i++){for (int j = 0;
j <
bmp.Width;
j++){Color pixel = bmp.GetPixel(j, i);
for (int n = 0;
n <
3;
n++){switch (colorUnitIndex % 3){case 0:{charValue = charValue * 2 + pixel.R % 2;
}break;
case 1:{charValue = charValue * 2 + pixel.G % 2;
}break;
case 2:{charValue = charValue * 2 + pixel.B % 2;
}break;
}colorUnitIndex++;
if (colorUnitIndex % 8 == 0){charValue = reverseBits(charValue);
if (charValue == 0){return extractedText;
}char c = (char)charValue;
extractedText += c.ToString();
}}}}return extractedText;
}public static int reverseBits(int n){int result = 0;
for (int i = 0;
i <
8;
i++){result = result * 2 + n % 2;
n /= 2;
}return result;
}}
第二个是StringCipher类。通过此类, 我们将加密你要隐藏在文件中的信息, 这显然会增加对机密数据的保护。它的所有方法都是静态的, 因此你每次加密或解密内容时都无需创建新实例:
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
class StringCipher{// This constant is used to determine the keysize of the encryption algorithm in bits.// We divide this by 8 within the code below to get the equivalent number of bytes.private const int Keysize = 256;
// This constant determines the number of iterations for the password bytes generation function.private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase){// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text// so that the same Salt and IV values can be used when decrypting.var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)){var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged()){symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes)){using (var memoryStream = new MemoryStream()){using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)){cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}}}}}}public static string Decrypt(string cipherText, string passPhrase){// Get the complete stream of bytes that represent:// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)){var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged()){symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)){using (var memoryStream = new MemoryStream(cipherTextBytes)){using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)){var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}}}}}}private static byte[] Generate256BitsOfRandomEntropy(){var randomBytes = new byte[32];
// 32 Bytes will give us 256 bits.using (var rngCsp = new RNGCryptoServiceProvider()){// Fill the array with cryptographically secure random bytes.rngCsp.GetBytes(randomBytes);
}return randomBytes;
}}
2.隐藏和检索图像上的信息使用第一步的Providen Helper类, 你将可以在图像上隐藏秘密信息, 并在几秒钟内检索它们:
A.隐藏和加密数据
要隐藏文件中的信息, 你需要声明密码。此密码使你以后可以从文件中检索信息。然后将要隐藏在图像上的信息存储在字符串变量中, 该信息也将使用相同的密码进行加密。接下来, 创建两个变量, 这些变量将存储文件的路径(将使用该信息创建的原始文件和新文件), 并继续创建SteganographyHelper的新实例。
使用密码和帮助程序加密数据, 这将生成另一个字符串(将存储在图像上的字符串)。现在, 使用助手的CreateNonIndexedImage方法创建不带索引像素的图像版本非常重要。如果不这样做, 算法的使用将抛出(带有一些图像, 而不是全部图像)以下异常:
具有索引像素格式的图像不支持SetPixel。
使用帮助程序的MergeText方法, 将加密的文本隐藏在图像的未索引版本中, 并将其存储在所需的位置:
// Declare the password that will allow you to retrieve the encrypted data laterstring _PASSWORD = "password";
// The String data to conceal on the imagestring _DATA_TO_HIDE = "Hello, no one should know that my password is 12345";
// Declare the path where the original image is locatedstring pathOriginalImage = @"C:\Users\sdkca\Desktop\image_example.png";
// Declare the new name of the file that will be generated with the hidden informationstring pathResultImage = @"C:\Users\sdkca\Desktop\image_example_with_hidden_information.png";
// Create an instance of the SteganographyHelperSteganographyHelper helper = new SteganographyHelper();
// Encrypt your data to increase security// Remember: only the encrypted data should be stored on the imagestring encryptedData = http://www.srcmini.com/StringCipher.Encrypt(_DATA_TO_HIDE, _PASSWORD);
// Create an instance of the original image without indexed pixelsBitmap originalImage = SteganographyHelper.CreateNonIndexedImage(Image.FromFile(pathOriginalImage));
// Conceal the encrypted data on the image !Bitmap imageWithHiddenData = SteganographyHelper.MergeText(encryptedData, originalImage);
// Save the image with the hidden information somewhere :)// In this case the generated file will be image_example_with_hidden_information.pngimageWithHiddenData.Save(pathResultImage);
在这种情况下, 我们存储在图像上的信息是一个简单的字符串, 即” … 没有人知道我的密码是… ” 。
B.检索和解密数据
要从上一步创建的图像中检索数据, 你仅需要帮助程序的ExtractText方法。这将返回加密的信息, 你可以使用先前设置的密码轻松解密该信息:
// The password used to hide the information on the previous stepstring _PASSWORD = "password";
// The path to the image that contains the hidden informationstring pathImageWithHiddenInformation = @"C:\Users\sdkca\Desktop\image_example_with_hidden_information.png";
// Create an instance of the SteganographyHelperSteganographyHelper helper = new SteganographyHelper();
// Retrieve the encrypted data from the imagestring encryptedData = http://www.srcmini.com/SteganographyHelper.ExtractText(new Bitmap(Image.FromFile(pathImageWithHiddenInformation)));
// Decrypt the retrieven data on the imagestring decryptedData = StringCipher.Decrypt(encryptedData, _PASSWORD);
// Display the secret text in the console or in a messagebox// In our case is"Hello, no one should know that my password is 12345"Console.WriteLine(decryptedData);
//MessageBox.Show(decryptedData);
请注意, 在此实现中, 我们不知道错误或用户界面, 因此你可以实现它。此外, 你可能希望将要包装的代码包装在另一个线程中, 以防止UI冻结。
编码愉快!
推荐阅读
- 如何在WinForms C#中使用LiveCharts库创建饼图
- 如何在PHP中使用Imagick检查图像是否具有透明度
- 如何在Symfony 3的Twig视图的Dates上使用time_diff和ago(time ago)函数
- 如何在Twig中有条件地扩展模板或导入宏
- 如何防止XSS攻击并禁止PHP Markdown解析器生成的HTML中的特定标签
- 解决方案错误(名称为”yourtable.tablename”的表已存在)
- 如何在Symfony 3上的控制器(带有或不带有FOSUserBundle)中手动验证(登录)用户
- 如何在WinForms C#中使用LiveCharts库创建地理图表(GeoHeatMap)
- 如何在Microsoft Visual Studio Code中禁用自动补全和智能感知