dev_tool/
secure_util.rs

1use crate::HexUtil;
2use base64::{prelude::BASE64_STANDARD, DecodeError, Engine};
3use base64ct::LineEnding;
4use hmac::{Hmac, Mac};
5use rsa::{
6    pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey},
7    Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey,
8};
9use sha1::Sha1;
10use sha2::{Digest, Sha256};
11
12/// 加解密工具,摘要算法等也归集次此工具类中
13pub struct SecureUtil;
14
15impl SecureUtil {
16
17    /// 生成RSA随机密钥对
18    ///
19    /// 该函数使用系统随机数生成器创建一个2048位的RSA密钥对。
20    ///
21    /// # 返回值
22    /// * `RsaPublicKey` - RSA公钥
23    /// * `RsaPrivateKey` - RSA私钥
24    ///
25    /// # Panics
26    /// 当密钥生成失败时会panic
27    pub fn rsa_random_key() -> (RsaPublicKey, RsaPrivateKey) {
28        let mut rng = rand::thread_rng(); // rand@0.8
29        let bits = 2048;
30        // 生成RSA私钥
31        let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
32        // 从私钥导出公钥
33        let pub_key = RsaPublicKey::from(&priv_key);
34        (pub_key, priv_key)
35    }
36
37    /// 使用RSA公钥对字节数据进行加密
38    ///
39    /// 该函数使用PKCS#1 v1.5填充方案对输入的字节数据进行RSA加密操作。
40    /// 加密过程中会使用随机数生成器来确保加密的安全性。
41    ///
42    /// # 参数
43    /// * `pub_key` - RSA公钥引用,用于执行加密操作
44    /// * `bytes` - 需要加密的原始字节数据
45    ///
46    /// # 返回值
47    /// 返回加密后的字节数据向量
48    ///
49    /// # Panics
50    /// 当加密操作失败时会panic,例如输入数据过长等情况
51    pub fn rsa_encode(pub_key: &RsaPublicKey, bytes: &[u8]) -> Vec<u8> {
52        let mut rng = rand::thread_rng(); // rand@0.8
53        let enc_data = pub_key
54            .encrypt(&mut rng, Pkcs1v15Encrypt, bytes)
55            .expect("failed to encrypt");
56        enc_data
57    }
58
59    /// 使用RSA私钥解密数据
60    ///
61    /// # 参数
62    /// * `priv_key` - RSA私钥引用,用于解密操作
63    /// * `enc_data` - 待解密的加密数据字节切片
64    ///
65    /// # 返回值
66    /// 返回解密后的原始数据字节向量
67    ///
68    /// # Panics
69    /// 当解密操作失败时会panic
70    pub fn rsa_decode(priv_key: &RsaPrivateKey, enc_data: &[u8]) -> Vec<u8> {
71        // 使用PKCS#1 v1.5填充方案进行RSA解密
72        let dec_data = priv_key
73            .decrypt(Pkcs1v15Encrypt, &enc_data)
74            .expect("failed to decrypt");
75        dec_data
76    }
77
78    /// 将RSA私钥转换为PKCS#8格式的PEM字符串
79    ///
80    /// 该函数接收一个RSA私钥对象,将其序列化为PKCS#8格式的PEM编码字符串。
81    /// PKCS#8是一种标准的私钥存储格式,使用PEM编码便于文本传输和存储。
82    ///
83    /// # 参数
84    /// * `priv_key` - 要转换的RSA私钥引用
85    ///
86    /// # 返回值
87    /// 返回PKCS#8格式的PEM编码字符串
88    ///
89    /// # Panics
90    /// 当私钥转换失败时会panic,因为使用了unwrap()
91    pub fn rsa_private_key_to_pkcs8(priv_key: &RsaPrivateKey) -> String {
92        let x = priv_key.to_pkcs8_pem(LineEnding::LF).unwrap();
93        x.to_string()
94    }
95
96    /// 将RSA公钥转换为PKCS#8格式的PEM字符串
97    ///
98    /// 该函数接收一个RSA公钥对象,将其序列化为PEM格式的字符串,
99    /// 使用LF作为换行符
100    ///
101    /// # 参数
102    /// * `pub_key` - 要转换的RSA公钥引用
103    ///
104    /// # 返回值
105    /// 返回PEM格式的PKCS#8公钥字符串
106    ///
107    /// # Panics
108    /// 当公钥转换失败时会panic(通过unwrap调用)
109    pub fn rsa_public_key_to_pkcs8(pub_key: &RsaPublicKey) -> String {
110        let x = pub_key.to_public_key_pem(LineEnding::LF).unwrap();
111        x
112    }
113
114    /// 从PKCS#8格式的PEM字符串中解析RSA私钥
115    ///
116    /// 该函数接收一个PKCS#8格式的PEM编码字符串,将其解析为RsaPrivateKey对象。
117    ///
118    /// # 参数
119    /// * `pkcs8_string` - 包含PKCS#8格式RSA私钥的PEM编码字符串
120    ///
121    /// # 返回值
122    /// 返回解析得到的RsaPrivateKey对象
123    ///
124    /// # Panics
125    /// 当PEM字符串格式不正确或无法解析时,函数会panic
126    pub fn rsa_private_key_from_pkcs8(pkcs8_string: &str) -> RsaPrivateKey {
127        // 从PKCS#8 PEM字符串解析RSA私钥
128        let res = RsaPrivateKey::from_pkcs8_pem(pkcs8_string).unwrap();
129        res
130    }
131    /// 从PKCS8格式的字符串创建RSA公钥
132    ///
133    /// 该函数接收一个PKCS8格式的PEM编码字符串,将其解析为RSA公钥对象。
134    ///
135    /// # 参数
136    /// * `pkcs8_string` - PKCS8格式的PEM编码字符串
137    ///
138    /// # 返回值
139    /// * `RsaPublicKey` - 解析得到的RSA公钥对象
140    ///
141    /// # 注意
142    /// 该函数使用unwrap()处理结果,如果解析失败会panic
143    pub fn rsa_public_key_from_pkcs8(pkcs8_string: &str) -> RsaPublicKey {
144        let res = RsaPublicKey::from_public_key_pem(pkcs8_string).unwrap();
145        res
146    }
147
148    /// 计算字节数组的SHA256哈希值并返回十六进制字符串
149    ///
150    /// 固定生成256 位(32 字节) 的哈希值,以 64 位十六进制字符串形式展示
151    ///
152    /// # 参数
153    /// * `bytes` - 需要计算哈希值的字节数组切片
154    ///
155    /// # 返回值
156    /// 返回SHA256哈希值的十六进制字符串表示
157    pub fn sha256_string(bytes: &[u8]) -> String {
158        // 计算SHA256哈希值
159        let result = Sha256::digest(bytes);
160        let result = &result[..];
161        // 将哈希值转换为十六进制字符串
162        HexUtil::to_hex(result)
163    }
164
165    pub fn md5_string(bytes: &[u8]) -> String {
166        // 计算输入数据的MD5哈希值
167        let data_md5 = md5::compute(bytes);
168        // 将MD5哈希值格式化为十六进制字符串
169        let md5_string = format!("{:x}", data_md5);
170        md5_string
171    }
172
173    /// 对字节数组进行Base64编码
174    ///
175    /// # 参数
176    /// * `bytes` - 需要编码的字节数组切片
177    ///
178    /// # 返回值
179    /// 返回编码后的Base64字符串
180    pub fn base64_encode(bytes: &[u8]) -> String {
181        BASE64_STANDARD.encode(bytes)
182    }
183
184    /// 对Base64编码的字符串进行解码
185    ///
186    /// # 参数
187    /// * `base64_str` - 需要解码的Base64编码字符串
188    ///
189    /// # 返回值
190    /// * `Ok(Vec<u8>)` - 解码成功,返回解码后的字节数据
191    /// * `Err(DecodeError)` - 解码失败,返回错误信息
192    ///
193    /// # 示例
194    /// ```
195    /// let decoded = base64_decode("SGVsbG8=").unwrap();
196    /// assert_eq!(decoded, b"Hello");
197    /// ```
198    pub fn base64_decode(base64_str: &str) -> Result<Vec<u8>, DecodeError> {
199        BASE64_STANDARD.decode(base64_str)
200    }
201
202    /// 使用HMAC-SHA1算法对数据进行签名
203    ///
204    /// 该函数接收待签名的数据和密钥,使用HMAC-SHA1算法生成数字签名。
205    ///
206    /// # 参数
207    /// * `data` - 待签名的字节数据切片
208    /// * `key` - 用于签名的密钥字符串
209    ///
210    /// # 返回值
211    /// 返回HMAC-SHA1签名结果的字节向量
212    ///
213    /// # Panics
214    /// 当密钥长度无效时会panic,但在实际中HMAC可以接受任意长度的密钥
215    pub fn hmac_sha1(data: &[u8], key: &str) -> Vec<u8> {
216        let key = key.as_bytes();
217        // 创建HMAC-SHA1实例并初始化
218        let mut hmac = Hmac::<Sha1>::new_from_slice(key).expect("HMAC can take key of any size");
219        // 更新HMAC状态,处理输入数据
220        hmac.update(data);
221        // 完成HMAC计算并获取结果
222        let result = hmac.finalize();
223        let hmac_bytes = result.into_bytes();
224        hmac_bytes.to_vec()
225    }
226
227    /// 将字节数组转换为MD5哈希字符串
228    ///
229    /// 该函数接收一个字节切片,计算其MD5哈希值,并将结果格式化为十六进制字符串。
230    ///
231    /// # 参数
232    /// * `bytes` - 需要计算MD5哈希的字节数据切片
233    ///
234    /// # 返回值
235    /// 返回表示MD5哈希值的十六进制字符串
236    ///
237    /// # 示例
238    /// ```
239    /// let data = b"hello world";
240    /// let md5_str = to_md5_str(data);
241    /// assert_eq!(md5_str, "5eb63bbbe01eeed093cb22bb8f5acdc3");
242    /// ```
243    #[deprecated(note = "This function is deprecated, please use md5_string instead")]
244    pub fn to_md5_str(bytes: &[u8]) -> String {
245        Self::md5_string(bytes)
246    }
247
248    /// 计算字节数组的MD5哈希值
249    ///
250    /// # 参数
251    /// * `bytes` - 需要计算MD5哈希值的字节数组切片
252    ///
253    /// # 返回值
254    /// 返回计算得到的MD5哈希值,以字节数组形式表示
255    ///
256    /// # 示例
257    /// ```
258    /// let data = b"hello world";
259    /// let hash = to_md5(data);
260    /// ```
261    pub fn to_md5(bytes: &[u8]) -> Vec<u8> {
262        // 使用md5库计算输入数据的哈希值并转换为Vec<u8>
263        let data_md5 = md5::compute(bytes).to_vec();
264        data_md5
265    }
266}
267
268#[cfg(test)]
269mod tests {
270
271    use super::*;
272
273    #[test]
274    fn test_base64() {
275        let bytes = "hello, rust!".as_bytes();
276        let base64_str = SecureUtil::base64_encode(bytes);
277        println!("{}", base64_str);
278    }
279
280    #[test]
281    fn test_sha256() {
282        let sha256_string = SecureUtil::sha256_string(b"hello world");
283        println!("{}", sha256_string);
284        assert_eq!(
285            sha256_string,
286            "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
287        );
288    }
289
290    #[test]
291    fn test_rsa() {
292        let (pub_key, priv_key) = SecureUtil::rsa_random_key();
293        // let data = "hello world";
294        // let enc_data = SecureUtil::rsa_encode(&pub_key, data.as_bytes());
295        // let dec_data = SecureUtil::rsa_decode(&priv_key, &enc_data);
296        // assert_eq!(data, std::str::from_utf8(&dec_data).unwrap());
297        // println!("{:?}", pub_key);
298        // let x = priv_key.to_pkcs8_pem(LineEnding::LF).unwrap();
299        // let y = (*x).clone();
300        // let x = x.to_string();
301        // println!("{}", y);
302
303        let pub_pkcs8_string = SecureUtil::rsa_public_key_to_pkcs8(&pub_key);
304        let priv_pkcs8_string = SecureUtil::rsa_private_key_to_pkcs8(&priv_key);
305
306        println!("pub_pkcs8_string => {}", pub_pkcs8_string);
307        println!("priv_pkcs8_string => {}", priv_pkcs8_string);
308
309        let pub_key = SecureUtil::rsa_public_key_from_pkcs8(&pub_pkcs8_string);
310        let priv_key = SecureUtil::rsa_private_key_from_pkcs8(&priv_pkcs8_string);
311
312        let data = "hello world";
313        let enc_data = SecureUtil::rsa_encode(&pub_key, data.as_bytes());
314        let dec_data = SecureUtil::rsa_decode(&priv_key, &enc_data);
315        println!("解密后{}", std::str::from_utf8(&dec_data).unwrap())
316    }
317
318    #[test]
319    fn test_rsa2() {
320        // https://www.metools.info/code/c80.html 生成RSA密钥对
321        let pub_pkcs8_string = r#"-----BEGIN PUBLIC KEY-----
322MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6HvM5v45WMgCVCM7gLyU
323q8Xi0fqwvfge7JU9C6niP+liQHyMALr9n+IQm76v/CnvfAYYnDJ1VYhLpGkPKJ2Q
32456InmEOSAcx9vfgGQcPkkTSqpH/vDHEgPysFsBAGVBvXfBxa9FBF8afrAmdqM3DO
325ygnWWCaSux0js2hkQB0+wUk3Lkw9yxcT+cK9D7aNaB3vVjxRVvGOuFVMuTFzLjis
326/1INfltSSvnCB4QPxA/h9YUrZ26itw7yQgGIiUbNydLx3X+qvWCGVOnYwX9z6wsF
3274Ch3VwlYF+H/y0zyjIuCpdJDy80D36lErcwmRpJhMciT4lXnLMTyZvlN0UFl492L
3281wIDAQAB
329-----END PUBLIC KEY-----"#;
330        let priv_pkcs8_string = r#"-----BEGIN PRIVATE KEY-----
331MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDoe8zm/jlYyAJU
332IzuAvJSrxeLR+rC9+B7slT0LqeI/6WJAfIwAuv2f4hCbvq/8Ke98BhicMnVViEuk
333aQ8onZDnoieYQ5IBzH29+AZBw+SRNKqkf+8McSA/KwWwEAZUG9d8HFr0UEXxp+sC
334Z2ozcM7KCdZYJpK7HSOzaGRAHT7BSTcuTD3LFxP5wr0Pto1oHe9WPFFW8Y64VUy5
335MXMuOKz/Ug1+W1JK+cIHhA/ED+H1hStnbqK3DvJCAYiJRs3J0vHdf6q9YIZU6djB
336f3PrCwXgKHdXCVgX4f/LTPKMi4Kl0kPLzQPfqUStzCZGkmExyJPiVecsxPJm+U3R
337QWXj3YvXAgMBAAECggEATgrBDgnpVlRN89Cf+OdVQRR8v+BX1G2mc+TlSTUOLkY6
338JUup89TRrwpEaQPqL8wkCI5DVKvbl4rZWaeq3weFzTwx7ntAWDo9O7g24XzRDa2Q
339WwhXRuXy3UGj9yZp+XIfNBqQrdMEp8qmqXftvrbvtAL5YT4Ro550jZVNkfg/SMKX
340VptR+ZGpjSWsQfLM+Q8i9GEo3FV1dpT41Uea96cfX2siaaNNhCUM3MYgVDRjp7gj
341N9qxHOil3ZmvJU2yRLBlwgtLO+5fQzGET/yOeoXJ9iCeyAz5MNweljdra4wNtriV
3429ECYR/d7R3Bz2DtaJ5ALyD80AeJ+1TTrLxa4exkHgQKBgQD7UXolf0J5qFMEn7yw
3430F7LNANX/Fx6T/ayf7MJEegHK0UYDc9wjyIQDcIdgauvqImseq7R5sQQbnAHdsZW
344f6bzb+1HjoUZr2c43QY4sBRDW7wfibtlgtaiMpIophlBwbvdcJRW65s7Ox8Czcl/
345JNqMdjXjHLSiEFND6v8Of68LFQKBgQDs0IByiZm7qJQaR8WOoXku5C0XLA5zCLyh
346jXTqla5m0qAFZU8abrvBl/o0+nhmGsMkQR0ssvLJLX/a0t1IWBq+WmIDB3YC4IVU
347eKoHY7AlDhMa0dJ+nbuvrPQdFsRAfH9CRhtMmQPCVsx41AXsQa/OKwUQJuNvhwMg
348AioMthWGOwKBgG6tvTtSRjZJuPXqWzELMxQOfgJ7s0ZyfNSzhGdUKXkuxykGu/p4
349LqofRQO1naSodqktBlyOYn5SBKhk2Igzg5TmD/tZeqiLJMxYGmtQsDvR5JGHGK5l
3505pxb5R5dt/XLmi61a76z2BNHwCp98mU6F72QOb8hXzOYOPNKRLVf6fjZAoGBAJD2
3512YpDvT3o5jBoOwEiy4Hu38NNjtLQSFhEtYtccVQ0HwzuhUvS+VB67gk8QjOOsmIh
352EfDo4kJQffHAHwFIHabkwRbFnHIKatPYwYygc5VbVkqWotorSFcz8oNUCnLHQ0eY
353juGG0YxHggd9EtsbIrl8EC9g/tyoszsG2CLL28U9AoGAbZsQM7989SiKtLJ652uw
3546kaRWItbBdVKPr5galsBhbe/eEEE6ePbJTeLroQ31u/jGZJNSyUjwfBq9k1OBfpL
355s7+EjYm9Yg5hcep499C2eRyqoX+2j8am2CYYy3rDPj8lYt+u/99oUyAyGYVKAByb
356j27x2gyifpYUwtDqYU5DwDU=
357-----END PRIVATE KEY-----"#;
358
359        println!("pub_pkcs8_string => {}", pub_pkcs8_string);
360        println!("priv_pkcs8_string => {}", priv_pkcs8_string);
361
362        let pub_key = SecureUtil::rsa_public_key_from_pkcs8(&pub_pkcs8_string);
363        let priv_key = SecureUtil::rsa_private_key_from_pkcs8(&priv_pkcs8_string);
364
365        let data = "hello world";
366        let enc_data = SecureUtil::rsa_encode(&pub_key, data.as_bytes());
367        let dec_data = SecureUtil::rsa_decode(&priv_key, &enc_data);
368        println!("解密后{}", std::str::from_utf8(&dec_data).unwrap())
369    }
370}