如何对图象文件进行加密?(100分)

  • 主题发起人 主题发起人 三山
  • 开始时间 开始时间

三山

Unregistered / Unconfirmed
GUEST, unregistred user!
我做了一个图象检索系统,大约有六七万张图象吧,花了大约三年才扫完,在自己单位还没什么,但给别的单位用,能否把图象加密一下,不至于被轻易地拷走呢?我用的是access数据库.把图象放进库里似不行?还有别的办法吗?
 
对图像数据进行加密后保存
Purpose
The purpose of the project is to show how to encrypt/decrypt BMP and JPEG image files.

Background

The image encryption/decryption technique described here will be "good enough" for many business applications, but would not be comparable to techniques used by the U.S. Government's National Security Agency. There are many ways to improve on the encryption technique used here. Here are some other sources for information about encryption:

Cryptography section of efg's Math Reference Library page

Cryptography and Multiple-Precision Arithmetic page of the efg's Delphi Math Functions pages

Book: Cryptography: A Primer

Book: Applied Cryptography

Book: Handbook of Applied Cryptography

There are many ways also to improve on the efficiency of the technique described below. The focus of this page is to explain the technique, not optimize it.

One simple way to encrypt a string of character data or binary data is to form a "random" string of bits as long as the original "message," and "exclusive or" (XOR) this random string of bits with the original message to give an "encrypted message." If the receiver of the message knows how to form the same "random" string of bits, a second "exclusive or" of the random string of bits with the encrypted message will decrypt the message.

Let's review the mechanics of this process. First, recall how the XOR function works with bits (0 or 1 values):

Boolean Exclusive OR (XOR) Function

x y x XOR y
0 0 0
0 1 1
1 0 1
1 1 0

Now let's start with the single-byte message "A" and the random bit string "01111010" (or $7A in hex). The following table shows how the encryption/decryption process works:

Step Boolean Expression Hexadecimal Binary Comments
1. ASCII "A" a $41 0100 0001 Original "message:
2. "Random" Bits b $7A 0111 1010 Pseudo-random value from "random" number generator
3. XOR to encrypt a XOR b $3B 0011 1011 Encrypted "message"
4. "Random" Bits b $7A 0111 1010 Same "Random bits" as above
5. XOR to decrypt (a XOR b) XOR b $41 0100 0001 Decrypted "message" (same as original)

A pseudo-random number generator can be used to compute "random" numbers. If the same "seed" is used with a random number generator for both encryption and decryption processes, the same "random" sequence will be generated. Only the "seed" must be communicated if both sender and receiver are using the same random number generator.

Borland's Online Help Note: " Because the implementation of the Random function may change between compiler versions, we do not recommend using Random for encryption or other purposes that require reproducible sequences of pseudo-random numbers."

A variety of random-number generators exist but Delphi's Random function is used in the programs described below. The RandSeed variable (in the System unit) is the "seed" for the random number generator. (See the Probability section of the efg's Delphi Math Functions for alternative random number generators.) If long-term reproducibility is critical, you should use a random number generator for which you have the source code, as recommended by Borland.

A BMP file can be encrypted and still used as a BMP file if only the scanline pixel data is encrypted and the file header is not changed. A JPEG file once encrypted cannot be used as a JPEG file until it is properly decrypted. Study the BMP encryption/decryption process first, since it is easier to understand. Then study the JPEG encryption/decryption process.


--------------------------------------------------------------------------------

BMP Files


--------------------------------------------------------------------------------

BMP Background

A BMP file (or a TBitmap in memory) consists of a header record of various information and the scanlines with pixel data. If the header record of a BMP is encrypted, the BMP can no longer be treated as a bitmap. If only the scanlines of pixel data are encrypted -- with no change in the header record -- the resulting encrypted BMP can still be displayed as a bitmap.

Only the scanline pixel data are encrypted in this project so the resulting TBitmap/BMP file can still be used as an image. If present, the palette could also be encrypted, but that is not covered here.

See the Scanline Tech Note for information about how to access pixel data within a TBitmap.

Materials and Equipment

Software Requirements
Windows 95/98
Delphi 3/4/5 (to recompile)
CryptBMP.EXE

Hardware Requirements
VGA display with 640-by-480 screen in high/true color display mode

Procedure

