C++ 使用 Crypto++ 库实现常用的加密算法

本文介绍了如何使用开源C++库Crypto++实现MD5摘要算法和AES加密解密。通过学习,读者将掌握Crypto++的安装、MD5与AES的工作原理,并能编写相应的C++代码进行加解密操作。

C++ 使用 Crypto++ 库实现常用的加密算法

 

Crypto++ 库是开源的 C++ 数据加密算法库,支持如下算法:RSA、MD5、DES、AES、SHA-256 等等,其中对于加密有对称加密和非对称加密。本实验通过 Cryto++ 库对字符串进行 MD5 校验,并用 AES 加密和解密。

1.1 知识点

  • 安装 Crypto++ 库并检验
  • 学习 MD5 摘要算法
  • 学习 AES 加密算法

1.2 效果截图

二、加密算法介绍

据记载,公元前400年,古希腊人发明了置换密码。1881年世界上的第一个电话保密专利出现。在第二次世界大战期间,德国军方启用“恩尼格玛”密码机,密码学在战争中起着非常重要的作用。 随着信息化和数字化社会的发展,人们对信息安全和保密的重要性认识不断提高,于是在1997年,美国国家标准局公布实施了“美国数据加密标准(DES)”,民间力量开始全面介入密码学的研究和应用中,采用的加密算法有 DES、RSA、SHA 等。随着对加密强度需求的不断提高,近期又出现了 AES、ECC 等。 使用密码学可以达到以下目的:保密性:防止用户的标识或数据被读取。数据完整性:防止数据被更改。身份验证:确保数据发自特定的一方。

2.1 加密的类型介绍

根据密钥类型不同将现代密码技术分为两类: 对称加密算法(秘密钥匙加密)和非对称加密算法(公开密钥加密)。

对称钥匙加密系统是加密和解密均采用同一把秘密钥匙,而且通信双方都必须获得这把钥匙,并保持钥匙的隐秘。 非对称密钥加密系统采用的加密钥匙(公钥)和解密钥匙(私钥)是不同的。

