generate_license

This commit is contained in:
novice.li 2024-01-28 15:50:55 +08:00
parent f8ee182ea8
commit 6ee0e87f68
3 changed files with 790 additions and 288 deletions

955
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,12 +5,9 @@ edition = "2021"
[dependencies.tokio]
version = "1.35.1"
features = ["full"]
[dependencies.axum]
version = "0.7.4"
[dependencies.actix-web]
version = "4.4.1"
[dependencies.base64]
version = "0.21.7"
@ -32,9 +29,11 @@ features = ["derive"]
version = "1.0.112"
[dependencies.tower]
version = "0.4.13"
[dependencies.x509-parser]
version = "0.15.1"
[dependencies.tower-http]
version = "0.5.1"
[dependencies.rand]
version = "0.8.1"
[dependencies.mime]
version= "0.3.17"

View File

@ -1,55 +1,94 @@
use std::error::Error;
use axum::{
routing::get,
Router,
response::Json,
response::Html,
};
use axum::http::header;
use axum::routing::post;
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, Value};
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());
async fn generate_license(Json(payload): Json<License>) -> Json<Value> {
dbg!(payload);
Json(json!({ "data": 42 }))
// 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()))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let app = Router::new()
.route("/", get(|| async { Html(INDEX_HTML) }))
.route("/images/icons.svg", get(|| async { ([(header::CONTENT_TYPE, "image/svg+xml")], ICONS) }))
.route("/generateLicense", post(generate_license));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;
return Ok(());
#[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")]
#[serde(default = "default_licensee_name",rename = "licenseeName")]
licensee_name: String,
#[serde(default = "default_empty_str")]
#[serde(default = "default_empty_str",rename = "assigneeName")]
assignee_name: String,
#[serde(default = "default_empty_str")]
#[serde(default = "default_empty_str",rename = "assigneeEmail")]
assignee_email: String,
#[serde(default = "default_empty_str")]
#[serde(default = "default_empty_str",rename = "licenseRestriction")]
license_restriction: String,
#[serde(default = "default_false")]
#[serde(default = "default_false",rename = "checkConcurrentUse")]
check_concurrent_use: bool,
products: Vec<Product>,
@ -60,22 +99,22 @@ pub struct License {
#[serde(default = "default_hash")]
hash: String,
#[serde(default = "default_grace_period_days")]
#[serde(default = "default_grace_period_days",rename = "gracePeriodDays")]
grace_period_days: i32,
#[serde(default = "default_true")]
#[serde(default = "default_true",rename = "autoProlongated")]
auto_prolongated: bool,
#[serde(default = "default_true")]
#[serde(default = "default_true",rename = "isAutoProlongated")]
is_auto_prolongated: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Product {
code: String,
#[serde(default = "default_expire_date")]
#[serde(default = "default_expire_date",rename = "fallbackDate")]
fallback_date: String,
#[serde(default = "default_expire_date")]
#[serde(default = "default_expire_date",rename = "paidUpTo")]
paid_up_to: String,
#[serde(default = "default_true")]
extended: bool,
@ -85,6 +124,7 @@ pub struct Product {
fn default_licensee_name() -> String {
String::from("for test only")
}
fn default_empty_str() -> String {
String::from("")
}
@ -110,5 +150,5 @@ fn default_grace_period_days() -> i32 {
}
fn default_expire_date() -> String {
"2023-12-31".into()
"2030-12-31".into()
}