Contents

Crypto++ Tips

编译安装

说到加密,首先想到的当然是 openssl,但是当我想用mingw64的clang++编译时,发现还需要perl,下载perl发现还是不行, 得用msys的perl,也就是说要安装msys,要是能不要这个依赖就好了。

对于c++来说,还有另一个选择,crypto++,编译使用makefile, 默认mingw64的g++可以编译通过,但clang++却不能,有一点bug,这个clang++也是被ms搞得有点复杂了。 makefile多少是有点难读懂的,并且最近也不用了,要是能用cmake就好了,幸好已经有其他人完成了这项工作 cryptopp-cmake,编译总算是顺利完成了。

使用入门

编译并安装后,cryptopp-cmake 提供了 cmake 的 config 文件,这用起来就简单了,大概是这样:

cmake_minimum_required(VERSION 3.28)
project(play)

set(CMAKE_CXX_STANDARD 17)
list(APPEND CMAKE_PREFIX_PATH "C:/Program Files (x86)/cryptopp-cmake")
find_package(cryptopp REQUIRED)

add_executable(play main.cpp)
target_link_libraries(play cryptopp::cryptopp)

虽然crypto++的wiki写得还算行,但我建议在编写c++代码前阅读 这篇文章, 要点是:

  • 各种加密、解密算法都是变换,source经过变化写到sink中
  • source可以是
    1. 文件:FileSource
    2. 字符串:StringSource
    3. 二进制缓冲区:ArraySource
  • sink和source相似,只是把Source换成Sink就好了
  • 所有的api中,传入的指针都被take ownership,而引用不会

非对称加密

非对称加密,也就是常用的RSA算法,crypto++可以自己生成密钥对,然后存储到FileSinkStringSink中。 如果想用openssl生成的密钥会有一点复杂,不过幸好我也没有这个需求。 注意一点就好了,RSA加密后的密文与密钥的位数是相同的,例如RSA 2048加密后的密文就是2048位,也就是2048/8字节。

生成密钥对

#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>

using namespace CryptoPP;

void GenerateKeyPair() {
  std::string privKeyStr; // 生成的私钥在这里
  std::string pubKeyStr; // 生成的公钥在这里

  AutoSeededRandomPool rng;  
  RSA::PrivateKey privKey;
  privKey.GenerateRandomWithKeySize(rng, 2048);
  StringSink sink(privKeyStr);
  privKey.Save(sink);
  
  RSA::PublicKey pubKey(privKey);
  StringSink sink2(pubKeyStr);
  pubKey.Save(sink2);
}

公钥加密

RSA::PublicKey pub;
StringSource src(pubKeyStr, true);
pub.Load(src);

std::string plain = "hello world";
std::string cipher;  // 加密后的结果

RSAES_OAEP_SHA_Encryptor enc(pub);
StringSource source(
    plain, true,
    new PK_EncryptorFilter(
        rng,
        enc,
        new StringSink(cipher)
    ));

私钥解密

RSA::PrivateKey priv;
StringSource src(privKeyStr, true);
priv.Load(src);

RSAES_OAEP_SHA_Decryptor enc(priv);
StringSource source(
    cipher, true,
    new PK_DecryptorFilter(
        rng,
        enc,
        new StringSink(plain2)
    ));

AES加密

AES是一个常用的,可靠的加密算法。AES有很多种模式,需要了解下,建议看下这篇文章, 要点就是,至少要用CBC模式,但是要正确地padding,最简单的选择也许是CTR模式。加密时需要

  • 一个128/256位的密钥(KEY)
  • 一个128位的随机数(IV)
void test_aes(const std::string& plain) {
    AutoSeededRandomPool rng;
    uint8_t key[16]{};
    uint8_t iv[16]{};
    rng.GenerateBlock(key, sizeof(key));
    rng.GenerateBlock(iv, sizeof(iv));

    std::string cipher;
    std::string plain2;

    //
    {
        CTR_Mode<AES>::Encryption enc;
        enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
        StringSource source(plain, true, new StreamTransformationFilter(
                                enc,
                                new StringSink(cipher)
                            ));
    }

    //
    {
        CTR_Mode<AES>::Decryption enc;
        enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
        StringSource source(cipher, true, new StreamTransformationFilter(
                                enc,
                                new StringSink(plain2)
                            ));

        std::cout << plain << std::endl;
        std::cout << plain2 << std::endl;
    }
}