对称加密算法:

  • DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合。
  • 3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高
  • AES(Advanced EncryptionStandard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高

非对称算法:

  • RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的。
  • DSA(Digital Signature Algorithm):数字签名算法,是一种标准的 DSS(数字签名标准)
  • ECC(Elliptic Curves Cryptography):椭圆曲线密码编码学。

散列算法:

  • MD5(Message Digest Algorithm 5):是RSA数据安全公司开发的一种单向散列算法。
  • SHA(Secure Hash Algorithm):可以对任意长度的数据运算生成一个160位的数值。

著名的密码学算法库还有 OpenSSL 。OpenSSL 为网络通信提供安全及数据完整性的一种安全协议 ,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及 SSL 协议,并提供了丰富的应用程序供测试或其它目的使用。通过比较 Crypto++ 和 Openssl,我们可以发现 Crypto++ 支持的算法比 Openssl 多。

三、安装 Crypto++

安装 Crypto++ 有两种方法:

  • 1 直接用 apt-get 安装
  • 2 在官方下载源码安装,或者在 github 上获取源代码。

3.1 用 ubuntu 的源安装

sudo apt-get update
apt-cache pkgnames | grep -i crypto++

 

sudo apt-get install libcrypto++-dev libcrypto++-doc libcrypto++-utils libcrypto++9 libcrypto++9-dbg

 

3.2 测试

安装完成之后编写一个测试程序检测是否安装成功:

cd /home/shiyanlou/
vim test1.cpp
#include <iostream>
using std::cout;
using std::endl;

#include "cryptopp/integer.h"
using CryptoPP::Integer;

int main( int, char** ) {

  Integer i;

  cout << "i: " << i << endl;
  cout << "Yo, man!" << endl;

  return 0;
}

编写完后退出 vim ,编译程序

g++ -o test1 test1.cpp -lcryptopp # 这里的lcryptopp 就是我们的 cryto++ 库,在 fedora 上可能是 -lcrypto++
./test1

四、编写实现 MD5 摘要算法

MD5 的全称是 Message-Digest Algorithm 5,在90年代初由 MIT 的计算机科学实验室和 RSA Data Security Inc 发明,由 MD2/MD3/MD4 发展而来的。MD5 的实际应用是对一段 Message (字节串)产生 fingerprint (指纹),可以防止被“篡改”,因此通常用于密码的加密存储,数字签名,文件完整性验证等。 在很多操作系统中,用户的密码是以 MD5 值(或类似的其它算法)的方式保存的,用户 Login 的时候,系统是把用户输入的密码计算成 MD5 值, 然后再去和系统中保存的MD5值进行比较,来验证该用户的合法性。 使用 crypto++ 库生成 MD5 值很简单,理解这个函数即可:

以下是我们用到函数的原型:

StringSource (const std::string &string,
              bool pumpAll,
              BufferedTransformation *attachment=NU

stringsource 是一个字符数组和字符串的源,源(Source)就是指数据的来源。

Crypto++ 提供以下常用的源:

  • File
  • Random Number
  • Socket
  • String
  • Windows Pipe

关于它的更多细节,请查阅官方手册

打开终端输入:

cd /home/shiyanlou
vim md5.cpp

4.1 核心代码

int main()
{
  std::string digest, inData;
  std::cin >> inData;
  Weak1::MD5 md5;   //创建 md5 对象
  StringSource(inData, true, new HashFilter(md5, new HexEncoder(new StringSink(digest))));    //转换的关键

  std::cout<< digest ;
}
#endif//_CRYPTO_UTIL_H_

这里提供了完整的代码:

wget http://labfile.oss.aliyuncs.com/courses/654/md5.cpp

4.2 编译生成程序

g++ -o md5 md5.cpp -lcryptopp
./md5

 

五、编写实现 AES 加密解密

AES 采用的是对称加密,在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的 DES ,已经被多方分析且广为全世界所使用。 AES 设计有三个密钥长度:128,192,256 比特, 相对而言, AES 的128比特密钥比 DES 的56比特密钥强10^21倍 。 AES有五种加密模式:

  • 电码本模式(ECB模式): 这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。
  • 密码分组链接模式(CBC模式):这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
  • 计算器模式(CTR模式):计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。
  • 密码反馈模式(CFB模式):与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)。
  • 输出反馈模式(OFB模式):OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流, 再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。

本实验采用 CBC 模式。在设计类 CCryptoUtil 中,分为两个模块, encrypt4aes 加密, decrypt4aes 解密。

5.1 设计加密的模块

在 /home/shiyanlou/ 目录下新建 aes.cpp 文件并添加如下代码:

static int encrypt4aes(const std::string &inData, const std::string &strKey,
        std::string &outData, std::string &errMsg)
{
    outData = "";
    errMsg = "";

    if (inData.empty() || strKey.empty()) // 判断待加密的字符串或者密钥是否为空
    {
        errMsg = "indata or key is empty!!";
        return -1;
    }

    unsigned int iKeyLen = strKey.length();

    if (iKeyLen != AES_KEY_LENGTH_16 && iKeyLen != AES_KEY_LENGTH_24  //判断密钥的长度是否符合要求
            && iKeyLen != AES_KEY_LENGTH_32)
    {
        errMsg = "aes key invalid!!";
        return -2;
    }

    byte iv[AES::BLOCKSIZE];
    int iResult = 0;

    try
    {
        CBC_Mode<AES>::Encryption e;  //CBC 模式加密
        e.SetKeyWithIV((byte*) strKey.c_str(), iKeyLen, iv);
        StringSource ss(inData, true, new StreamTransformationFilter(e, new StringSink(outData)));    //加密的关键, outData 就是加密后的数据
    } catch (const CryptoPP::Exception& e)
    {
        errMsg = "Encryptor throw exception!!";
        iResult = -3;
    }

    return iResult;
}

5.2 设计解密的模块

在 /home/shiyanlou/aes.cpp 文件下添加如下代码:

static int decrypt4aes(const std::string &inData, const std::string &strKey,
        std::string &outData, std::string &errMsg)
{
    outData = "";
    errMsg = "";

    if (inData.empty() || strKey.empty()) // 判断待加密的字符串或者密钥是否为空
    {
        errMsg = "indata or key is empty!!";
        return -1;
    }

    unsigned int iKeyLen = strKey.length();

    if (iKeyLen != AES_KEY_LENGTH_16 && iKeyLen != AES_KEY_LENGTH_24  //判断密钥的长度是否符合要求
            && iKeyLen != AES_KEY_LENGTH_32)
    {
        errMsg = "aes key invalid!!";
        return -2;
    }

    byte iv[AES::BLOCKSIZE];
    int iResult = 0;

    try
    {
        CBC_Mode<AES>::Decryption d;    //CBC 模式解密
        d.SetKeyWithIV((byte*) strKey.c_str(), iKeyLen, iv);
        StringSource ss(inData, true,
                new StreamTransformationFilter(d, new StringSink(outData)));  //解密的函数,outData 是解密的结果
    }
    catch (const CryptoPP::Exception& e)
    {
        errMsg = "Encryptor throw exception";
        iResult = -3;
    }

    return iResult;
}

5.3 main 函数

在 /home/shiyanlou/aes.cpp 文件下添加如下代码:

int main(int argc, char **argv)
{
    std::string strCipher;     //待加密的字符串
    std::string strKey;       //用来加解密的密钥
    std::cout << "Please enter a string" << std::endl;
    std::cin >> strCipher;
    std::cout << "please enter a key, you just can write 16,24 or 32 words as a key" << std::endl;
    std::cin << strKey;

    std::string strResult;
    std::string strErrMsg;
    int iResult = CCryptoUtil::encrypt4aes(strCipher, strKey, strResult, strErrMsg);
    std::cout << "the result is :" << strResult << std::endl;

    if(iResult)   
    {
        std::cout<<"CCryptoUtil::encrypt4aes failed,errMsg:"<<strErrMsg;
        return -1;
    }

    std::string strPlainText;
    iResult = CCryptoUtil::decrypt4aes(strResult,strKey,strPlainText,strErrMsg);
    if(iResult)
    {
        std::cout<<"CCryptoUtil::decrypt4aes failed,errMsg:"<<strErrMsg;
        return -2;
    }

    std::cout << "PlainText:"<<strPlainText << std::endl;
}

这里也提供了完整的代码,可以直接编译:

wget http://labfile.oss.aliyuncs.com/courses/654/aes.cpp

5.3-1

 

六、总结

这一节实验我们学习安装了 crypto++ 库,从中学习到 crypto 库的基本用法,了解了 MD5 和 AES 的基本知识,并实现了对它们的应用。但是在实现 AES 加解密的时候没用将加密后的字符串打印出来,因为直接输出到屏幕上它是乱码的,那应该怎么 办呢?请同学们思考思考。

七、参考资料

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值