2024-01-28 15:50:55 +08:00

154 lines
4.5 KiB
Rust

use std::error::Error;
use actix_web::{App, HttpResponse, HttpServer, web};
use base64::Engine;
use base64::engine::general_purpose;
use mime;
use rand::Rng;
use rsa::pkcs1::DecodeRsaPrivateKey;
use rsa::pkcs1v15::SigningKey;
use rsa::RsaPrivateKey;
use rsa::signature::{SignatureEncoding, Signer};
use serde::{Deserialize, Serialize};
use serde_json::json;
use sha1::Sha1;
use x509_parser::pem::parse_x509_pem;
const INDEX_HTML: &[u8] = include_bytes!("../assets/index.html");
const ICONS: &[u8] = include_bytes!("../assets/images/icons.svg");
const CRT_PEM: &[u8] = include_bytes!("../jetbra.pem");
const RSA_PRIVATE_KEY: &'static str = include_str!("../jetbra.key");
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
pub async fn generate_license(web::Json(mut license): web::Json<License>) -> Result<HttpResponse, Box<dyn Error>> {
// 1 generate license id
let mut rng = rand::thread_rng();
let license_id: String = (0..10)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect();
license.license_id = Some(license_id.clone());
// 2 generate license_part_base64
let license_part = serde_json::to_string(&license)?;
let license_part_base64 = general_purpose::STANDARD.encode(license_part.as_bytes());
// 3 generate sigResultsBase64
let private_key = RsaPrivateKey::from_pkcs1_pem(RSA_PRIVATE_KEY)?;
let signing_key = SigningKey::<Sha1>::new(private_key);
let signature = signing_key.try_sign(license_part.as_bytes())?;
let sig_results_base64 = general_purpose::STANDARD.encode(signature.to_bytes());
// 4 generate cert base64
let (_, pem) = parse_x509_pem(CRT_PEM)?;
let cert_base64 = general_purpose::STANDARD.encode(pem.contents);
// 5 combine license
let license = format!("{}-{}-{}-{}", license_id, license_part_base64, sig_results_base64, cert_base64);
Ok(HttpResponse::Ok().content_type(mime::APPLICATION_JSON).body(json!({"license": license}).to_string()))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(|| async { HttpResponse::Ok().content_type(mime::TEXT_HTML_UTF_8).body(INDEX_HTML) }))
.route("/index.html", web::get().to(|| async { HttpResponse::Ok().content_type(mime::TEXT_HTML_UTF_8).body(INDEX_HTML) }))
.route("/images/icons.svg", web::get().to(|| async { HttpResponse::Ok().content_type(mime::IMAGE_SVG).body(ICONS) }))
.route("/generateLicense", web::post().to(generate_license))
})
.bind(("127.0.0.1", 3000))?
.run()
.await
}
#[derive(Debug, Serialize, Deserialize)]
pub struct License {
#[serde(rename = "licenseId")]
license_id: Option<String>,
#[serde(default = "default_licensee_name",rename = "licenseeName")]
licensee_name: String,
#[serde(default = "default_empty_str",rename = "assigneeName")]
assignee_name: String,
#[serde(default = "default_empty_str",rename = "assigneeEmail")]
assignee_email: String,
#[serde(default = "default_empty_str",rename = "licenseRestriction")]
license_restriction: String,
#[serde(default = "default_false",rename = "checkConcurrentUse")]
check_concurrent_use: bool,
products: Vec<Product>,
#[serde(default = "default_metadata")]
metadata: String,
#[serde(default = "default_hash")]
hash: String,
#[serde(default = "default_grace_period_days",rename = "gracePeriodDays")]
grace_period_days: i32,
#[serde(default = "default_true",rename = "autoProlongated")]
auto_prolongated: bool,
#[serde(default = "default_true",rename = "isAutoProlongated")]
is_auto_prolongated: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Product {
code: String,
#[serde(default = "default_expire_date",rename = "fallbackDate")]
fallback_date: String,
#[serde(default = "default_expire_date",rename = "paidUpTo")]
paid_up_to: String,
#[serde(default = "default_true")]
extended: bool,
}
fn default_licensee_name() -> String {
String::from("for test only")
}
fn default_empty_str() -> String {
String::from("")
}
fn default_false() -> bool {
false
}
fn default_true() -> bool {
true
}
fn default_metadata() -> String {
"0120230102PPAA013009".into()
}
fn default_hash() -> String {
"41472961/0:1563609451".into()
}
fn default_grace_period_days() -> i32 {
7
}
fn default_expire_date() -> String {
"2030-12-31".into()
}