pub mod error;
extern crate aes_soft as aes;
extern crate base64;
extern crate block_modes;
extern crate byteorder;
extern crate chrono;
extern crate crypto;
extern crate rand;
extern crate serde;
extern crate quick_xml;
use aes::Aes256;
use base64::decode_config;
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cbc};
use byteorder::{BigEndian, ReadBytesExt};
use chrono::prelude::*;
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use std::borrow::{Cow, Borrow};
use urldecode::decode as urldecode;
use serde::Deserialize;
use quick_xml::de::from_str;
type Aes256Cbc = Cbc<Aes256, Pkcs7>;
#[derive(Debug, Deserialize)]
struct Xml {
#[serde(rename = "ToUserName", default)]
to_user_name: String,
#[serde(rename = "Encrypt", default)]
encrypt: String,
#[serde(rename = "AgentID", default)]
agentid: String,
}
pub struct WxCrptUtil<'a> {
token: Cow<'a, str>,
aeskey: Cow<'a, [u8]>,
receiveid: Cow<'a, str>,
}
impl<'a> WxCrptUtil<'a> {
pub fn new<T>(token: T, encodingaeskey: T, receiveid: T) -> error::Result<WxCrptUtil<'a>>
where
T: Into<Cow<'a, str>>,
{
let mut en_aeskey = encodingaeskey.into();
en_aeskey.to_mut().push_str("=");
let config = base64::STANDARD.decode_allow_trailing_bits(true);
let aeskey = decode_config(en_aeskey.to_mut(), config)?;
Ok(
WxCrptUtil {
token: token.into(),
aeskey: Cow::from(aeskey),
receiveid: receiveid.into(),
}
)
}
pub fn encrypt<T>(&self, random_str: T, plaintext: T) -> error::Result<String>
where
T: Into<Cow<'a, str>>,
{
let config = base64::STANDARD.decode_allow_trailing_bits(true);
let iv = &self.aeskey[0..16];
let key = &self.aeskey[..];
let mut v_bytes = random_str.into().to_mut().as_bytes().to_vec();
let mut text_bytes = plaintext.into().to_mut().as_bytes().to_vec();
let mut text_len_bytes = get_network_bytes_order(text_bytes.len()).to_vec();
let mut receiveid_bytes = self.receiveid.as_bytes().to_vec();
v_bytes.append(&mut text_len_bytes);
v_bytes.append(&mut text_bytes);
v_bytes.append(&mut receiveid_bytes);
let cipher = Aes256Cbc::new_var(key, iv)?;
let ciphertext = cipher.encrypt_vec(&v_bytes);
let encode_base64_encrypted = base64::encode_config(&ciphertext, config);
Ok(encode_base64_encrypted)
}
pub fn decrypt<T>(&self, cipher_text: T) -> error::Result<String>
where
T: Into<Cow<'a, str>>,
{
let config = base64::STANDARD.decode_allow_trailing_bits(true);
let text_s = decode_config(&urldecode(cipher_text.into().to_mut().to_string()), config)?;
let iv = &self.aeskey[0..16];
let key = &self.aeskey[..];
let cipher = Aes256Cbc::new_var(key, iv)?;
let decrypted_ciphertext = cipher.decrypt_vec(&text_s[..])?;
let content = &decrypted_ciphertext[16..];
let mut msg_slice = &content[..4];
let msg_len = msg_slice.read_u32::<BigEndian>().unwrap();
let msg = &content[4..(msg_len + 4) as usize];
let result = String::from_utf8(msg.to_vec()).unwrap();
Ok(result)
}
pub fn encrypt_msg<T>(&self, plaintext: T) -> error::Result<String>
where
T: Into<Cow<'a, str>>,
{
let random_str = get_random_str();
let encrypted_xml = self.encrypt(random_str, plaintext.into().to_mut().to_string())?;
let nonce = get_random_str();
let timestamp = Local::now().timestamp();
let signature = self.getsha1(timestamp.to_string(), nonce.clone(), encrypted_xml.clone());
Ok(
xml_create(
encrypted_xml.clone(),
signature,
timestamp.to_string(),
nonce.clone(),
)
)
}
pub fn decrypt_msg<T>(
&self,
msgsignature: T,
timestamp: T,
nonce: T,
postdata: T,
) -> error::Result<String>
where T: Into<Cow<'a, str>> + Copy
{
let x: Xml = from_str(postdata.into().borrow())?;
let signature = self.getsha1(timestamp.into().to_string(), nonce.into().to_string(), x.encrypt.clone());
if signature != msgsignature.into().to_mut().to_string() {
panic!(
"消息解密,Sha1签名验证不通过:\n {} \n {}",
signature, msgsignature.into()
);
};
let result = self.decrypt(x.encrypt.clone())?;
Ok(result)
}
pub fn getsha1<T>(&self, timestamp: T, nonce: T, encrypt: T) -> String
where T: Into<Cow<'a, str>>
{
let mut v = vec![
self.token.to_string(),
timestamp.into().to_mut().to_string(),
nonce.into().to_mut().to_string(),
urldecode(encrypt.into().to_mut().to_string()),
];
v.sort();
let str: String = v.into_iter().collect();
let mut hasher = Sha1::new();
hasher.input_str(&str);
hasher.result_str()
}
pub fn verifyurl<T>(
&self,
msgsignature: T,
timestamp: T,
nonce: T,
echostr: T,
) -> error::Result<String>
where T: Into<Cow<'a, str>> + Copy
{
let signature = self.getsha1(timestamp, nonce, echostr);
if signature != msgsignature.into().to_mut().to_string() {
panic!("Sha1签名验证不通过:\n {} \n {}", signature, msgsignature.into());
};
let result = self.decrypt(echostr)?;
Ok(result)
}
}
fn get_random_str() -> String {
let rand_string: String = thread_rng().sample_iter(&Alphanumeric).take(16).collect();
rand_string
}
fn get_network_bytes_order(num: usize) -> [u8; 4] {
(num as u32).to_be_bytes()
}
pub fn xml_create(encrypt: String, signaure: String, timestamp: String, nonce: String) -> String {
let xml_str = format!("<xml>\n<Encrypt><![CDATA[{}]]></Encrypt>\n<MsgSignature><![CDATA[{}]]></MsgSignature>\n<TimeStamp>{}</TimeStamp>\n<Nonce><![CDATA[{}]]></Nonce>\n</xml>", encrypt, signaure, timestamp, nonce);
xml_str
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xml_create() {
let s = super::xml_create(
"123".to_string(),
"345".to_string(),
"789".to_string(),
"012".to_string(),
);
let c_str = "<xml>\n<Encrypt><![CDATA[123]]></Encrypt>\n<MsgSignature><![CDATA[345]]></MsgSignature>\n<TimeStamp>789</TimeStamp>\n<Nonce><![CDATA[012]]></Nonce>\n</xml>";
assert_eq!(s, c_str);
}
}