Crypto++虽然全面,但是太重了。如果只是想要实现安全但不需要特定的方法,则Monocypher是更好的选择。
Monocypher是一个轻量化的安全库,只有一个.h和.c文件,直接加入工程即可。其提供了安全方法的良好抽象,直接在业务层面使用,不需要使用者关心底层用的到底是什么实现。
一、Hash
有一个crypto_blake2b可以直接算数据的hash,使用方法很简单,比如官网例子:
uint8_t hash [64]; /* Output hash (64 bytes) */
uint8_t message[12] = "Lorem ipsum"; /* Message to hash */
crypto_blake2b(hash, message, 12);
Monocypher的所有代码风格都是这样后面的内容就不在这里给出了,去官网看例子即可。
二、公钥签名
当你只需要authentication和integrity,不需要encryption时,可以使用public key signature算法。这个算法分成三步
首先A使用crypto_sign_public_key生成公钥和私钥,公钥传给B。
其次A使用crypto_sign函数,对消息进行签名(需要私钥)
最后B收到A的消息和签名后,使用crypto_check对消息进行检查(需要公钥)
该算法保证没有私钥的人是不可能提供有效签名的,所以能够保证authentication和integrity。
三、认证加密
Monocypher提供的authenticated encryption即可使用对称加密也可使用非对称加密。
先看对称加密。假设A和B通过某种途径互通了秘钥,则算法分成三步:
首先A使用crypto_lock对消息进行加密。需要用到秘钥和nonce(随机字节),加密的过程中库会生成mac(message authentication code)。
然后A把密文+nonce+mac发送给B。注意nonce和mac可以直接发送不用进一步处理,但是只能用一次。
最后B收到A的消息后,使用crypto_unlock对消息进行解密(需要秘钥+nonce+mac)。
再看非对称加密。
如果加密通讯的前提是已经有一条秘密渠道,那么适用范围就太狭窄了。
Monocypher的非对称加密体现为key exchange算法。
要在非安全环境下交换秘钥,需要以下几步:
首先A在本地生成私钥AS(随机字节),然后使用crypto_key_exchange_public_key生成对应公钥AP。同理B在本地生成私钥BS(随机字节),然后使用crypto_key_exchange_public_key生成对应公钥BP。
然后A和B互相将公钥发给对方,然后使用crypto_key_exchange,基于自己的私钥和对方的公钥生成session key SSK。这个SSK就相当于对称加密中的共同秘钥。
最后A使用crypto_lock加密信息(秘钥用SSK),B使用crypto_unlock解密信息(秘钥用SSK),方法跟上述对称加密一样。
四、生成随机字节
Monocypher为了轻量化没有提供生成随机字节的方法,并建议用户使用操作系统层面的API而不是在应用层面生成随机字节。
本人经过摸索可以这样实现:
在Mac上可以
#include <stdlib.h>
void Crypto::GenRndBuffer(uint8_t* pBuf, int nSize)
{
arc4random_buf(pBuf, nSize);
}
在Linux上可以
//https://man7.org/linux/man-pages/man2/getrandom.2.html
#include <sys/random.h>
void Crypto::GenRndBuffer(uint8_t* pBuf, int nSize)
{
getrandom(pBuf, nSize, 0);
}
在Win上可以
//https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
#include <Windows.h>
#include <bcrypt.h>
#pragma comment(lib, "Bcrypt")
void Crypto::GenRndBuffer(uint8_t* pBuf, int nSize)
{
BCRYPT_ALG_HANDLE hProv;
BCryptOpenAlgorithmProvider(&hProv, BCRYPT_RNG_ALGORITHM, NULL, 0);
BCryptGenRandom(hProv, pBuf, nSize, 0);
BCryptCloseAlgorithmProvider(hProv, 0);
}