Double click on the CryptBMP.EXE icon to start the program.
Press the Load button and select a BMP file (not provided). Press the Open button.
If desired, uncheck the "stretch" button. Normally, the image is stretched to fit the space available. In some cases, such as with small images, this may not be desirable.
Press the Encrypt button to display the encrypted image. This encrypted image can be saved to a BMP file by selecting the Save button.
Experiment with the Encyrpt and Decrypt Seed Numbers. As shown below, the decrypted image will not match the original when these two numbers are not the same.


Note that the Decrypt button is more of a label than a button that does anything. The decryption process is automatically called after any encryption, or when the decrypt seed number is changed.

Discussion
Consult the complete source code for all the details, but the main Encryption/Decryption routines are shown here.

The EncryptImage method looks at each scanline, regardless of PixelFormat, and XORs a random bit string with the original pixel data. The resulting encrypted image is displayed in the ImageEncrypted TImage.

For palletized images, the original palette is copied to the encrypted image.

Encrypt BMP File
// Don't bother trying to understand structure of pixels within scanline.
// Just find length of scanline in bytes and process all bytes.
PROCEDURE TFormCrypt.EncryptImage;
VAR
i : INTEGER;
j : INTEGER;
RandomValue : BYTE;
rowIn : pByteArray;
rowOut : pByteArray;
ScanlineByteCount: INTEGER;
BEGIN
IF Assigned(BitmapEncrypted)
THEN BitmapEncrypted.Free;
BitmapEncrypted := TBitmap.Create;
BitmapEncrypted.Width := BitmapOriginal.Width;
BitmapEncrypted.Height := BitmapOriginal.Height;
BitmapEncrypted.PixelFormat := BitmapOriginal.PixelFormat;
// Copy palette if palletized image
IF BitmapOriginal.PixelFormat IN [pf1bit, pf4bit, pf8bit]
THEN BitmapEncrypted.Palette := CopyPalette(BitmapOriginal.Palette);
// This finds the number of bytes per scanline regardless of PixelFormat
ScanlineByteCount := ABS(Integer(BitmapOriginal.Scanline[1]) -
Integer(BitmapOriginal.Scanline[0]));
TRY
RandSeed := StrToInt(EditSeedEncrypt.Text)
EXCEPT
RandSeed := 79997 // use this prime number if entry is invalid
END;
FOR j := 0 TO BitmapOriginal.Height-1 DO
BEGIN
RowIn := BitmapOriginal.Scanline[j];
RowOut := BitmapEncrypted.Scanline[j];
FOR i := 0 TO ScanlineByteCount-1 DO
BEGIN
RandomValue := Random(256); // 0..255 value
RowOut := RowIn XOR RandomValue
END
END;
ImageEncrypted.Picture.Graphic := BitmapEncrypted;
DecryptImage;
ButtonDecrypt.Enabled := TRUE;
ButtonSave.Enabled := TRUE
END {EncryptImage};


See Andreas Filsinger's original summary of this encryption method and an updated version for Delphi 6.01.

The DecryptImage method works much like the EncryptImage routine.

Decrypt BMP File

PROCEDURE TFormCrypt.DecryptImage;
VAR
BitmapDecrypted : TBitmap;
i : INTEGER;
j : INTEGER;
RandomValue : BYTE;
rowIn : pByteArray;
rowOut : pByteArray;
ScanlineByteCount: INTEGER;
BEGIN
BitmapDecrypted := TBitmap.Create;
BitmapDecrypted.Width := BitmapEncrypted.Width;
BitmapDecrypted.Height := BitmapEncrypted.Height;
BitmapDecrypted.PixelFormat := BitmapEncrypted.PixelFormat;
// Copy palette if palletized image
IF BitmapEncrypted.PixelFormat IN [pf1bit, pf4bit, pf8bit]
THEN BitmapDecrypted.Palette := CopyPalette(BitmapEncrypted.Palette);
// This finds the number of bytes per scanline regardless of PixelFormat
ScanlineByteCount := ABS(Integer(BitmapEncrypted.Scanline[1]) -
Integer(BitmapEncrypted.Scanline[0]));
TRY
RandSeed := StrToInt(EditSeedDecrypt.Text)
EXCEPT
RandSeed := 79997 // use this prime number if entry is invalid
END;
FOR j := 0 TO BitmapEncrypted.Height-1 DO
BEGIN
RowIn := BitmapEncrypted.Scanline[j];
RowOut := BitmapDecrypted.Scanline[j];
FOR i := 0 TO ScanlineByteCount-1 DO
BEGIN
RandomValue := Random(256); // 0..255 value
RowOut := RowIn XOR RandomValue
END
END;
ImageDecrypted.Picture.Graphic := BitmapDecrypted;
EditSeedDecrypt.Enabled := TRUE;
END {DecryptImage};


