Rust 下 SM4/AES/RSA 加解密
aes
和rsa
加密使用非常广泛, 而sm4
在信创系统中比较常见, 以前用Java
开发的时候, 有强大的bc库, 还有易用的hutool, 现在转向了Rust
, 也是找到了RustCrypto这个项目.
RustCrypto
有点类似于bc
, 包含的东西非常多, 编码, 哈希, 签名, 加解密, 椭圆曲线等等. 常用的套件差不多准备全了, 这就来放手试试. 这个项目的文档还不那么完善, 而且代码库分得比较细, 我在使用中也是绕了一些弯路, 这篇文章呢就记录一下.
分块加密, 工作模式, 填充模式
** 分块加密**: 属于对称密钥算法, 加解密用的是同一个密钥. 分块的意思是, 在加解密过程中, 会把数据切分成等长的块来处理. RustCrypto 的实现在block-ciphers, sm4
和rsa
都属于分块加密算法 .
工作模式: 把初始向量, 密钥, 原文和密文这些分好块后, 要按什么顺序计算, 最终才得到加密的密文(或解密的原文). 工作模式和分块加密是组合关系, 一种加密算法能选用多种工作模式, 一种工作模式也能适用多种加密算法. RustCrypto 的实现在block-modes.
填充模式: 定义了怎么补全不完整的块, 上面提到分组加密需要把数据切分块处理, 比如 16 字节是一块, 而我们提供的数据并不总会是 16 的倍数, 这样最后一个块就可能会有"空白"部分, 而填充模式就决定了空白是什么内容. RustCrypto 的填充模式实现在block-padding.
SM4 加密/解密
上面是 cbc+sm4 加解密过程中要用到的依赖, 有些依赖项是通过sm4
重新导出来的, 比如填充模式Pkcs7
. 需要使用什么加密算法, 最好用这个库里导出的, 不然在编译时可能会出现晦涩的错误(一大串的泛型~).
这里给cbc::Encryptor<sm4::Sm4>
起了个别名Sm4CbcEnc
, 方法第一步是构造加密器, 参数是密钥key
和初始向量iv
. 虽然这两个参数的类型是切片, 但对长度是有一定要求的, key
和iv
得是 16 字节, 不然会panic
.
data
参数是要加密的原数据, 如果采用的填充模式是NoPadding
(不填充), 那数据的长度就得是 16 的倍数, 其他填充模式则不需要, 这里是用pkcs7
模式来填充.
方法的第二步加密原文, 并返回密文字节. encrypt_padded_vec_mut
方法会自动分配内存来放结果, 这需要开启alloc
特性. 如果没有开启这个特性的话, 需要自己申请内存, 这样可以在没有std
的环境中使用.
解密代码和加密区别不大, 一个是改成了解密器cbc::Decryptor<sm4::Sm4>
, 还一个是改成解密方法encrypt_padded_vec_mut
. key
, iv
, 还有填充模式和数据长度, 和加密过程的要求一样.
AES 加密/解密
某些工作模式可以让分块密码像流密码一样工作, 也就是不需要非得是完整的块, 也就不需要填充字节. ctr
和ofb
就是这样的模式, 接下来的aes
加密我就搭配ofb
的工作模式, 上面是我们需要用到的依赖.
aes
的密钥长度可以是 16, 24 或 32 字节, 密钥的长度不同, 需要用到的类型也不同. 上面代码是 16 字节长度密钥的实例, 选用的类型是Aes128
, 128b = 16B. 其他长度的密钥, 可以用Aes192
, Aes256
加解密的代码是一样的, 别名类型是为了看上去有所区别. apply_keystream
方法可以把密文数据存在原数据的位置, 所以这边的data
传递的是可变引用, 这样在不再需要原文数据的情况下, 就省去了分配额外的内存的开销.
RSA 加密/解密
RustCrypto 的实现在仓库RSA, 这是一种非对称密钥算法, 有公钥pub_key
和私钥pri_key
两个密钥. 公钥用来加密, (公钥Φ原文)=密文; 私钥则用来解密, (私钥Φ密文)=原文. 上面是生成密钥的代码, 参数length
指密钥长度为 2<sup>length</sup>.
公私钥可以用pem
(标头 + Base64 数据), der
(二进制数据)等方式编码, 私钥还有pkcs1
和pkcs8
格式的区别, 库里都提供了对应的解析方法. 上面实例代码中是解析 的der
格式. rsa
的加解密用起来说更简单一些, 因为没有了工作模式, 不过填充模式还是有, 这边用的 是Pkcs1v15Encrypt
.
原文发布在 Github: https://dlzht.github.io/012-rustxia-sm4-aes-rsajia-jie-mi/
评论