11. 加解密实现
大家好,我是程序喵。
前面我介绍过数据库的作用,会在上报前先把数据按一定格式存储到数据库中,用于持久化,防止信息丢失,而且也提到过,存储内容一定要是加密过的数据,防止数据泄漏,如果数据随随便便就以明文形式泄漏到外部,这可是很大的安全事故。
所以,本节我就会主要介绍如何实现加解密模块。
加密一般分为对称加密和非对称加密,对称加密大体指的是双方使用同样的秘钥,A使用一个秘钥加密后的数据,B使用同样的秘钥可以直接解密。非对称加密一般要分为公钥和私钥,公钥和私钥是一对,A使用公钥加密后的数据,B拿相同的公钥无法解密,拿其它的私钥也无法加密,一定要拿和此公钥同时相伴而成的私钥才可以成功解密。
常用的对称加密算法有AES、RC4等,常用的非对称加密算法有RSA、ECC等。给大家出个思考题,https使用的是对称加密还是非对称加密?
在本项目中,我们会使用AES这种经典的对称加密算法,会引入mbedtls三方库来实现加解密功能,关于如何引入mbedtls,可以查看第4节。
下面就直接介绍如何实现项目中的加解密算法模块。
首先,我们需要了解一下AES加密算法是如何工作的,前面说过AES(Advanced Encryption Standard)是一种对称加密算法,意味着加密和解密使用相同的密钥。在这段代码中,我们就会生成一个用于AES加密的密钥。
通过将salt与用户提供的password相结合,可以生成一个更强大的密钥。使用随机的salt可以防止恶意攻击者使用预先计算好的表来破解密码。
password是用户提供的密码,它结合salt,然后使用PBKDF2算法(Password-Based Key Derivation Function 2)和HMAC-SHA256哈希算法来生成密钥。
PBKDF2算法是一种密码推导函数,它使用 password 和salt作为输入,并通过多次迭代的哈希运算生成一个密钥。在这段代码中,我们使用了1000次迭代和SHA256哈希算法来生成一个32字节(256位)的密钥。
通过将password和salt传递给mbedtls_pkcs5_pbkdf2_hmac函数,我们可以生成一个加密安全的密钥。最后,我们将生成的密钥转换为std::string类型并返回。
这段代码实现了一个AES加密算法模块,主要包括以下几个函数及其作用:
- AESImpl::Init(const char key, size_t key_size): 该函数用于初始化加密算法模块。它接受一个密钥 key 和密钥的长度 key_size 作为参数,并进行以下操作:
- 调用 mbedtls_cipher_init 初始化加密上下文 encrypt_ctx_ 和解密上下文 decrypt_ctx_。
- 调用 mbedtls_cipher_setup 配置加密上下文 encrypt_ctx_ 和解密上下文 decrypt_ctx_,指定加密算法类型为 AES-256-CBC。
- 调用 mbedtls_cipher_set_padding_mode 设置加密上下文 encrypt_ctx_ 和解密上下文 decrypt_ctx_ 的填充模式为 PKCS7。
- 调用 mbedtls_cipher_setkey 设置加密上下文 encrypt_ctx_ 和解密上下文 decrypt_ctx_ 的密钥,将传入的 key 转换为 unsigned char* 类型,并指定密钥的位数为 key_size * 8。
- 获取加密上下文 encrypt_ctx_ 的块大小,并将其保存在变量 encrypt_block_size_ 中。
- 获取解密上下文 decrypt_ctx_ 的块大小,并将其保存在变量 decrypt_block_size_ 中。
- AESImpl::UnInit(): 该函数用于释放加密算法模块的资源。它进行以下操作:
- 调用 mbedtls_cipher_free 释放加密上下文 encrypt_ctx_ 和解密上下文 decrypt_ctx_。
- AESImpl::Encrypt(const void input, size_t input_size): 该函数用于对输入数据进行加密。它接受一个指向输入数据的指针 input 和输入数据的大小 input_size,并返回加密后的数据。在函数内部,它进行以下操作:
- 调用 mbedtls_cipher_set_iv 设置加密上下文 encrypt_ctx_ 的初始化向量(IV)为全零的 16 字节。
- 调用 mbedtls_cipher_reset 重置加密上下文 encrypt_ctx_ 的状态。
- 创建一个输出字符串 output,大小为输入数据的大小加上加密块的大小。
- 调用 mbedtls_cipher_update 对输入数据进行加密处理,并将加密结果存储在 output 中。
- 调用 mbedtls_cipher_finish 完成加密操作,并将最后一部分加密结果追加到 output 中。
- 调整 output 的大小为实际加密结果的大小,并返回加密结果。
通过使用这些函数,可以实现对数据的加密操作。
再介绍这段解密的代码
这段代码实现了一个AES解密算法模块,主要包括:
- AESImpl::Decrypt(const void input, size_t input_size)*: 该函数用于对输入数据进行解密。它接受一个指向输入数据的指针 input 和输入数据的大小 input_size,并返回解密后的数据。在函数内部,它进行以下操作:
- 调用 mbedtls_cipher_set_iv 设定解密上下文 decrypt_ctx_ 的初始向量为 iv_。
- 调用 mbedtls_cipher_reset 重置解密上下文 decrypt_ctx_。
- 创建一个初始大小为 input_size + decrypt_block_size_ 的输出字符串 output。
- 调用 mbedtls_cipher_update 对输入数据进行解密操作,并将解密后的数据存储到 output 中。
- 检查解密操作是否成功,如果失败则返回空字符串。
- 调用 mbedtls_cipher_finish 完成解密操作,并将结果追加到 output 中。
- 重新调整 output 的大小,将其截断为正确的长度。
- 返回解密后的数据。
可以看到,前面的类声明的是AESCryptImpl,带了个Impl后缀,这里我还是和上一节一样,使用了pimpl模式,对外类会只会暴露以下方法:
然后我们再写个程序测试一下本节功能:
结果就不贴了,肯定是正确的。
到这里,本节已经介绍完毕,我们下期见。
本节详细代码见:
- https://github.com/chengxumiaodaren/BuriedPoint/blob/main/src/crypt/crypt.h
- https://github.com/chengxumiaodaren/BuriedPoint/blob/main/src/crypt/crypt.cc
- https://github.com/chengxumiaodaren/BuriedPoint/blob/main/tests/test_crypt.cc