In the pf24bit example shown at the top of this page, the encrypted image gives no hint as to what colors might be present in the original image.

For palletized images (i.e., pf1bit, pf4bit, pf8bit), the palette is copied from the original image to the encrypted image and only the scanlines are encrypted. Because some information about the image is contained in the palette, encrypting the palette may also be a good idea, but that was not done in this project.

For example, only the colors of the pf1bit "Smiley" (from the Single-Bit Bitmaps Lab Report) are present in the encrypted form (see below). Hiding these colors by encrypting the palette entries may be desirable.

"Smiley" Encrypted Smiley


Using a different "Encrypt Seed Number" for each and every encrypted image is quite important. If the same Encrypt Seed Number is used for two images, some information about both images can be learned without the Encrypt Seed Number. For example, assume you have two images, A and B:

Original Images
A B


If you encrypt both of these images with the Encrypt Seed Number 19937, the results seem to hide the images:

Images Encrypted Using Seed 19937
A19937 B19937


But now if you take both of these images, and without any knowledge of the original encryption key, perform certain operations with the images, some information about the original images can be seen. For example, if you use XOR with corresponding color components for each pixel, (R,G,B) = (R1 XOR R2, G1 XOR G2, B1 XOR B2), some information about the original can be extracted. In particular, many traces of image B can be seen:

A19937 XOR B19937


This image is the equivalent to XORing the original images A and B:

A XOR B


If we assume R is the random sequence of bits, this is the math that explains why the randomness does not hide the composite image, A XOR B:

(A XOR R) XOR (B XOR R) = A XOR B

The solution is to use unique random sequences with A and B:

(A XOR R1) XOR (B XOR R2) <> A XOR B

This emphasizes why a unique key should be used with each image. If Image A had been encrypted with the key, 66547, the image A66547 looks much like A19937:

A66547 A19937


With the unique key, the A XOR B operations are quite different:

A66547 XOR B19937 A19937 XOR B19937


(Thanks to Christian Berger for stressing this limitation in a posting to the Borland Community site.)


--------------------------------------------------------------------------------

JPEG Files


--------------------------------------------------------------------------------

JPG Background

Unlike the BMP file, manipulating the "pixel" scanlines separately from any JPG "header" information is not possible (at least without modifying the existing Delphi TJPEGImage definition). So a different approach must be used. With JPGs, the whole file is encrypted. But this means that the resulting file cannot be treated as a JPG image.

Materials and Equipment

Software Requirements
Windows 95/98
Delphi 3/4/5 (to recompile)
CryptJPEG.EXE

Hardware Requirements
VGA display with 640-by-480 screen in high/true color display mode

Procedure

Double click on the CryptJPEG.EXE icon to start the program.
Press the Load button and select a JPG file (Flower.JPG is provided). Press the Open button.
If desired, uncheck the "stretch" button. Normally, the image is stretched to fit the space available. In some cases, such as with small images, this may not be desirable.
Press the Encrypt button to display the encrypted image (see discussion below). This encrypted image can be saved to a .Binary file by selecting the Save BIN button.
Experiment with the Encyrpt and Decrypt Seed Numbers. When the number match, the encrypted image will be correctly decrypted.
Load a previously saved .Binary file by pressing the Load BIN button. If the Decrypt Seed is correct, this encrypted .Binary file can be decrypted and displayed.


Discussion
Consult the complete source code for all the details, but the main Encryption/Decryption routines are shown here.

The technique shown here for a JPEG file could be used with any graphics file, such as GIFs, or any other type of file. Unlike the BMP encryption/decryption process described above, once a JPEG file is encrypted, it cannot be displayed as an image file. The encrypted JPEG file is a file of binary data that must be decrypted before it can be used in any way.

The processing of loading a BMP file wasn't explained above since it was so straightforward. However, in addition to the "normal" process of loading and displaying a TJPEGImage in a TImage, the JPEG file is loaded in to a JPEGOriginalBinary TMemoryStream for later processing by the encryption routine.

