本文转自:http://suicidedamsel.blogbus.com/logs/27337701.html
在win32的操作系统下用vc6++来编译Crypto++™ Library 5.1 的源代码,在对应的目录下会产生文件夹Debug,在文件夹Debug里,会有一个编译好的静态库文件cryptlib.lib;下面通过实例研究这个静态库文件的使用:
在应用lib文件时先把库里的头文件和lib文件复制到工程的目录里这是最好的方法,或者把它们放到一个文件夹里修改指定的include 目录,即在菜单Tools中选Options,在Options的对话框里选Directories在include files和Library files 的页面添加指向该文件夹的路径。
3.1、Hash函数的应用:
Hash函数的最基本的用法就是计算Hash值,一个Hash函数是一个多对一的映射,可以输入任意长度的消息,输出却是一个固定长度的消息,而且,只要有一点很微小的差异的两个消息之间的Hash值也会有很大的差异,根据两个不同的Hash值就可以判断对应的两个消息是不同的。所以Hash函数通常用于数字签名和消息的完整性检测等等一些安全性方面的应用。下面举一个计算字符串的Hash值简单的例子。
#pragma comment (lib,"cryptlib.lib") //加载lib文件的语句 #include "md5.h" #include <iostream> using namespace CryptoPP; //使用名字空间CryptoPP using namespace std; //使用名字空间std void main(){ byte message[128]; byte m[16]; cout<<"输入字符串"<<endl; cin.getline((char*) message,128); MD5 md5; md5.Update(message,128); md5.Final(m); for(int i=0;i<16;i++)printf("%02x",m[i]);printf("\n"); }
这个例子首先生成MD5对象(类MD5在md5.h里定义),调用方法Update()和Final(),这两个方法是定义在基类HashTransformation里的。void Update (const byte *input, unsigned int length) 是用来处理输入的方法,input 是byte型是将要计算Hash值的字符串,length是字符串的长度。void Final (byte *digest) 是计算当前消息的Hash值并重新开始新的消息的方法,digest是用来存放Hash值的byte型数组。
HashTransformation还定义了几个常用的方法 void CalculateDigest (byte *digest, const byte *input, unsigned int length)也是用来计算hash值的方法,可以用md5. CalculateDigest(m,message,128),来替换上面例子md5.Update(message,128)与 md5.Final(m)这两个语句。
在消息完整性检测方面我们通常都是检测文件的完整性,一个相当大的文件如果对其进行一个很微小的修改我们根本看不出的,但是它们的Hash值却有很大的变化,所以我们只要比较Hash值就很容易看出该文件是否为原来的文件。计算文件的Hash值在这个密码库的支持下就比较简单就用下面的一行代码就行了
FileSource f(file, true, new HashFilter(md5, new HexEncoder(new ArraySink(buffer,2 * MD5::DIGESTSIZE))));
这一行代码总共用了4个类ArraySink、HexEncoder、HashFilter和FileSource。首先用类ArraySink复制一个2 * MD5::DIGESTSIZE 字节的buffer存储缓冲区,接着用类HexEncoder把这个缓冲区转换为16进制。计算Hash值主要用到类HashFilter。类FileSource是把要计算Hash值的文件file进行一定的转换放入临时缓冲区,然后调用实例化的HashFilter对其进行计算相应Hash函数的Hash值。
3.2 对称密码的应用
我们说明DES的编程。DES有四种工作模式:电子密码本模式(electronic codebook mode),密码分组链接模式(cipher block chaining mode),密码反馈模式(cipher feedback mode)以及输出反馈模式(output feedback mode)。
应用对称密码首先我们要确定用那种工作模式,即用ECB, CBC, CFB和OFB中的那种模式。Crypto++提供的工作模式对象,其本质上是把分组密码(block cipher)转换成序列密码(stream cipher)。下面就是创建工作模式CFB的例子:
byte key[AES::DEFAULT_KEYLENGTH], iv[AES::BLOCKSIZE];
CFB_Mode<AES >::Encryption cfbEncryption(key, AES::DEFAULT_KEYLENGTH, iv);
其中iv 是一个增量值,可以随便取一个字符串。知道什么创建工作模式后我们就通过具体的例子来研究对称密码的用法。下面我们来看高级加密标准AES在工作模式CFB下的简单应用。
string AESEncryptString(const char *instr, const char *passPhrase) // AES加密字符串函数 { std::string outstr; byte iv[AES::BLOCKSIZE]="123456"; AES::Encryption aesEncryption((byte *)passPhrase, AES::DEFAULT_KEYLENGTH); CFB_Mode_ExternalCipher::Encryption cfbEncryption(aesEncryption, iv); StreamTransformationFilter cfbEncryptor(cfbEncryption, new HexEncoder(new StringSink(outstr))); cfbEncryptor.Put((byte *)instr, strlen(instr)); cfbEncryptor.MessageEnd(); return outstr; } string AESDecryptString(const char *instr, const char *passPhrase)// AES解密字符串函数 { std::string outstr; byte iv[AES::BLOCKSIZE]="123456"; CFB_Mode<AES >::Decryption cfbDecryption((byte *)passPhrase, AES::DEFAULT_KEYLENGTH, iv); HexDecoder decryptor(new StreamTransformationFilter(cfbDecryption, new StringSink(outstr))); decryptor.Put((byte *)instr, strlen(instr)); decryptor.MessageEnd(); return outstr; }
由于AES加密速度比较快、效率高而且比较安全,所以我们通常用来对文件进行加解密,下面我们就来看加解密文件的例子。
void AESEncryptFile(const char *in, const char *out, const char *passPhrase)//加密文件 { byte iv[AES::BLOCKSIZE]="123456"; AES::Encryption aesEncryption((byte *)passPhrase, AES::DEFAULT_KEYLENGTH); CFB_Mode_ExternalCipher::Encryption cfbEncryption(aesEncryption, iv); FileSource f(in, true, new StreamTransformationFilter(cfbEncryption, new FileSink(out))); } void AESDecryptFile(const char *in, const char *out, const char *passPhrase)//解密文件 { byte iv[AES::BLOCKSIZE]="123456"; CFB_Mode<AES >::Decryption cfbDecryption((byte *)passPhrase, 16, iv); FileSource f(in, true, new StreamTransformationFilter(cfbDecryption, new FileSink(out))); }
下面来看在工作模式CBC下用DES-EDE对字符串加解密的简单例子。下面给出两个函数。
string EncryptString(const char *instr, const char *passPhrase) { string outstr; DefaultEncryptorWithMAC encryptor(passPhrase, new HexEncoder(new StringSink(outstr))); encryptor.Put((byte *)instr, strlen(instr)); encryptor.MessageEnd(); return outstr; } string DecryptString(const char *instr, const char *passPhrase) { string outstr; HexDecoder decryptor(new DefaultDecryptorWithMAC(passPhrase, new StringSink(outstr))); decryptor.Put((byte *)instr, strlen(instr)); decryptor.MessageEnd(); return outstr; }
加密函数string EncryptString(const char *instr, const char *passPhrase) 中instr是要加密的字符串,passPhrase是用来加密的密钥。这个函数用了密码库里的四个类DefaultEncryptorWithMAC、HexEncoder和StringSink。StringSink 是类StringSinkTemplate<std::string>的一个实例化,主要作用是添加输入到一个字符串对象。HexEncoder在hex.h里定义是用来转换所给的数据为十六进制。DefaultEncryptorWithMAC在default.h里定义,是用DES-EDE2 和 HMAC/SHA-1 在密钥下进行加密,Put()和MessageEnd()是这个类的两个基本的方法。unsigned int Put (const byte *inString, unsigned int length, bool blocking=true)是用来处理输入多重的字节,bool MessageEnd (int propagation=-1, bool blocking=true)用来作为输入消息的结束标志。这个函数的基本流程是:首先用类StringSink 把outstr添加到一个String对象,接着用HexEncoder把这个对象转换为十六进制。然后用加密密钥passPhrase产生一个DefaultEncryptorWithMAC对象encryptor,最后调用encryptor的方法Put()来处理输入instr进而对其进行加密,把加密后的密文放到outstr。
解密函数string DecryptString(const char *instr, const char *passPhrase) 中instr是要解密的字符串,passPhrase是用来解密的密钥,跟加密的密钥要相同。也用了三个类HexDecoder、DefaultDecryptorWithMAC和StringSink。HexDecoder是把给定的十六进制数据转换成为byte型。DefaultDecryptorWithMAC 也是定义在default.h文件里与类DefaultEncryptorWithMAC相应的是用来解密。这个函数的基本流程是:首先用类StringSink把outstr添加到一个String对象,然后用解密密钥passPhrase产生一个DefaultDecryptorWithMAC对象,利用类HexDecoder对DefaultDecryptorWithMAC对象进行数据转换生成对象decryptor,然后利用decryptor的方法Put()进行输入处理,进而进行解密,把解密后的明文放到outstr。
3.3、公钥密码的应用
RSA虽然很安全,但是最困难的就是要产生两个很大的素数,这样才会生成公钥和密钥。Crypto++密码库提供一些大整数的快速运算,还有高效的概率素数测试,运用这个库很容易找出足够大的素数;我们通过例子就来研究产生公钥密码RSA的密钥和公钥文件。
void GenerateRSAKey(unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed)//产生公钥密钥文件 { RandomPool randPool; randPool.Put((byte *)seed, strlen(seed)); RSAES_OAEP_SHA_Decryptor priv(randPool, keyLength); HexEncoder privFile(new FileSink(privFilename)); //打开文件实行序列化操作 priv.DEREncode(privFile); //写密码对象priv到文件对象privFile里 privFile.MessageEnd(); RSAES_OAEP_SHA_Encryptor pub(priv); HexEncoder pubFile(new FileSink(pubFilename)); pub.DEREncode(pubFile); pubFile.MessageEnd(); }
函数void GenerateRSAKey(unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed)是用来产生密钥和公钥文件。参数keyLength是密钥长度。PrivFilename是存放密钥的文件名,pubFilename是存放公钥的文件名,seed时产生密钥的种子。该函数首先用类RandomPool的方法Put()产生种子seed的byte型伪随机数。RSAES_OAEP_SHA_Decryptor是一个解密的公钥密码系统在文件rsa.h 有如下定义:
typedef RSAES<OAEP<SHA> >::Decryptor RSAES_OAEP_SHA_Decryptor;就是在这个类用前面产生的伪随机数和密钥长度keyLength生解密的密钥,然后通过类FileSink打开文件pubFilename实行序列化操作,用HexEncoder把它转换为十六进制,最后用方法DEREncode()把上面处理好的密码对象写入文件。这样就得到公钥密码的密钥文件了。产生公钥文件的方法和产生密钥文件不同的地方就是使用了RSAES_OAEP_SHA_Encryptor是一个加密的公钥密码系统,在文件rsa.h 有如下定义:
typedef RSAES<OAEP<SHA> >::Encryptor RSAES_OAEP_SHA_Encryptor;是用上面产生的密钥密码系统priv来生成相应公钥。
有了公钥密码的密钥和公钥文件后我们就可以用来对字符串进行加密和解密了,下面我们也给出了加密和解密的两个函数:
string RSAEncryptString(const char *pubFilename, const char *seed, const char *message)//加密 { FileSource pubFile(pubFilename, true, new HexDecoder); RSAES_OAEP_SHA_Encryptor pub(pubFile); RandomPool randPool; randPool.Put((byte *)seed, strlen(seed)); string result; StringSource(message, true, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(result)))); return result; } string RSADecryptString(const char *privFilename, const char *ciphertext)//解密 { FileSource privFile(privFilename, true, new HexDecoder); RSAES_OAEP_SHA_Decryptor priv(privFile); string result; StringSource(ciphertext, true, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(result)))); return result; }
加密函数string RSAEncryptString(const char *pubFilename, const char *seed, const char *message) 中pubFilename是公钥文件,seed是加密种子,message是要加密的字符串。这个函数的基本流程是:首先用类FileSource对公钥文件pubFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型, 然后用FileSource的对象pubFile 实例化公钥密码系统RSAES_OAEP_SHA_Encryptor生成对象pub。用类RandomPool在种子seed下用方法Put()产生伪随机数,Seed可以任取。用类StringSink 把outstr添加到一个String对象,接着用HexEncoder把这个对象转换为十六进制。然后用伪随机数randPool、公钥密码系统pub和十六进制的String对象实例化一个公钥密码加密的过滤器,再用这个过滤器对字符串message进行加密把结果放到十六进制的字符串result里,这样就完成了对字符串的加密。解密函数的基本流程跟加密函数的基本流程差不多,就使用了几个不同的类,但是这些类跟加密函数的对应类的功能是相对的,很容易理解所以就不多加以解释了。
现在我们就来用公钥密码RSA对字符串“密码类库Crypto++ 5.1的应用研究“进行加密和解密(完整的原代码附加在后面)。我们先取密钥长keyLength为 1000,seed可以随便要一个字符串,然后调用产生密钥函数GenerateRSAKey()产生了公钥文件pub.txt和密钥文件priv.txt。有了密钥和公钥文件后我们就可以调用RSA的加密解密函数对字符串进行加密解密了。