use base64;
use reqwest::header::HeaderMap;
use crate::{errors::LabraError, LabradorResult, util::md5};
use serde::{Deserialize, Serialize};
use crate::prp::PrpCrypto;
#[derive(Debug, Eq, PartialEq)]
pub struct WechatCrypto {
key: Vec<u8>,
token: Option<String>,
s_receive_id : Option<String>,
}
#[derive(Debug, Eq, PartialEq)]
pub struct WechatCryptoV3 {
v3_key: Vec<u8>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SignatureHeader {
pub time_stamp: String,
pub nonce: String,
pub signature: String,
pub serial: String,
}
impl SignatureHeader {
pub fn from_header(header: &HeaderMap) -> Self {
let timpestamp = header.get("Wechatpay-Timestamp");
let time_stamp = timpestamp.map(|h| h.to_str().unwrap_or_default().to_string()).unwrap_or_default();
let nonce = header.get("Wechatpay-Nonce");
let nonce = nonce.map(|h| h.to_str().unwrap_or_default().to_string()).unwrap_or_default();
let signature = header.get("Wechatpay-Signature");
let signature = signature.map(|h| h.to_str().unwrap_or_default().to_string()).unwrap_or_default();
let serial = header.get("Wechatpay-Serial");
let serial = serial.map(|h| h.to_str().unwrap_or_default().to_string()).unwrap_or_default();
SignatureHeader {
time_stamp,
nonce,
signature,
serial
}
}
}
#[allow(unused)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptV3 {
pub original_type: Option<String>,
pub algorithm: String,
pub ciphertext: Option<String>,
pub nonce: String,
pub associated_data: Option<String>,
}
#[allow(unused)]
impl WechatCrypto {
pub fn new(encoding_aes_key: &str) -> WechatCrypto {
let mut aes_key = encoding_aes_key.to_owned();
aes_key += "=";
let key = base64::decode(&aes_key).unwrap_or_default();
WechatCrypto {
key,
token: None,
s_receive_id: None
}
}
pub fn token(mut self, token: &str) -> WechatCrypto {
self.token = token.to_string().into();
self
}
pub fn receive_id(mut self, receive_id: &str) -> WechatCrypto {
self.s_receive_id = receive_id.to_string().into();
self
}
pub fn get_signature(&self, timestamp: i64, nonce: &str, encrypted: &str) -> String {
let token = self.token.to_owned().unwrap_or_default();
let mut data = vec![
token.to_string(),
timestamp.to_string(),
nonce.to_string(),
];
if !encrypted.is_empty() {
data.push(encrypted.to_string());
}
data.sort();
let data_str = data.join("");
Self::get_sha1_sign(&data_str)
}
pub fn get_sha1_sign(encrypt_str: &str) -> String {
#[cfg(feature = "openssl-crypto")]
fn sha1(encrypt_str: &str) -> String {
let mut hasher = openssl::sha::Sha1::new();
hasher.update( encrypt_str.as_bytes());
let signature = hasher.finish();
hex::encode(signature)
}
#[cfg(not(feature = "openssl-crypto"))]
fn sha1(encrypt_str: &str) -> String {
use sha1::{Sha1, Digest};
let mut hasher = Sha1::new();
hasher.update(encrypt_str);
let hex = hasher.finalize();
hex::encode(hex)
}
sha1(encrypt_str)
}
pub fn create_hmac_sha256_sign(key: &str, message: &str) -> LabradorResult<String> {
let prp = PrpCrypto::new(key.as_bytes().to_vec());
prp.hmac_sha256_sign( message)
}
pub fn decrypt_data(session_key: &str, encrypted_data: &str, iv: &str) -> LabradorResult<String> {
let key = base64::decode(&session_key)?;
let prp = PrpCrypto::new(key);
let encrypted_data = base64::decode(encrypted_data)?;
let encrypt_content = prp.aes_128_cbc_decrypt_data(encrypted_data, Some(iv))?;
let encrypt_content = String::from_utf8_lossy(&encrypt_content).to_string();
println!("{}",encrypt_content);
Ok(encrypt_content)
}
pub fn check_signature(&self, signature: &str, timestamp: i64, nonce: &str, echo_str: &str) -> LabradorResult<bool> {
let real_signature = self.get_signature(timestamp, nonce, echo_str);
if signature != &real_signature {
return Err(LabraError::InvalidSignature("Unmatched signature.".to_string()));
}
Ok(true)
}
pub fn encrypt_message(&self, msg: &str, timestamp: i64, nonce: &str) -> LabradorResult<String> {
let prp = PrpCrypto::new(self.key.to_owned());
let encrypted_msg = "".to_string(); todo!("coding...");
let signature = self.get_signature(timestamp, nonce, &encrypted_msg);
let msg = format!(
"<xml>\n\
<Encrypt><![CDATA[{encrypt}]]></Encrypt>\n\
<MsgSignature><![CDATA[{signature}]]></MsgSignature>\n\
<TimeStamp>{timestamp}</TimeStamp>\n\
<Nonce><![CDATA[{nonce}]]></Nonce>\n\
</xml>",
encrypt=encrypted_msg,
signature=signature,
timestamp=timestamp,
nonce=nonce,
);
Ok(msg)
}
pub fn decrypt_message(&self, xml: &str, signature: &str, timestamp: i64, nonce: &str) -> LabradorResult<String> {
use crate::util::xmlutil;
let package = xmlutil::parse(xml);
let doc = package.as_document();
let encrypted_msg = xmlutil::evaluate(&doc, "//xml/Encrypt/text()").string();
let real_signature = self.get_signature(timestamp, nonce, &encrypted_msg);
if signature != &real_signature {
return Err(LabraError::InvalidSignature("unmatched signature.".to_string()));
}
let prp = PrpCrypto::new(self.key.to_owned());
let msg = "".to_string(); todo!("coding...");
Ok(msg)
}
pub fn decrypt_content(&self, encrypted_content: &str, signature: &str, timestamp: i64, nonce: &str) -> LabradorResult<String> {
let real_signature = self.get_signature(timestamp, nonce, &encrypted_content);
if signature != &real_signature {
return Err(LabraError::InvalidSignature("unmatched signature.".to_string()));
}
let prp = PrpCrypto::new(self.key.to_owned());
let result = prp.aes_128_cbc_decrypt_data(encrypted_content.as_bytes().to_vec(), None)?;
let msg = String::from_utf8_lossy(&result).to_string();
Ok(msg)
}
pub fn decrypt_xml(&self, encrypted_xml: &str, signature: &str, timestamp: i64, nonce: &str) -> LabradorResult<String> {
let doc = serde_xml_rs::from_str::<serde_json::Value>(encrypted_xml).unwrap_or(serde_json::Value::Null);
let cipher_text = doc["Encrypt"]["$value"].as_str().unwrap_or_default();
self.decrypt_content(cipher_text, signature, timestamp, nonce)
}
pub fn decrypt_data_refund(app_key: &str, ciphertext: &str) -> LabradorResult<String> {
let b64decoded = base64::decode(ciphertext)?;
let md5_key = md5::md5(app_key);
let key = md5_key.as_bytes();
let prp = PrpCrypto::new(key.to_vec());
prp.aes_256_ecb_decrypt(&b64decoded)
}
}
#[allow(unused)]
impl WechatCryptoV3 {
pub fn new(v3_key: &str) -> Self {
let v3_key = v3_key.as_bytes().to_vec();
WechatCryptoV3 {
v3_key
}
}
pub fn signature_v3(method: &String, url: &String, timestamp: i64, nonce_str: &String, body: &String, private_key: &String) -> LabradorResult<String> {
let signature_str = [method, url, ×tamp.to_string(), nonce_str, body];
let sign = signature_str.iter().map(|item| item.to_string()).collect::<Vec<_>>().join("\n") + "\n";
PrpCrypto::rsa_sha256_sign_with_pem(&sign, private_key)
}
pub fn sign(sign: &String, private_key: &String) -> LabradorResult<String> {
PrpCrypto::rsa_sha256_sign_with_pem(&sign, private_key)
}
pub fn verify(message: &str, signature: &str, public_key: &String) -> LabradorResult<bool> {
PrpCrypto::rsa_sha256_verify_with_pem(public_key, message, signature)
}
pub fn decrypt_data_v3(&self, decrypt: &EncryptV3) -> LabradorResult<Vec<u8>> {
let associated_data = decrypt.associated_data.to_owned().unwrap_or_default();
let nonce = decrypt.nonce.to_owned();
let ciphertext = decrypt.ciphertext.to_owned().unwrap_or_default();
let cipher_text = base64::decode(ciphertext)?;
let base64_cipher = hex::encode(cipher_text);
let cipher_text = hex::decode(base64_cipher)?;
let aad= associated_data.as_bytes();
let iv = nonce.as_bytes();
let cipherdata_length = cipher_text.len() - 16;
let cipherdata_bytes = &cipher_text[0..cipherdata_length];
let tag = &cipher_text[cipherdata_length..cipher_text.len()];
let prp = PrpCrypto::new(self.v3_key.to_owned());
let res = prp.aes_256_gcm_decrypt(aad, iv, cipherdata_bytes, tag)?;
Ok(res)
}
}