use std::thread::sleep;
use aes_gcm::aead::{Aead, KeyInit};
use aes_gcm::{Aes256Gcm, Nonce};
use base64::{engine::general_purpose, DecodeError, Engine as _};
use openssl::{hash::MessageDigest, pkey::PKey, sign::Signer,x509::X509};
use rand::Rng;
use std::time::Duration;
use std::fs;
use std::path::Path;
use openssl::hash::hash;
use openssl::nid::Nid;
pub fn gen_nonce(len: usize) -> String {
let mut rng = rand::thread_rng();
(0..len)
.map(|_| {
let n = rng.gen_range(0..36);
std::char::from_digit(n as u32, 36).unwrap()
})
.collect()
}
pub fn now_ts() -> String {
::time::OffsetDateTime::now_utc().unix_timestamp().to_string()
}
pub fn rsa_sign_sha256_pem(private_key_pem: &str, data: &str) -> anyhow::Result<String> {
let private_key_pem = load_private_key(private_key_pem);
println!("private_key_pem: {:?}", private_key_pem);
let pkey = PKey::private_key_from_pem(private_key_pem.as_bytes())?;
let mut signer = Signer::new(MessageDigest::sha256(), &pkey)?;
signer.update(data.as_bytes())?;
let sig = signer.sign_to_vec()?;
Ok(general_purpose::STANDARD.encode(sig))
}
pub fn rsa_verify_sha256_pem(
public_key_pem: &str,
data: &str,
signature_base64: &str,
) -> anyhow::Result<bool> {
use openssl::sign::Verifier;
let pkey = if public_key_pem.contains("BEGIN CERTIFICATE") {
let cert = X509::from_pem(public_key_pem.as_bytes())?;
cert.public_key()?
} else {
PKey::public_key_from_pem(public_key_pem.as_bytes())?
};
let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey)?;
verifier.update(data.as_bytes())?;
let sig = general_purpose::STANDARD.decode(signature_base64)?;
Ok(verifier.verify(&sig)?)
}
pub fn aes_gcm_decrypt(
api_v3_key: &str,
associated_data: &str,
nonce: &str,
ciphertext_b64: &str,
) -> anyhow::Result<String> {
let key = api_v3_key.as_bytes();
if key.len() != 32 {
anyhow::bail!("api_v3_key must be 32 bytes");
}
let cipher = Aes256Gcm::new_from_slice(key).map_err(|e| anyhow::anyhow!(e.to_string()))?;
let nonce_bytes = nonce.as_bytes();
#[allow(deprecated)]
let nonce= Nonce::from_slice(nonce_bytes);
let ciphertext = base64::engine::general_purpose::STANDARD.decode(ciphertext_b64)?;
let plain = cipher.decrypt(
nonce,
aes_gcm::aead::Payload {
msg: &ciphertext,
aad: associated_data.as_bytes(),
},
);
let plain = plain.map_err(|e| anyhow::anyhow!(e.to_string()))?;
Ok(String::from_utf8(plain)?)
}
pub async fn retry_async<F, Fut, T, E>(mut attempts: usize, mut f: F) -> Result<T, E>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = Result<T, E>>,
E: std::fmt::Debug,
{
let mut delay = 200u64;
loop {
match f().await {
Ok(v) => return Ok(v),
Err(e) => {
attempts -= 1;
if attempts == 0 {
return Err(e);
}
sleep(Duration::from_millis(delay));
delay = std::cmp::min(delay * 2, 5000);
}
}
}
}
pub fn extract_pubkey_from_cert(cert_pem: &str) -> anyhow::Result<String> {
let cert = X509::from_pem(cert_pem.as_bytes())?;
let pubkey: PKey<openssl::pkey::Public> = cert.public_key()?;
let pub_pem = pubkey.public_key_to_pem()?;
Ok(String::from_utf8(pub_pem)?)
}
pub fn get_cert_sn(cert: &str) -> anyhow::Result<String> {
let cert = std::fs::read_to_string(cert)?;
get_cert_sn_by_content(cert.as_ref())
}
pub fn get_root_cert_sn(cert_content: &str) -> anyhow::Result<String> {
let cert_content = std::fs::read_to_string(cert_content)?;
let root_cert_sn = cert_content
.split_inclusive("-----END CERTIFICATE-----")
.filter(|cert| {
let ssl = X509::from_pem(cert.as_ref()).unwrap();
let algorithm = ssl.signature_algorithm().object().nid();
algorithm == Nid::SHA256WITHRSAENCRYPTION || algorithm == Nid::SHA1WITHRSAENCRYPTION
})
.filter_map(|cert| get_cert_sn_by_content(cert.as_ref()).ok())
.collect::<Vec<String>>()
.join("_");
Ok(root_cert_sn)
}
pub fn get_cert_sn_by_content(cert_content: &[u8]) -> anyhow::Result<String> {
let cert = X509::from_pem(cert_content).unwrap();
let mut sumary = cert
.issuer_name()
.entries()
.map(|item| {
item.object().nid().short_name().unwrap().to_string()
+ "="
+ &item.data().as_utf8().unwrap().to_string()
})
.collect::<Vec<String>>();
sumary.reverse();
let sumary = sumary.join(",");
let serial_number = cert.serial_number().to_bn()?.to_dec_str()?;
let sumary = sumary + &serial_number;
let md5_digest = hash(MessageDigest::md5(), sumary.as_bytes())?;
let cert_sn: &String = &md5_digest
.iter()
.map(|byte| format!("{:02x}", byte))
.collect();
let mut cert_sn = cert_sn.to_string();
while cert_sn.len() < 32 {
cert_sn.insert(0, '0');
}
Ok(cert_sn)
}
#[inline]
pub fn load_private_key(source: &str) -> String {
let path = Path::new(source);
if path.exists() {
let data = fs::read_to_string(path).unwrap_or_default();
if source.ends_with(".pem") || data.contains("-----BEGIN") {
return data; }
return wrap_rsa_key(&data);
}
if source.contains("-----BEGIN") {
source.to_string()
} else {
wrap_rsa_key(source)
}
}
#[inline]
fn wrap_rsa_key(raw: &str) -> String {
let mut key = String::with_capacity(raw.len() + 80);
key.push_str("-----BEGIN RSA PRIVATE KEY-----\n");
let bytes = raw.as_bytes();
let mut i = 0;
while i < bytes.len() {
let end = usize::min(i + 64, bytes.len());
key.push_str(unsafe { std::str::from_utf8_unchecked(&bytes[i..end]) });
key.push('\n');
i = end;
}
key.push_str("-----END RSA PRIVATE KEY-----");
key
}
pub fn base64_encode<T>(input: T) -> String
where
T: AsRef<[u8]>,
{
general_purpose::STANDARD.encode(input)
}
pub fn base64_decode<T>(input: T) -> Result<Vec<u8>, DecodeError>
where
T: AsRef<[u8]>,
{
general_purpose::STANDARD.decode(input)
}