use std::fs::File;
use std::io::{Read, Write};
use std::error::Error;
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use ring::aead;
use ring::aead::{Aad, LessSafeKey, Nonce};
#[derive(Debug, Serialize, Deserialize)]
pub struct LicenseInfo {
pub license_key: String,
pub expire_date: String,
pub issued_date: String,
pub issued_to: String,
pub api_allowed: bool,
pub api_limit: u32,
pub api_used: u32,
}
pub struct LicenseConfig {
key: Vec<u8>,
}
impl LicenseConfig {
pub fn new(key: &str) -> Result<Self, Box<dyn Error>> {
// 确保密钥长度为32字节(AES-256)
let key_bytes = if key.len() >= 32 {
key.as_bytes()[..32].to_vec()
} else {
// 如果密钥不足32字节,填充到32字节
let mut bytes = key.as_bytes().to_vec();
bytes.resize(32, 0);
bytes
};
Ok(Self {
key: key_bytes,
})
}
// 加密license信息
pub fn encrypt_license(&self, license: &LicenseInfo) -> Result<Vec<u8>, Box<dyn Error>> {
// 序列化license信息为JSON
let license_json = serde_json::to_string(license)?;
let license_bytes = license_json.as_bytes();
// 创建AES-256-CBC加密器
let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, &self.key)?;
let key = LessSafeKey::new(unbound_key);
// 生成随机12字节nonce(GCM模式推荐使用12字节nonce)
let mut nonce_bytes = [0u8; 12];
getrandom::getrandom(&mut nonce_bytes).map_err(|e| e.to_string())?;
let nonce = Nonce::assume_unique_for_key(nonce_bytes);
// 创建加密缓冲区
let mut ciphertext = Vec::with_capacity(license_bytes.len() + aead::AES_256_GCM.tag_len());
ciphertext.extend_from_slice(license_bytes);
// 加密数据
key.seal_in_place_append_tag(nonce, Aad::empty(), &mut ciphertext)?;
// 组合nonce和密文
let mut result = Vec::with_capacity(nonce_bytes.len() + ciphertext.len());
result.extend_from_slice(&nonce_bytes);
result.extend_from_slice(&ciphertext);
Ok(result)
}
// 解密license信息
pub fn decrypt_license(&self, encrypted_data: &[u8]) -> Result<LicenseInfo, Box<dyn Error>> {
// 分离nonce和密文
if encrypted_data.len() < 12 + aead::AES_256_GCM.tag_len() {
return Err("Invalid encrypted data".into());
}
let nonce_bytes = &encrypted_data[..12];
let ciphertext = &encrypted_data[12..];
// 创建AES-256-GCM解密器
let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, &self.key)?;
let key = LessSafeKey::new(unbound_key);
let nonce = Nonce::assume_unique_for_key(nonce_bytes.try_into()?);
// 创建解密缓冲区
let mut plaintext = ciphertext.to_vec();
// 解密数据
key.open_in_place(nonce, Aad::empty(), &mut plaintext)?;
plaintext.truncate(plaintext.len() - aead::AES_256_GCM.tag_len());
// 解析JSON数据
let license_info: LicenseInfo = serde_json::from_slice(&plaintext)?;
Ok(license_info)
}
// 验证license有效性
pub fn verify_license(&self, license_path: &str) -> Result<LicenseInfo, Box<dyn Error>> {
// 读取license文件
let mut file = File::open(license_path)?;
let mut encrypted_data = Vec::new();
file.read_to_end(&mut encrypted_data)?;
// 解密license
let license_info = self.decrypt_license(&encrypted_data)?;
// 验证license是否过期
let expire_date = DateTime::parse_from_rfc3339(&license_info.expire_date)?;
let now = Utc::now();
if expire_date < now {
return Err("License has expired".into());
}
Ok(license_info)
}
// 验证API license有效性
pub fn verify_api_license(&self, license_path: &str) -> Result<LicenseInfo, Box<dyn Error>> {
let license_info = self.verify_license(license_path)?;
// 验证API是否被允许
if !license_info.api_allowed {
return Err("API access is not allowed with this license".into());
}
// 验证API调用是否超出限制
if license_info.api_used >= license_info.api_limit {
return Err("API call limit exceeded".into());
}
Ok(license_info)
}
// 更新API使用次数
pub fn update_api_usage(&self, license_path: &str) -> Result<LicenseInfo, Box<dyn Error>> {
// 读取并验证license
let mut license_info = self.verify_license(license_path)?;
// 更新API使用次数
license_info.api_used += 1;
// 重新加密并写入license文件
let encrypted_license = self.encrypt_license(&license_info)?;
let mut file = File::create(license_path)?;
file.write_all(&encrypted_license)?;
Ok(license_info)
}
}
// 生成示例license
pub fn generate_sample_license() -> LicenseInfo {
LicenseInfo {
license_key: "sample-license-key-12345".to_string(),
expire_date: "2026-12-31T23:59:59Z".to_string(),
issued_date: "2025-01-01T00:00:00Z".to_string(),
issued_to: "Test Company".to_string(),
api_allowed: true,
api_limit: 10000,
api_used: 0,
}
}
Comments | NOTHING