Load JPEG Image

procedure TFormCrypt.ButtonLoadJPGClick(Sender: TObject);
VAR
JPEGOriginal: TJPEGImage;
begin
IF OpenPictureDialog.Execute
THEN BEGIN
// Load JPEG Image for Display
JPEGOriginal := TJPEGImage.Create;
TRY
JPEGOriginal.LoadFromFile(OpenPictureDialog.Filename);
ImageOriginal.Picture.Graphic := JPEGOriginal
FINALLY
JPEGOriginal.Free
END;
// Load JPEG Image as Binary Stream
IF Assigned(JPEGOriginalBinary)
THEN JPEGOriginalBinary.Free;
JPEGOriginalBinary := TMemoryStream.Create;
JPEGOriginalBinary.LoadFromFile(OpenPictureDialog.Filename);
ButtonEncrypt.Enabled := TRUE;
EditSeedEncrypt.Enabled := TRUE;
END
end;


With the BMP file, the EncryptImage method looked at each scanline. Here with a JPEG, the whole file is treated as a binary stream of data and encrypted byte-by-byte to form a new binary stream, JPEGEncryptedBinary (a TMemoryStream).

The binary data in a TMemoryStream is processed by obtaining a pointer to the beginning of the stream, such as in

pIn := JPEGOriginalBinary.Memory;

The pointer is incremented, INC(pIn), as each byte is processed by XORing it with a byte from a sequence of "random" bytes.

Encrypt JPEG File

// Encrypt bytes in JPEGOriginalBinary TMemoryStream to give JPEGEncryptedBinary
PROCEDURE TFormCrypt.EncryptImage;
VAR
BitmapDisplay: TBitmap;
i : INTEGER;
pIn : pByte;
pOut : pByte;
RandomValue : BYTE;
BEGIN
TRY
RandSeed := StrToInt(EditSeedEncrypt.Text)
EXCEPT
RandSeed := 67547 // use this prime number if entry is invalid
END;
IF Assigned(JPEGEncryptedBinary)
THEN JPEGEncryptedBinary.Free;
JPEGEncryptedBinary := TMemoryStream.Create;
// Encrypted version same size as the original version
JPEGEncryptedBinary.Size := JPEGOriginalBinary.Size;
pIn := JPEGOriginalBinary.Memory;
pOut := JPEGEncryptedBinary.Memory;
FOR i := 1 TO JPEGOriginalBinary.Size DO
BEGIN
RandomValue := Random(256); // 0..255
pOut^ := pIn^ XOR RandomValue;
INC(pIn);
INC(pOut)
END;
// JPEGEncryptedBinary cannot be displayed as an image. So, let's just
// create a "noise" bitmap to display instead. The "seed" for this noise
// image will be the RandSeed left over from the JPEG encryption, so the
// same noise image will be created for a given JPEG.
BitmapDisplay := CreateNoiseImage(ImageEncrypted.Width,
ImageEncrypted.Height);
TRY
ImageEncrypted.Picture.Graphic := BitmapDisplay;
FINALLY
BitmapDisplay.Free
END;
DecryptImage;
ButtonDecrypt.Enabled := TRUE;
ButtonSaveBIN.Enabled := TRUE
END {EncryptImage};


Unlike the encrypted BMP file, the encrypted JPEG binary stream cannot be displayed as an image. To display something for this encrypted file, a "noise" image was displayed instead, which was created as shown next:

Create Noise Image

// Create pf24bit "noise" image using random numbers
FUNCTION CreateNoiseImage(CONST Width, Height: INTEGER): TBitmap;
VAR
i : INTEGER;
j : INTEGER;
row: pByteArray;
BEGIN
RESULT := TBitmap.Create;
RESULT.Width := Width;
RESULT.Height := Height;
RESULT.PixelFormat := pf24bit;
FOR j := 0 TO Height-1 DO
BEGIN
row := RESULT.Scanline[j];
FOR i := 0 TO 3*Width-1 DO // 3 bytes per pixel
BEGIN
row := Random(256)
END
END
END {CreateNoiseImage};


