记得在很久以前,听过一个朋友说过图片水印加密技术(他好像也是从书上看来的),图片水印加密技术——就是将文字信息转化为对应的二进制信息后,写入BMP图片中,例如某文字的二进制码为01001001,则分别将0、1、0、0、1、0、0、1写入bmp图片的八个像素中,从而达到隐藏信息的作用,最后也可用特定的程序将信息还原。
但在他告诉我的方法中,对文本编码方式有特殊要求,特别是中文——必须是GB2312编码,否则还原后的文字将是乱码。并且,仅仅这样对文字进行隐藏是不太安全的,对方只要有一个类似的加解密软件,便可将信息全部还原。
我在研究这种算法一段时间后,有了一些想法——我们可以现将文本信息进行一次AES加密(或其他的加密算法)后,在将加密后的密文写入bmp图片中。这样对文本信息进行处理后,密文既有隐藏式加密的隐秘性,又有算法式加密的复杂性,可谓是一种更强的加密方式!
因此,我将我的方法写成了一个.net类库,并改进了文本的二进制编码方式,使其不受特定编码的限制。
以下是代码,望高手赐教!!
(类库中包含AES加密模块,也可改为其他加密算法)
代码
Imports System.Security
Imports
System.Security.Cryptography
Imports
System.Text
Imports
System.IO
Imports
System.Runtime.Serialization.Formatters
Public
Class
AesWithBMP
<
Serializable()
>
Public
Structure
KeyFileStructure ‘可用此结构来创建一个密钥文件
Public
strKey
As
String
’密钥
Public
strIV
As
String
‘初始向量
Public
IsEnCryptWithBMP
As
Boolean
’是否写入bmp图片
Public
IsInputBMPWithEnCrypt
As
Boolean
‘写入bmp图片的内容是否经过AES加密
End Structure
Public
Sub
CreateKeyFile(
ByVal
strKey
As
String
,
ByVal
strIV
As
String
,
ByVal
IsEnCryptWithBMP
As
Boolean
,
ByVal
IsInputBMPWithEnCrypt
As
Boolean
,
ByVal
KeySaveName
As
String
,
Optional
ByVal
IsRewriteKeyFile
As
Boolean
=
False
) ’此函数用于在指定路径创建一个密钥文件
Dim
fStream
As
FileStream
=
Nothing
Try
If
Not
File.Exists(KeySaveName)
Then
Dim
KeyStructure
As
New
KeyFileStructure
With
KeyStructure
.strKey =
strKey
.strIV =
strIV
.IsEnCryptWithBMP =
IsEnCryptWithBMP
.IsInputBMPWithEnCrypt =
IsInputBMPWithEnCrypt
End
With
Dim
kFomatter
As
New
Binary.BinaryFormatter
fStream =
New
FileStream(KeySaveName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
kFomatter.Serialize(fStream, KeyStructure) ‘将密钥结构序列化
Else
If
IsRewriteKeyFile
Then
’当发现同名密钥文件时是否覆盖
Dim
KeyStructure
As
New
KeyFileStructure
With
KeyStructure
.strKey =
strKey
.strIV =
strIV
.IsEnCryptWithBMP =
IsEnCryptWithBMP
.IsInputBMPWithEnCrypt =
IsInputBMPWithEnCrypt
End
With
Dim
kFomatter
As
New
Binary.BinaryFormatter
fStream =
New
FileStream(KeySaveName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
kFomatter.Serialize(fStream, KeyStructure)
KeyStructure =
Nothing
End
If
End
If
Finally
If
fStream
IsNot
Nothing
Then
fStream.Close()
End
Try
End Sub
Public
Sub
GetKeyFile(
ByVal
SBKName
As
String
,
ByRef
out_KeyFile
As
KeyFileStructure) ‘此过程用于从密钥文件中获取密钥等信息
Dim
fStream
As
FileStream
=
Nothing
Try
Dim
kFomatter
As
New
Binary.BinaryFormatter
fStream =
New
FileStream(SBKName, FileMode.Open, FileAccess.Read)
out_KeyFile =
kFomatter.Deserialize(fStream)
Finally
If
fStream
IsNot
Nothing
Then
fStream.Close()
End
Try
End Sub
Private
Sub
EnCrypt(
ByVal
SourceFileName
As
String
,
ByVal
ResultFileName
As
String
,
ByVal
Key
As
String
,
ByVal
IV
As
String
) ’对指定文件进行AES加密,并保存到指定路径
Dim
myCryptoStream
As
CryptoStream
=
Nothing
Dim
SourceFileStream
As
FileStream
=
Nothing
Dim
ResultFileStream
As
FileStream
=
Nothing
Dim
myAESProvider
As
AesCryptoServiceProvider
=
Nothing
Try
myAESProvider =
New
AesCryptoServiceProvider
myAESProvider.Key =
ASCIIEncoding.ASCII.GetBytes(Key)
myAESProvider.IV =
ASCIIEncoding.ASCII.GetBytes(IV)
Dim
myICryptoTransform
As
ICryptoTransform
=
myAESProvider.CreateEncryptor(myAESProvider.Key, myAESProvider.IV)
SourceFileStream =
New
FileStream(SourceFileName, FileMode.Open, FileAccess.Read, FileShare.None)
ResultFileStream =
New
FileStream(ResultFileName, FileMode.Create, FileAccess.Write)
myCryptoStream =
New
CryptoStream(ResultFileStream, myICryptoTransform, CryptoStreamMode.Write)
Dim
InputByteArray(SourceFileStream.Length
-
1
)
As
Byte
SourceFileStream.Read(InputByteArray, 0
, InputByteArray.Length)
myCryptoStream.Write(InputByteArray, 0
, InputByteArray.Length)
Finally
If
myCryptoStream
IsNot
Nothing
Then
myCryptoStream.Close()
If
SourceFileStream
IsNot
Nothing
Then
SourceFileStream.Close()
If
ResultFileStream
IsNot
Nothing
Then
ResultFileStream.Close()
If
myAESProvider
IsNot
Nothing
Then
myAESProvider.Clear()
End
Try
End Sub
Private
Sub
DeCrypt(
ByVal
SourceFileName
As
String
,
ByVal
ResultFileName
As
String
,
ByVal
Key
As
String
,
ByVal
IV
As
String
) ‘对指定文件进行AES解密,并将明文保存到指定路径
Dim
myCryptoStream
As
CryptoStream
=
Nothing
Dim
SourceFileStream
As
FileStream
=
Nothing
Dim
ResultFileStream
As
FileStream
=
Nothing
Dim
myAESProvider
As
AesCryptoServiceProvider
=
Nothing
Try
myAESProvider =
New
AesCryptoServiceProvider
myAESProvider.Key =
ASCIIEncoding.ASCII.GetBytes(Key)
myAESProvider.IV =
ASCIIEncoding.ASCII.GetBytes(IV)
Dim
myICryptoTransform
As
ICryptoTransform
=
myAESProvider.CreateDecryptor(myAESProvider.Key, myAESProvider.IV)
SourceFileStream =
New
FileStream(SourceFileName, FileMode.Open, FileAccess.Read, FileShare.None)
ResultFileStream =
New
FileStream(ResultFileName, FileMode.Create, FileAccess.Write)
myCryptoStream =
New
CryptoStream(ResultFileStream, myICryptoTransform, CryptoStreamMode.Write)
Dim
InputByteArray(SourceFileStream.Length
-
1
)
As
Byte
SourceFileStream.Read(InputByteArray, 0
, InputByteArray.Length)
myCryptoStream.Write(InputByteArray, 0
, InputByteArray.Length)
Finally
If
myCryptoStream
IsNot
Nothing
Then
myCryptoStream.Close()
If
SourceFileStream
IsNot
Nothing
Then
SourceFileStream.Close()
If
ResultFileStream
IsNot
Nothing
Then
ResultFileStream.Close()
If
myAESProvider
IsNot
Nothing
Then
myAESProvider.Clear()
End
Try
End Sub
Public
Sub
AES_EnCrypt(
ByVal
SourceFileName
As
String
,
ByVal
ResultFileName
As
String
,
ByVal
Key
As
String
,
ByVal
IV
As
String
,
ByVal
IsEnCryption
As
Boolean
) ‘主调用过程,用于控制AES加解密过程的调用顺序
If
IsEnCryption
Then
EnCrypt(SourceFileName, ResultFileName, Key, IV)
Else
DeCrypt(SourceFileName, ResultFileName, Key, IV)
End
If
End Sub
Private
Function
GetBitOfByte(
ByVal
b
As
Byte
)
As
Byte
() ’将一字节数分解为八个二进制位
Dim
res(
7
)
As
Byte
For
i
As
Integer
=
0
To
7
res( 7
-
i)
=
(b
>>
i)
And
1
Next
Return
res
End Function
Private
Sub
EnCryptWithBMP(
ByVal
SourceFileName
As
String
,
ByVal
ResultBMPName
As
String
,
Optional
ByVal
IsInputBMPWithEnCrypt
As
Boolean
=
False
,
Optional
ByVal
strKey
As
String
=
""
,
Optional
ByVal
strIV
As
String
=
""
) ‘次调用过程,控制图片水印加密过程的调用顺序
If
IsInputBMPWithEnCrypt
Then
Dim
tempFileName
As
String
=
Path.GetTempFileName
EnCrypt(SourceFileName, tempFileName, strKey, strIV)
InputTextToBMP(tempFileName, ResultBMPName)
File.Delete(tempFileName)
Else
InputTextToBMP(SourceFileName, ResultBMPName)
End
If
End Sub
Private
Sub
DeCryptWithBMP(
ByVal
SourceBMPName
As
String
,
ByVal
ResultFileName
As
String
,
Optional
ByVal
IsInputBMPWithEnCrypt
As
Boolean
=
False
,
Optional
ByVal
strKey
As
String
=
""
,
Optional
ByVal
strIV
As
String
=
""
) ’次调用过程,控制图片水印解密过程的调用顺序
If
IsInputBMPWithEnCrypt
Then
Dim
tempFileName
As
String
=
Path.GetTempFileName
GetTextFromBMP(SourceBMPName, tempFileName)
DeCrypt(tempFileName, ResultFileName, strKey, strIV)
File.Delete(tempFileName)
Else
GetTextFromBMP(SourceBMPName, ResultFileName)
End
If
End Sub
Public
Sub
AESwithBMP_EnCrypt(
ByVal
SourceFileName
As
String
,
ByVal
ResultFileName
As
String
,
ByVal
IsEnCryption
As
Boolean
,
Optional
ByVal
IsInputBMPWithEnCrypt
As
Boolean
=
False
,
Optional
ByVal
Key
As
String
=
""
,
Optional
ByVal
IV
As
String
=
""
) ‘主调用过程,用于控制对图片水印的次调用过程的调用顺序
If
IsEnCryption
Then
If
IsInputBMPWithEnCrypt
Then
EnCryptWithBMP(SourceFileName, ResultFileName, True
, Key, IV)
Else
EnCryptWithBMP(SourceFileName, ResultFileName)
End
If
Else
If
IsInputBMPWithEnCrypt
Then
DeCryptWithBMP(SourceFileName, ResultFileName, True
, Key, IV)
Else
DeCryptWithBMP(SourceFileName, ResultFileName)
End
If
End
If
End Sub
Private
Sub
InputTextToBMP(
ByVal
SourceFileName
As
String
,
ByVal
BMPName
As
String
) ’将指定的文本信息进行二进制编码后,写入指定bmp图片
Dim
fStream
As
FileStream
=
Nothing
Dim
bmpStream
As
FileStream
=
Nothing
Try
fStream =
New
FileStream(SourceFileName, FileMode.Open, FileAccess.Read)
bmpStream =
New
FileStream(BMPName, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
Dim
dm(
1
)
As
Byte
bmpStream.Read(dm, 0
,
2
)
If
Not
(dm(
0
)
=
66
AndAlso
dm(
1
)
=
77
)
Then
’bmp图片的前两字节是文件类型标示位
Throw
New
ApplicationException(
"
目标图片不是合法的BMP位图文件
"
)
End
If
Dim
i
As
Integer
Dim
BMP_Data(
7
)
As
Byte
Dim
Text_Data(
0
)
As
Byte
Dim
Input_Data(
7
)
As
Byte
bmpStream.Seek( 55
, SeekOrigin.Begin) ‘将bmp图片的指针跳到第55字节,因为55字节后才是数据区。
For
i
=
0
To
fStream.Length
-
1
fStream.Read(Text_Data, 0
,
1
)
bmpStream.Read(BMP_Data, 0
,
8
)
Dim
temp_Data()
As
Byte
=
GetBitOfByte(Text_Data(
0
))
For
k
As
Integer
=
0
To
7
If
temp_Data(k)
=
0
Then
Input_Data(k) =
BMP_Data(k)
And
254
Else
Input_Data(k) =
BMP_Data(k)
Or
1
End
If
Next
bmpStream.Seek( -
8
, SeekOrigin.Current)
bmpStream.Write(Input_Data, 0
,
8
)
Next
bmpStream.Read(BMP_Data, 0
,
8
)
For
k
As
Integer
=
0
To
7
Input_Data(k) =
BMP_Data(k)
Or
1
Next
bmpStream.Seek( -
8
, SeekOrigin.Current)
bmpStream.Write(Input_Data, 0
,
8
)
Finally
If
fStream
IsNot
Nothing
Then
fStream.Close()
If
bmpStream
IsNot
Nothing
Then
bmpStream.Close()
End
Try
End Sub
Private
Sub
GetTextFromBMP(
ByVal
SourceBMPName
As
String
,
ByVal
ResultFileName
As
String
) ‘将指定bmp图片中的二进制信息还原为文本
Dim
bmpStream
As
FileStream
=
Nothing
Dim
fStream
As
FileStream
=
Nothing
Try
bmpStream =
New
FileStream(SourceBMPName, FileMode.Open, FileAccess.Read, FileShare.None)
fStream =
New
FileStream(ResultFileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
Dim
dm(
1
)
As
Byte
bmpStream.Read(dm, 0
,
2
)
If
Not
(dm(
0
)
=
66
AndAlso
dm(
1
)
=
77
)
Then
Throw
New
ApplicationException(
"
目标图片不是合法的BMP位图文件
"
)
End
If
bmpStream.Seek( 55
, SeekOrigin.Begin)
Do
Dim
BMP_Data(
7
)
As
Byte
Dim
Text_Data(
0
)
As
Byte
bmpStream.Read(BMP_Data, 0
,
8
)
Dim
temp_Data(
7
)
As
Byte
For
k
As
Integer
=
0
To
7
temp_Data(k) =
BMP_Data(k)
And
1
Next
Dim
resByte
As
Byte
=
GetByteFromBit(temp_Data)
If
resByte
=
255
Then
Exit
Do
End
If
fStream.WriteByte(resByte)
Loop
Finally
If
fStream
IsNot
Nothing
Then
fStream.Close()
If
bmpStream
IsNot
Nothing
Then
bmpStream.Close()
End
Try
End Sub
Private
Function
GetByteFromBit(
ByVal
Bits()
As
Byte
)
As
Byte
’根据八个二进制位算出对应的十进制数
Dim
res
As
Byte
For
i
As
Integer
=
0
To
7
res +=
Bits(
7
-
i)
*
2
^
i
Next
Return
res
End Function
End Class