轉自http://www.cppblog.com/ArthasLee/archive/2010/12/01/135186.htmlphp
最近,基於某些緣由和須要,筆者須要去了解一下Crypto++庫,而後對一些數據進行一些加密解密的操做。html
筆者以前沒接觸過任何加密解密方面的知識(固然,把每一個字符的ASCII值加1之流對明文進行加密的「趣事」仍是幹過的,當時還很樂在其中。),甚至一開始連Crypto++的名字都沒有聽過,被BS了以後,就開始了Crypto++的入門探索過程。ios
最初,大概知道了要了解兩大類算法中的幾個算法——對稱加密算法:DES、AES(後來由於人品好的緣故也瞭解了下非對稱加密算法RSA,後文會詳述何謂「人品好」);散列算法(須要經過Hash運算):SHA-256。c++
起初,筆者覺得這樣的知名算法在網上應該有不少現成的例子。筆者比較懶,對於本身不熟悉的東西,總但願找捷徑,直接找別人現(在已經寫)成可(編譯運)行的代碼而後施展ctrl + C,ctrl + V算法(咳,什麼算法,是大法!!!)。算法
However,發覺網上的例子不是稀缺,就是隻有代碼沒有解釋。筆者以爲很難忍受這樣的「莫名其妙」(奇怪的是筆者容忍了windows了,儘管它不開源),遂決定從零開始……windows
……寫在代碼前……
安全
若是以前像筆者同樣沒相關經驗——徹底沒接觸過加密解密——,請務必閱讀下文。
dom
一些前期工做——編譯cryptlib並使其可用:函數
本文不涉及這部份內容,由於已經有相對完善的資料:
http://www.cnblogs.com/cxun/archive/2008/07/30/743541.html工具
總結了一點預備知識:
關於幾個算法的介紹,網上各大百科都有,筆者再也不詳細Ctrl+C/V了。不過在寫代碼以前,即便複製修改人家代碼以前,也有必要了解一下幾個算法(除了名稱以外)的使用流程(不是算法具體的實現,汗!)。
對稱加密:(AES、DES)
相對於與非對稱加密而言,加密、解密用的密匙相同。就像平常生活中的鑰匙,開門和鎖門都是同一把。
詳見:http://baike.baidu.com/view/119320.htm
非對稱加密:(RSA)
相對於上述的對稱加密而言,加密、解密用的密匙不一樣,有公匙和私匙之分。
詳見:http://baike.baidu.com/view/554866.htm
散列算法:(SHA系列,咱們熟悉的MD5等)
用途:驗證信息有沒有被修改。
原理:對長度大的信息進行提煉(經過一個Hash函數),提煉事後的信息長度小不少,獲得的是一個固定長度的值(Hash值)。對於兩個信息量很大的文件經過比較這兩個值,就知道這兩個文件是否徹底一致(另一個文件有沒有被修改)。從而避免了把兩個文件中的信息進行逐字逐句的比對,減小時間開銷。
形象地描述:鬼泣3裏面維吉爾跟但丁除了髮型以外都很像。怎麼區分兩個雙生子?比較他們的DNA就比如是比較信息量很大的文件,然而直接看髮型就比如是看Hash值。一眼就看出來了。
注:以上是筆者對幾個概念的,很是不嚴格的,很是主觀的,歸納的描述,想要詳細瞭解,能夠:
http://wenku.baidu.com/view/4fb8e0791711cc7931b716aa.html
幾個算法的介紹,選擇,比較。
……Code speaking……
平臺:WindowsXP
IDE以及工具:Visual Studio 2008 + Visual Assist
庫版本:Crypto++ 5.6.0
庫的文檔(包括類和函數的接口列表):
http://www.cryptopp.com/docs/ref/index.html
對稱加密算法:
DES:
一開始筆者並無找到關於DES運用的很好的例程,或者說,筆者的搜索功力薄弱,未能找到很是完整的例程吧。
http://bbs.pediy.com/showthread.php?p=745389
筆者如下的代碼主要是參考上面URL的論壇回帖,可是做了些修改:
1
#include
<
iostream
>
2
#include
<
des.h
>
3
4
#pragma comment( lib,
"
cryptlib.lib
"
)
5
6
using
namespace
std;
7
using
namespace
CryptoPP;
8
9
int
main(
void
)
10
{
11 //主要是打印一些基本信息,方便調試:
12 cout << "DES Parameters: " << endl;
13 cout << "Algorithm name : " << DES::StaticAlgorithmName() << endl;
14
15 unsigned char key[ DES::DEFAULT_KEYLENGTH ];
16 unsigned char input[ DES::BLOCKSIZE ] = "12345";
17 unsigned char output[ DES::BLOCKSIZE ];
18 unsigned char txt[ DES::BLOCKSIZE ];
19
20 cout << "input is: " << input << endl;
21
22 //能夠理解成首先構造一個加密器
23 DESEncryption encryption_DES;
24
25 //回憶一下以前的背景,對稱加密算法須要一個密匙。加密和解密都會用到。
26 //所以,設置密匙。
27 encryption_DES.SetKey( key, DES::KEYLENGTH );
28 //進行加密
29 encryption_DES.ProcessBlock( input, output );
30
31 //顯示結果
32 //for和for以後的cout無關緊要,主要爲了運行的時候看加密結果
33 //把字符串的長度寫成一個常量其實並不被推薦。
34 //不過筆者事先知道字符串長,爲了方便調試,就直接寫下。
35 //這裏主要是把output也就是加密後的內容,以十六進制的整數形式輸出。
36 for( int i = 0; i < 5; i++ )
37 {
38 cout << hex << (int)output[ i ] << ends;
39 }
40 cout << endl;
41
42 //構造一個加密器
43 DESDecryption decryption_DES;
44
45 //因爲對稱加密算法的加密和解密都是同一個密匙,
46 //所以解密的時候設置的密匙也是剛纔在加密時設置好的key
47 decryption_DES.SetKey( key, DES::KEYLENGTH );
48 //進行解密,把結果寫到txt中
49 //decryption_DES.ProcessAndXorBlock( output, xorBlock, txt );
50 decryption_DES.ProcessBlock( output, txt );
51
52 //以上,加密,解密還原過程已經結束了。如下是爲了驗證:
53 //加密前的明文和解密後的譯文是否相等。
54 if ( memcmp( input, txt, 5 ) != 0 )
55 {
56 cerr << "DES Encryption/decryption failed.\n";
57 abort();
58 }
59 cout << "DES Encryption/decryption succeeded.\n";
60
61 return 0;
62}
63
回想一下以上代碼的編寫過程,就能夠發現,進行DES加密,流程大概是:
數據準備;
構造加密器;
設置加密密匙;
加密數據;
顯示(非必要);
設置解密密匙(跟加密密匙是同一個key);
解密數據;
驗證與顯示(非必要);
因而可知,主要函數的調用流程就是這樣。可是文檔沒有詳細講,筆者當時打開下載回來的源文件時,就傻了眼。
猜測:
AES和之後的算法,是否是都是按照這些基本的套路呢?
AES:
在實際運用的時候,從代碼上看,AES跟DES很是相像。可是值得注意一點的是,AES取代了DES成爲21世紀的加密標準。是由於以其密匙長度和高安全性得到了先天優點。雖然界面上看上去沒多大區別,可是破解難度遠遠大於DES。詳細狀況,在以前的URL有說起過。
很幸運,筆者很快就找到了AES的使用例程,並且很詳細:
http://dev.firnow.com/course/3_program/c++/cppsl/2008827/138033.html
1
#include
<
iostream
>
2
#include
<
aes.h
>
3
4
#pragma comment( lib,
"
cryptlib.lib
"
)
5
6
using
namespace
std;
7
using
namespace
CryptoPP;
8
9
int
main()
10
{
11
12 //AES中使用的固定參數是以類AES中定義的enum數據類型出現的,而不是成員函數或變量
13 //所以須要用::符號來索引
14 cout << "AES Parameters: " << endl;
15 cout << "Algorithm name : " << AES::StaticAlgorithmName() << endl;
16
17 //Crypto++庫中通常用字節數來表示長度,而不是經常使用的字節數
18 cout << "Block size : " << AES::BLOCKSIZE * 8 << endl;
19 cout << "Min key length : " << AES::MIN_KEYLENGTH * 8 << endl;
20 cout << "Max key length : " << AES::MAX_KEYLENGTH * 8 << endl;
21
22 //AES中只包含一些固定的數據,而加密解密的功能由AESEncryption和AESDecryption來完成
23 //加密過程
24 AESEncryption aesEncryptor; //加密器
25
26 unsigned char aesKey[AES::DEFAULT_KEYLENGTH]; //密鑰
27 unsigned char inBlock[AES::BLOCKSIZE] = "123456789"; //要加密的數據塊
28 unsigned char outBlock[AES::BLOCKSIZE]; //加密後的密文塊
29 unsigned char xorBlock[AES::BLOCKSIZE]; //必須設定爲全零
30
31 memset( xorBlock, 0, AES::BLOCKSIZE ); //置零
32
33 aesEncryptor.SetKey( aesKey, AES::DEFAULT_KEYLENGTH ); //設定加密密鑰
34 aesEncryptor.ProcessAndXorBlock( inBlock, xorBlock, outBlock ); //加密
35
36 //以16進制顯示加密後的數據
37 for( int i=0; i<16; i++ ) {
38 cout << hex << (int)outBlock[i] << " ";
39 }
40 cout << endl;
41
42 //解密
43 AESDecryption aesDecryptor;
44 unsigned char plainText[AES::BLOCKSIZE];
45
46 aesDecryptor.SetKey( aesKey, AES::DEFAULT_KEYLENGTH );
47 //細心的朋友注意到這裏的函數不是以前在DES中出現過的:ProcessBlock,
48 //而是多了一個Xor。其實,ProcessAndXorBlock也有DES版本。用法跟AES版本差很少。
49 //筆者分別在兩份代碼中列出這兩個函數,有興趣的朋友能夠本身研究一下有何差別。
50 aesDecryptor.ProcessAndXorBlock( outBlock, xorBlock, plainText );
51
52
53 for( int i=0; i<16; i++ )
54 {
55 cout << plainText[i];
56 }
57 cout << endl;
58
59 return 0;
60}
61
62
其實來到這裏,均可以發現,加密解密的套路也差很少,至於以後筆者在誤打誤撞中找到的RSA,也只不過是在設置密匙的時候多了私匙和公匙的區別而已。筆者總以爲,有完整的例程對照學習,是一件很幸福的事情。
非對稱加密算法:
RSA:
小背景:
其實,筆者在一開始並無接到「瞭解RSA」的要求。不過因爲筆者很粗心,在看AES的時候只記得A和S兩個字母,Google的時候就誤打誤撞Google了一個RSA。其實RSA方面的資料仍是挺多的,所以它事實上是筆者第一個編譯運行成功的Crypto++庫中算法的應用實例。
http://www.cnblogs.com/cxun/archive/2008/07/30/743541.html
如下代碼主要是按照上述URL中提供的代碼寫成的,做爲筆者的第一份有效學習資料,筆者認爲做爲調用者的咱們,不用清楚算法實現的細節。只須要明白幾個主要函數的功用和調用的次序便可。
由如下代碼能夠看出,其實RSA也離不開:數據準備、設置密匙(注意,有公匙和私匙)、加密解密這樣的套路。至於如何產生密匙,有興趣的朋友能夠到Crypto++的主頁上下載源文件研究。做爲入門和了解階段,筆者以爲:只須要用起來便可。
1
//
version at Crypto++ 5.60
2
#include
"
randpool.h
"
3
#include
"
rsa.h
"
4
#include
"
hex.h
"
5
#include
"
files.h
"
6
#include
<
iostream
>
7
8
using
namespace
std;
9
using
namespace
CryptoPP;
10
11
#pragma comment(lib,
"
cryptlib.lib
"
)
12
13
14
//
------------------------
15
16
//
函數聲明
17
18
//
------------------------
19
20
void
GenerateRSAKey( unsigned
int
keyLength,
const
char
*
privFilename,
const
char
*
pubFilename,
const
char
*
seed );
21
string
RSAEncryptString(
const
char
*
pubFilename,
const
char
*
seed,
const
char
*
message );
22
string
RSADecryptString(
const
char
*
privFilename,
const
char
*
ciphertext );
23
RandomPool
&
GlobalRNG();
24
25
//
------------------------
26
//
主程序
27
//
------------------------
28
29
void
main(
void
)
30
31
{
32 char priKey[ 128 ] = { 0 };
33 char pubKey[ 128 ] = { 0 };
34 char seed[ 1024 ] = { 0 };
35
36 // 生成 RSA 密鑰對
37 strcpy( priKey, "pri" ); // 生成的私鑰文件
38 strcpy( pubKey, "pub" ); // 生成的公鑰文件
39 strcpy( seed, "seed" );
40 GenerateRSAKey( 1024, priKey, pubKey, seed );
41
42 // RSA 加解密
43 char message[ 1024 ] = { 0 };
44 cout<< "Origin Text:\t" << "Hello World!" << endl << endl;
45 strcpy( message, "Hello World!" );
46 string encryptedText = RSAEncryptString( pubKey, seed, message ); // RSA 公匙加密
47 cout<<"Encrypted Text:\t"<< encryptedText << endl << endl;
48 string decryptedText = RSADecryptString( priKey, encryptedText.c_str() ); // RSA 私匙解密
49}
50
51
52
53
//
------------------------
54
55
//
生成RSA密鑰對
56
57
//
------------------------
58
59
void
GenerateRSAKey(unsigned
int
keyLength,
const
char
*
privFilename,
const
char
*
pubFilename,
const
char
*
seed)
60
{
61 RandomPool randPool;
62 randPool.Put((byte *)seed, strlen(seed));
63
64 RSAES_OAEP_SHA_Decryptor priv(randPool, keyLength);
65 HexEncoder privFile(new FileSink(privFilename));
66
67 priv.DEREncode(privFile);
68 privFile.MessageEnd();
69
70 RSAES_OAEP_SHA_Encryptor pub(priv);
71 HexEncoder pubFile(new FileSink(pubFilename));
72 pub.DEREncode(pubFile);
73
74 pubFile.MessageEnd();
75
76 return ;
77}
78
79
80
81
//
------------------------
82
83
//
RSA加密
84
85
//
------------------------
86
87
string
RSAEncryptString(
const
char
*
pubFilename,
const
char
*
seed,
const
char
*
message )
88
{
89 FileSource pubFile( pubFilename, true, new HexDecoder );
90 RSAES_OAEP_SHA_Encryptor pub( pubFile );
91
92 RandomPool randPool;
93 randPool.Put( (byte *)seed, strlen(seed) );
94
95 string result;
96 StringSource( message, true, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(result))) );
97
98 return result;
99}
100
101
102
103
//
------------------------
104
//
RSA解密
105
//
------------------------
106
107
string
RSADecryptString(
const
char
*
privFilename,
const
char
*
ciphertext )
108
{
109 FileSource privFile( privFilename, true, new HexDecoder );
110 RSAES_OAEP_SHA_Decryptor priv(privFile);
111
112 string result;
113 StringSource( ciphertext, true, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(result))) );
114
115 return result;
116}
117
118
119
120
//
------------------------
121
122
//
定義全局的隨機數池
123
124
//
------------------------
125
126
RandomPool
&
GlobalRNG()
127
{
128 static RandomPool randomPool;
129 return randomPool;
130}
131
132
散列算法:
SHA-256
SHA-256主要是用來求一大段信息的Hash值,跟以前三個用於加密、解密的算法有所不一樣。用到SHA的場合,多半是爲了校驗文件。
筆者的參考資料:http://hi.baidu.com/magic475/blog/item/19b37a8c1fa15a14b21bbaeb.html
請注意,筆者在實現的時候,稍微修改了一下兩個子函數的實現,以知足筆者的需求。所以會與上述URL中的代碼有差別。
1
//
http://hi.baidu.com/magic475/blog/item/19b37a8c1fa15a14b21bbaeb.html
2
#include
<
iostream
>
3
#include
<
string
.h
>
4
5
#include
"
sha.h
"
6
#include
"
secblock.h
"
7
#include
"
modes.h
"
8
#include
"
hex.h
"
9
10
#pragma comment( lib,
"
cryptlib.lib
"
)
11
12
using
namespace
std;
13
using
namespace
CryptoPP;
14
15
void
CalculateDigest(
string
&
Digest,
const
string
&
Message);
16
bool
VerifyDigest(
const
string
&
Digest,
const
string
&
Message);
17
18
int
main(
void
)
19
{
20 //main函數中註釋掉的,關於strMessage2的代碼,實際上是筆者模擬了一下
21 //經過求Hash值來對「大」量數據進行校驗的這個功能的運用。
22 //註釋以後並不影響這段代碼表達的思想和流程。
23 string strMessage( "Hello world" );
24 string strDigest;
25 //string strMessage2( "hello world" ); //只是第一個字母不一樣
26 //string strDigest2;
27
28 CalculateDigest( strDigest, strMessage ); //計算Hash值並打印一些debug信息
29 cout << "the size of Digest is: " << strDigest.size() << endl;
30 cout << "Digest is: " << strDigest << endl;
31
32 //CalculateDigest( strDigest2, strMessage2 );
33 //why put this function here will affect the Verify function?
34 //做者在寫代碼的過程當中遇到的上述問題。
35 //若是把這行代碼的註釋取消,那麼以後的運行結果就不是預料中的同樣:
36 //即便strDigest也沒法對應strMessage,筆者不知道爲何,但願高手指出,謝謝!
37
38 bool bIsSuccess = false;
39 bIsSuccess = VerifyDigest( strDigest, strMessage );
40 //經過校驗,看看strDigest是否對應原來的message
41 if( bIsSuccess )
42 {
43 cout << "sussessive verify" << endl;
44 cout << "origin string is: " << strMessage << endl << endl;
45 }
46 else
47 {
48 cout << "fail!" << endl;
49 }
50
51 //經過strDigest2與strMessage進行校驗,要是相等,
52 //就證實strDigest2是對應的strMessage2跟strMessage1相等。
53 //不然,像這個程序中的例子同樣,兩個message是不相等的
54 /*CalculateDigest( strDigest2, strMessage2 );
55 bIsSuccess = VerifyDigest( strDigest2, strMessage );
56 if( !bIsSuccess )
57 {
58 cout << "success! the tiny modification is discovered~" << endl;
59 cout << "the origin message is: \n" << strMessage << endl;
60 cout << "after modify is: \n" << strMessage2 << endl;
61 }*/
62 return 0;
63}
64
65
66
//
基於某些緣由,如下兩個子函數的實現跟原來參考代碼中的實現有所區別,
67
//
詳細緣由,筆者在CalculateDigest函數的註釋中寫明
68
void
CalculateDigest(
string
&
Digest,
const
string
&
Message)
69
{
70 SHA256 sha256;
71 int DigestSize = sha256.DigestSize();
72 char* byDigest;
73 char* strDigest;
74
75 byDigest = new char[ DigestSize ];
76 strDigest = new char[ DigestSize * 2 + 1 ];
77
78 sha256.CalculateDigest((byte*)byDigest, (const byte *)Message.c_str(), Message.size());
79 memset(strDigest, 0, sizeof(strDigest));
80 //uCharToHex(strDigest, byDigest, DigestSize);
81 //參考的代碼中有以上這麼一行,可是貌似不是什麼庫函數。
82 //原做者大概是想把Hash值轉換成16進制數保存到一個string buffer中,
83 //而後在主程序中輸出,方便debug的時候對照查看。
84 //可是這並不影響計算Hash值的行爲。
85 //所以筆者註釋掉了這行代碼,而且修改了一下這個函數和後面的VerifyDigest函數,
86 //略去原做者這部分的意圖,繼續咱們的程序執行。
87
88 Digest = byDigest;
89
90 delete []byDigest;
91 byDigest = NULL;
92 delete []strDigest;
93 strDigest = NULL;
94
95 return;
96}
97
98
bool
VerifyDigest(
const
string
&
Digest,
const
string
&
Message)
99
{
100 bool Result;
101 SHA256 sha256;
102 char* byDigest;
103
104 byDigest = new char[ sha256.DigestSize() ];
105 strcpy( byDigest, Digest.c_str() );
106
107 //HexTouChar(byDigest, Digest.c_str(), Digest.size());
108 //爲什麼註釋掉,請參看CalculateDigest函數的註釋
109 Result = sha256.VerifyDigest( (byte*)byDigest, (const byte *)Message.c_str(), Message.size() );
110
111 delete []byDigest;
112 byDigest = NULL;
113 return Result;
114}
115
116
後記:
爲何寫這篇文章呢?由於筆者在搜索過程當中以爲這方面的資料有
點分散,所以想把它們集中起來,方便剛剛入門的朋友。 同時,也算是爲本身留點學習資料吧。 鳴謝: jingzhongrong vczh 沒了這兩位,在這個宇宙、這個時間、這個維度確定不會有這篇文章,哈哈!