1use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
47use anyhow::{anyhow, Result};
48use base64::alphabet::STANDARD;
49use base64::engine::{GeneralPurpose, GeneralPurposeConfig};
50use base64::Engine;
51use byteorder::{BigEndian, WriteBytesExt};
52use cbc::cipher::block_padding::NoPadding;
53use serde::{Deserialize, Serialize};
54use sha1::{Digest, Sha1};
55use std::iter::repeat_with;
56
57#[derive(Deserialize, Serialize, Debug)]
59pub struct VerifyInfo {
60 #[serde(rename = "msg_signature")]
62 pub signature: String,
63 pub timestamp: i64,
65 pub nonce: i64,
67}
68const G: GeneralPurpose = GeneralPurpose::new(
70 &STANDARD,
71 GeneralPurposeConfig::new().with_decode_allow_trailing_bits(true),
72);
73
74pub fn decode_aes_key(encoded_aes_key: &str) -> Result<Vec<u8>> {
76 Ok(G.decode(format!("{}=", encoded_aes_key))?)
77}
78
79pub fn calc_signature(token: &str, ts: &str, nonce: &str, data: &str) -> String {
107 let mut sort_arr = vec![token, ts, nonce, data];
108 sort_arr.sort();
109 let mut buffer = String::new();
110 for value in sort_arr {
111 buffer.push_str(value);
112 }
113
114 let mut sha = Sha1::new();
115
116 sha.update(buffer.as_bytes());
117 let signature = sha.finalize();
118 format!("{:x}", signature)
119}
120
121pub fn verify_url(
157 token: &str,
158 q: &VerifyInfo,
159 echo_str: &str,
160 aes_key: &[u8],
161 corp_id: &str,
162) -> Result<String> {
163 let signature = calc_signature(
164 token,
165 q.timestamp.to_string().as_str(),
166 q.nonce.to_string().as_str(),
167 echo_str,
168 );
169 if signature != q.signature {
170 return Err(anyhow::anyhow!("签名不正确"));
171 }
172 let es = base64::engine::general_purpose::STANDARD
173 .decode(echo_str)
174 .map_err(|e| anyhow::Error::new(e).context("echo_str base64 解密失败"))?;
175 let plaintext = decrypt(aes_key, &es)?;
176 let (msg, receiver_id) = parse_plain_text(&plaintext)?;
177 if receiver_id != corp_id {
178 return Err(anyhow!("receiver_id={} 与服务端配置不一致", receiver_id));
179 }
180 Ok(msg)
181}
182
183pub fn parse_plain_text(plaintext: &[u8]) -> Result<(String, String)> {
187 let msg_len = u32::from_be_bytes([plaintext[16], plaintext[17], plaintext[18], plaintext[19]]);
189 let msg = &plaintext[20..(20 + msg_len as usize)];
190 let receiver_id = &plaintext[(20 + msg_len as usize)..];
191 Ok((
192 String::from_utf8_lossy(msg).to_string(),
193 String::from_utf8_lossy(receiver_id).to_string(),
194 ))
195}
196
197type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
198type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
199pub fn decrypt(aes_key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
220 let iv = &aes_key[..16];
221 let key = &aes_key[..32];
222
223 let cipher = Aes256CbcDec::new_from_slices(key, iv)
224 .map_err(|e| anyhow::Error::new(e).context("初始化解密函数失败"))?;
225 let mut buffer = vec![0u8; data.len()];
226
227 let r = cipher
228 .decrypt_padded_b2b_mut::<NoPadding>(data, &mut buffer)
229 .map_err(|e| anyhow!("解密失败 {}", e))?;
230 let end = r.len() - (r[r.len() - 1] as usize);
231 Ok(r[..end].to_vec())
232}
233
234pub fn encrypt(aes_key: &[u8], plaintext: &str, corp_id: &str) -> Result<Vec<u8>> {
251 let mut wtr = gen_random_byte();
252 wtr.write_u32::<BigEndian>(plaintext.len() as u32)
253 .map_err(|e| anyhow::Error::new(e).context("写入数据长度失败"))?;
254 wtr.extend(plaintext.bytes());
255 wtr.extend(corp_id.bytes());
256
257 let iv = &aes_key[..16];
258 let key = &aes_key[..32];
259
260 let cipher = Aes256CbcEnc::new_from_slices(key, iv)
261 .map_err(|e| anyhow::Error::new(e).context("初始化加密函数失败"))?;
262
263 let mut buffer = vec![0u8; (wtr.len() + 15) / 16 * 16];
264 let r = cipher
265 .encrypt_padded_b2b_mut::<Pkcs7>(wtr.as_slice(), &mut buffer)
266 .map_err(|e| anyhow!("解密失败 {}", e))?;
267 Ok(r.to_vec())
268}
269
270fn gen_random_byte() -> Vec<u8> {
271 if cfg!(test) {
272 vec![
273 49u8, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54,
274 ]
275 } else {
276 repeat_with(|| fastrand::u8(..)).take(16).collect()
277 }
278}
279#[cfg(test)]
280mod test {
281 use super::*;
282
283 use base64::Engine;
284
285 #[test]
286 fn test_calc_signature() {
287 let token = "QDG6eK";
288 let receiver_id = "wx5823bf96d3bd56c7";
289 let encoded_aes_key = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C";
290 let verify_msg_sign = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
291 let verify_timestamp = "1409659589";
292 let verify_nonce = "263014780";
293 let verify_echo_str = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
294 assert_eq!(
296 verify_msg_sign,
297 calc_signature(token, verify_timestamp, verify_nonce, verify_echo_str)
298 );
299 let v = base64::engine::general_purpose::STANDARD
300 .decode(verify_echo_str)
301 .unwrap();
302 let aes_key = decode_aes_key(encoded_aes_key).unwrap();
303 let r = decrypt(aes_key.as_slice(), v.as_slice()).unwrap();
304 let (m, r) = dbg!(parse_plain_text(&r).unwrap());
305 assert_eq!(r, receiver_id);
306 assert_eq!("1616140317555161061", m.as_str());
307
308 let token = "QDG6eK";
309 let signature = "477715d11cdb4164915debcba66cb864d751f3e6";
310 let timestamps = "1409659813";
311 let nonce = "1372623149";
312 let msg_encrypt = "RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==";
313
314 assert_eq!(
315 signature,
316 calc_signature(token, timestamps, nonce, msg_encrypt)
317 );
318
319 let v = base64::engine::general_purpose::STANDARD
320 .decode(msg_encrypt)
321 .unwrap();
322
323 dbg!(parse_plain_text(&decrypt(aes_key.as_slice(), v.as_slice()).unwrap()).unwrap());
324 let signature = calc_signature("test", "123456", "test", "rust");
325 assert_eq!("d6056f2bb3ad3e30f4afa5ef90cc9ddcdc7b7b27", signature);
326
327 let receiver_id = "wx49f0ab532d5d035a";
328 let encoded_aes_key = "kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aQ";
329 let verify_echo_str = "4ByGGj+sVCYcvGeQYhaKIk1o0pQRNbRjxybjTGblXrBaXlTXeOo1+bXFXDQQb1o6co6Yh9Bv41n7hOchLF6p+Q==";
330
331 let v = base64::engine::general_purpose::STANDARD
332 .decode(verify_echo_str)
333 .unwrap();
334 let aes_key = decode_aes_key(encoded_aes_key).unwrap();
335 let r = decrypt(aes_key.as_slice(), v.as_slice()).unwrap();
336 let (m, r) = dbg!(parse_plain_text(&r).unwrap());
337 assert_eq!(r, receiver_id);
338 assert_eq!("5927782489442352469", m.as_str());
339 }
340
341 #[test]
342 fn test_decode_aes_key() -> Result<()> {
343 let encoded_aes_key = "IJUiXNpvGbODwKEBSEsAeOAPAhkqHqNCF6g19t9wfg2";
344 let b = decode_aes_key(encoded_aes_key)?;
345 let a = [
346 32u8, 149, 34, 92, 218, 111, 25, 179, 131, 192, 161, 1, 72, 75, 0, 120, 224, 15, 2, 25,
347 42, 30, 163, 66, 23, 168, 53, 246, 223, 112, 126, 13,
348 ];
349 assert_eq!(a, b.as_slice());
350 Ok(())
351 }
352
353 #[test]
354 fn test_decrypt() {
355 let encoded_aes_key = "kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aQ";
356 let aes_key = decode_aes_key(encoded_aes_key).unwrap();
357 let r = decrypt(
358 aes_key.as_slice(),
359 &base64::engine::general_purpose::STANDARD
360 .decode("9s4gMv99m88kKTh/H8IdkNiFGeG9pd7vNWl50fGRWXY=")
361 .unwrap(),
362 )
363 .unwrap();
364 dbg!(String::from_utf8(r.clone()).unwrap());
365 let (t, _) = parse_plain_text(&r).unwrap();
366 assert_eq!("test", &t);
367 }
368
369 #[test]
370 fn test_encrypt() -> Result<()> {
371 let encoded_aes_key = "kWxPEV2UEDyxWpmPdKC3F4dgPDmOvfKX1HGnEUDS1aQ";
372 let aes_key = decode_aes_key(encoded_aes_key)?;
373 let encrypted = encrypt(aes_key.as_slice(), "test", "rust").unwrap();
374 assert_eq!(
375 "9s4gMv99m88kKTh/H8IdkNiFGeG9pd7vNWl50fGRWXY=",
376 &base64::engine::general_purpose::STANDARD.encode(encrypted)
377 );
378 Ok(())
379 }
380
381 #[test]
382 fn test_verify_url() -> Result<()> {
383 let token = "QDG6eK";
384 let receiver_id = "wx5823bf96d3bd56c7";
385 let encoded_aes_key = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C";
386 let aes_key = decode_aes_key(encoded_aes_key).unwrap();
387
388 let verify_msg_sign = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
389 let verify_timestamp = 1409659589;
390 let verify_nonce = 263014780;
391 let verify_echo_str = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
392
393 let echo_str = verify_url(
394 token,
395 &VerifyInfo {
396 signature: verify_msg_sign.to_string(),
397 timestamp: verify_timestamp,
398 nonce: verify_nonce,
399 },
400 verify_echo_str,
401 aes_key.as_slice(),
402 receiver_id,
403 )?;
404 assert_eq!("1616140317555161061", echo_str.as_str());
405 Ok(())
406 }
407}