Crypto++入门学习笔记(DES、AES、RSA、SHA-256)(加解密)
转自http://www.cppblog.com/ArthasLee/archive/2010/12/01/135186.html
最近,基于某些原因和需要,笔者需要去了解一下Crypto++库,然后对一些数据进行一些加密解密的操作。
笔者之前没接触过任何加密解密方面的知识(当然,把每个字符的ASCII值加1之流对明文进行加密的“趣事”还是干过的,当时还很乐在其中。),甚至一开始连Crypto++的名字都没有听过,被BS了之后,就开始了Crypto++的入门探索过程。
最初,大概知道了要了解两大类算法中的几个算法——对称加密算法:DES、AES(后来因为人品好的缘故也了解了下非对称加密算法RSA,后文会详述何谓“人品好”);散列算法(需要通过Hash运算):SHA-256。
起初,笔者以为这样的知名算法在网上应该有很多现成的例子。笔者比较懒,对于自己不熟悉的东西,总希望找捷径,直接找别人现(在已经写)成可(编译运)行的代码然后施展ctrl + C,ctrl + V算法(咳,什么算法,是大法!!!)。
However,发觉网上的例子不是稀缺,就是只有代码没有解释。笔者觉得很难忍受这样的“莫名其妙”(奇怪的是笔者容忍了windows了,尽管它不开源),遂决定从零开始……
……写在代码前……
如果之前像笔者一样没相关经验——完全没接触过加密解密——,请务必阅读下文。
一些前期工作——编译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的论坛回帖,但是作了些修改:
#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
//因此解密的时候设置的密匙也是刚才在加密时设置好的key47
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
#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++的主页上下载源文件研究。作为入门和了解阶段,笔者觉得:只需要用起来即可。
//version at Crypto++ 5.602
#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中的代码有差异。
//http://hi.baidu.com/magic475/blog/item/19b37a8c1fa15a14b21bbaeb.html2
#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是否对应原来的message41
if( bIsSuccess )42
{43
cout << "sussessive verify" << endl;44
cout << "origin string is: " << strMessage << endl << endl;45
}46
else47
{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
没了这两位,在这个宇宙、这个时间、这个维度肯定不会有这篇文章,哈哈!
Crypto++入门学习笔记(DES、AES、RSA、SHA-256)(加解密)的更多相关文章
- Crypto++入门学习笔记(DES、AES、RSA、SHA-256)
最先附上 下载地址 背景(只是个人感想,技术上不对后面的内容构成知识性障碍,可以skip): 最近,基于某些原因和需要,笔者需要去了解一下Crypto++库,然后对一些数据进行一些加密解密的操作. 笔 ...
- Hadoop入门学习笔记---part1
随着毕业设计的进行,大学四年正式进入尾声.任你玩四年的大学的最后一次作业最后在激烈的选题中尘埃落定.无论选择了怎样的选题,无论最后的结果是怎样的,对于大学里面的这最后一份作业,也希望自己能够尽心尽力, ...
- Hadoop入门学习笔记---part4
紧接着<Hadoop入门学习笔记---part3>中的继续了解如何用java在程序中操作HDFS. 众所周知,对文件的操作无非是创建,查看,下载,删除.下面我们就开始应用java程序进行操 ...
- Hadoop入门学习笔记---part3
2015年元旦,好好学习,天天向上.良好的开端是成功的一半,任何学习都不能中断,只有坚持才会出结果.继续学习Hadoop.冰冻三尺,非一日之寒! 经过Hadoop的伪分布集群环境的搭建,基本对Hado ...
- PyQt4入门学习笔记(三)
# PyQt4入门学习笔记(三) PyQt4内的布局 布局方式是我们控制我们的GUI页面内各个控件的排放位置的.我们可以通过两种基本方式来控制: 1.绝对位置 2.layout类 绝对位置 这种方式要 ...
- PyQt4入门学习笔记(一)
PyQt4入门学习笔记(一) 一直没有找到什么好的pyqt4的教程,偶然在google上搜到一篇不错的入门文档,翻译过来,留以后再复习. 原始链接如下: http://zetcode.com/gui/ ...
- Hadoop入门学习笔记---part2
在<Hadoop入门学习笔记---part1>中感觉自己虽然总结的比较详细,但是始终感觉有点凌乱.不够系统化,不够简洁.经过自己的推敲和总结,现在在此处概括性的总结一下,认为在准备搭建ha ...
- Scala入门学习笔记三--数组使用
前言 本篇主要讲Scala的Array.BufferArray.List,更多教程请参考:Scala教程 本篇知识点概括 若长度固定则使用Array,若长度可能有 变化则使用ArrayBuffer 提 ...
- OpenCV入门学习笔记
OpenCV入门学习笔记 参照OpenCV中文论坛相关文档(http://www.opencv.org.cn/) 一.简介 OpenCV(Open Source Computer Vision),开源 ...
随机推荐
- 关于iphone、安卓手机VPN全面解析
现在智能手机功能越来越强大,网络APP层出不穷,社交大佬facebook.twitter等纷纷推出了自己的社交APP应用,大部分手机已经内置了很多社交应用,包括facebook等:android.io ...
- MySQL使用小记
时间格式化: select date_format('2008-08-08 22:23:01', '%Y%m%d%H%i%s'); 去重复: use iksdb3; select distinct ` ...
- Hibernate映射问题之OneToOne【自己整理】
首先贴上一个MkYong的例子 stock.java package com.mkyong.stock; import javax.persistence.CascadeType; import ja ...
- How to Call SharePoint 2013 API Service to Query The Lists
How to Call SharePoint 2013 API In SharePoint 2013, we can query the list by it owner service, then ...
- 蛙蛙推荐:如何实时监控MySql状态
大多网站的性能瓶颈都会出在数据库上,所以想把Mysql监控起来,就搜索了下相关资料. 后来和同事讨论了下cacti和nagios有些老套和过时,graphite比较时尚,然后就搜了下相关的资料,最后搞 ...
- SRS文档 软件需求说明书
[摘要] 随着信息时代科技的飞速发展,经济全球化已广为人知,英语作为全球最主要的语言之一,受到越来越多的人的喜爱,不仅为了增长知识,也为了能适应社会发展的需求.但是,学英语最重要的事首先是积累词汇,没 ...
- HTTP权威指南阅读笔记二:URL与资源
URL中的限制字符: 在URL中,有几个字符被保留起来,有着特殊的含义.有些字符不在定义的US-ASCII可打印字符集中.还有些字符会与某些因特网网关和协议产生混淆,因此不赞成使用. 下面这些字符是U ...
- [汇编] 从键盘输入一个一位数字,然后响铃n声
; multi-segment executable file template. data segment ends stack segment dw dup() ends code segment ...
- SpringMVC文件上传实现
SpringMVC(注解)上传文件需要注意的几个地方:1.form的enctype="multipart/form-data",这个是上传文件必须的2.applicationCon ...
- ftp 操作,支持断点续传或者继续下载。
1.ftpclient 类 public class FTPClient:IDisposable { public static object _obj = new object(); #region ...