The DecryptImage method works much like the EncryptImage routine. After the decrypted binary stream is formed, JPEGDecryptedBinary, this stream is loaded into a new TJPEGImage. If there is any exception in this process (e.g., the decrypted file never was a JPEG file, or the decryption did no result in a JPEG file), then a "noise" image is displayed.

Decrypt JPEG File

PROCEDURE TFormCrypt.DecryptImage;
VAR
BitmapDisplay : TBitmap;
i : INTEGER;
JPEGDecrypted : TJPEGImage;
JPEGDecryptedBinary: TMemoryStream;
pIn : pByte;
pOut : pByte;
RandomValue : BYTE;
BEGIN
TRY
RandSeed := StrToInt(EditSeedDecrypt.Text)
EXCEPT
RandSeed := 67547 // use this prime number if entry is invalid
END;
JPEGDecryptedBinary := TMemoryStream.Create;
TRY
// Decrypted version same size as the encrypted version
JPEGDecryptedBinary.Size := JPEGEncryptedBinary.Size;
pIn := JPEGEncryptedBinary.Memory;
pOut := JPEGDecryptedBinary.Memory;
FOR i := 1 TO JPEGEncryptedBinary.Size DO
BEGIN
RandomValue := Random(256); // 0..255
pOut^ := pIn^ XOR RandomValue;
INC(pIn);
INC(pOut)
END;
// At this point the decrypted JPEG binary stream is in the
// JPEGDecryptedBinary stream. Load this memory stream into the
// JPEGDecrypted TJPEGImage.
JPEGDecrypted := TJPEGImage.Create;
TRY
TRY
JPEGDecrypted.LoadFromStream(JPEGDecryptedBinary);
ImageDecrypted.Picture.Graphic := JPEGDecrypted;
EXCEPT
// If any error occurs in the conversion, just show a noise image
BitmapDisplay := CreateNoiseImage(ImageEncrypted.Width,
ImageEncrypted.Height);
TRY
ImageDecrypted.Picture.Graphic := BitmapDisplay;
FINALLY
BitmapDisplay.Free
END;
END
FINALLY
JPEGDecrypted.Free
END
FINALLY
JPEGDecryptedBinary.Free
END;
EditSeedDecrypt.Enabled := TRUE;
END {DecryptImage};



--------------------------------------------------------------------------------

Conclusions
Encryption using a sequence of random bytes and XOR is a fairly simple process. A similar technique can be applied to other types of files.

An encrypted BMP file can still be displayed as a BMP file. An encrypted JPEG file is a binary stream that cannot be treated as a JPEG image until (and unless) it is correctly decrypted.


--------------------------------------------------------------------------------

Thanks to Morten Jacobsen of Norway for asking how an image could be encrypted.


--------------------------------------------------------------------------------

Keywords
XOR, Random, RandSeed, BMP, JPEG, TBitmap, TJEGImage, Scanline, PixelFormat, Palette, CopyPalette, TMemoryStream, Noise Image, pByte, pByteArray, TMemoryStream.Memory, LoadFromFile, LoadFromStream, ShellExecute, EditNumericKeyPress


 
對文件加個文件頭,使用的時候再去掉文件頭..
 
这和图像加密关系不大,想办法隐藏或加密图片路径就行了
 
谢谢各位大侠的回答
To yanghai0437,
你的方法看来只有私下努力看懂再与你讨论了.
To kouchun,
具体如何做呢?

 
加密图像

procedure EncryptBMP(const BMP: TBitmap; Key: Integer);
var
BytesPorScan: Integer;
w, h: integer;
p: pByteArray;
begin
try
BytesPorScan := Abs(Integer(BMP.ScanLine[1]) -
Integer(BMP.ScanLine[0]));
except
raise Exception.Create('Error');
end;
RandSeed := Key;
for h := 0 to BMP.Height - 1 do
begin
P := BMP.ScanLine[h];
for w := 0 to BytesPorScan - 1 do
P^[w] := P^[w] xor Random(256);
end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
EncryptBMP(Image1.Picture.Bitmap, 623);
Image1.Refresh;
end;
{再次调用这个函数进行解密}
 
找一个硬件加密设备例如EKEY什么的,对所有存入的文件做一个加密后再存储,而显示出来的则做解密后再取出显示,系统是你自己做的,在界面操作上不提供复制的手段,不也能达到目的吗?
 
To xuhao1,
我都是JPG文件,也能这么做吗?
 
后退
顶部