139 lines
4.3 KiB
Rust
139 lines
4.3 KiB
Rust
use std::fs::{self, File};
|
||
use std::io::{BufWriter, Write};
|
||
use std::path::PathBuf;
|
||
use std::{io, path::Path};
|
||
|
||
use openssl::bn::BigNumContext;
|
||
use openssl::hash::MessageDigest;
|
||
use openssl::x509::{X509NameBuilder, X509};
|
||
use openssl::{
|
||
asn1::{Asn1Integer, Asn1Time},
|
||
bn::BigNum,
|
||
pkey::PKey,
|
||
rsa::Rsa,
|
||
x509::X509Builder,
|
||
};
|
||
|
||
mod root_certificate;
|
||
|
||
/// 生成证书,公钥保存为.pem文件,私钥保存为.key文件
|
||
///
|
||
/// - `storage_path`:证书存储路径
|
||
/// - `certificate_name`:证书名称
|
||
/// - `key_length`:密钥长度
|
||
/// - `available_days`:证书有效天数
|
||
/// - `version`:证书版本
|
||
/// - `serial_number`:证书序列号,如果不设定则默认为1
|
||
pub fn generate_certificate(
|
||
storage_path: &str,
|
||
certificate_name: &str,
|
||
key_length: u32,
|
||
available_days: u32,
|
||
version: i32,
|
||
serial_number: Option<u32>,
|
||
) -> anyhow::Result<()> {
|
||
let rsa = Rsa::generate(key_length)?;
|
||
let private_key = rsa.private_key_to_pem()?;
|
||
let pkey = PKey::from_rsa(rsa)?;
|
||
let mut builder = X509Builder::new()?;
|
||
builder.set_pubkey(&pkey)?;
|
||
|
||
let mut x509_name = X509NameBuilder::new()?;
|
||
x509_name.append_entry_by_text("CN", certificate_name)?;
|
||
x509_name.append_entry_by_text("C", "CN")?;
|
||
x509_name.append_entry_by_text("ST", "Hebei")?;
|
||
x509_name.append_entry_by_text("L", "Shi Jiazhuang")?;
|
||
x509_name.append_entry_by_text("O", "archgrid.xyz")?;
|
||
let x509_name = x509_name.build();
|
||
builder.set_subject_name(&x509_name)?;
|
||
builder.set_issuer_name(&x509_name)?;
|
||
builder.set_version(version)?;
|
||
|
||
let not_before = Asn1Time::days_from_now(0)?;
|
||
let not_after = Asn1Time::days_from_now(available_days)?;
|
||
builder.set_not_before(¬_before)?;
|
||
builder.set_not_after(¬_after)?;
|
||
let serial_number = BigNum::from_u32(serial_number.unwrap_or(1))?;
|
||
let sn = Asn1Integer::from_bn(&serial_number)?;
|
||
builder.set_serial_number(&sn)?;
|
||
|
||
builder.sign(&pkey, MessageDigest::sha256())?;
|
||
|
||
let certificate = builder.build();
|
||
|
||
let store_path = PathBuf::from(storage_path);
|
||
ensure_path_exists(&store_path).unwrap();
|
||
|
||
let cert_path = store_path.clone().join(format!("{}.pem", certificate_name));
|
||
let cert_file = File::create(cert_path)?;
|
||
let mut writer = BufWriter::new(cert_file);
|
||
let cert_data = certificate.to_pem()?;
|
||
writer.write_all(&cert_data)?;
|
||
writer.flush()?;
|
||
|
||
let private_key_path = store_path.join(format!("{}.key", certificate_name));
|
||
let private_key_file = File::create(private_key_path)?;
|
||
let mut writer = BufWriter::new(private_key_file);
|
||
writer.write_all(&private_key)?;
|
||
writer.flush()?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 确保指定路径存在,如果不存在则创建
|
||
///
|
||
/// - `target_path`:目标路径
|
||
fn ensure_path_exists<P: AsRef<Path>>(target_path: P) -> Result<(), io::Error> {
|
||
let path = target_path.as_ref();
|
||
if !path.exists() {
|
||
fs::create_dir_all(path)?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 计算用于net-filter中power插件中Equal所使用的值。
|
||
///
|
||
/// - `cert`:用于生成EQUAL值的X509证书
|
||
pub fn calculate_power_euqal_result(cert: X509) -> anyhow::Result<String> {
|
||
let x = BigNum::from_slice(cert.signature().as_slice())?;
|
||
let y = BigNum::from_u32(65537)?;
|
||
let root_cert_pub_key = root_certificate::x509_certificate()?.public_key()?.rsa()?;
|
||
let z = root_cert_pub_key.n();
|
||
let cert_pub_key = cert.public_key()?.rsa()?;
|
||
let n = cert_pub_key.n();
|
||
|
||
let mut ctx = BigNumContext::new()?;
|
||
let mut result = BigNum::new()?;
|
||
result.mod_exp(&x, &y, &n, &mut ctx)?;
|
||
Ok(format!(
|
||
"EQUAL,{},{},{}->{}",
|
||
x.to_dec_str()?,
|
||
y.to_dec_str()?,
|
||
z.to_dec_str()?,
|
||
result.to_dec_str()?
|
||
))
|
||
}
|
||
|
||
/// 从.pem文件中读取X509证书
|
||
///
|
||
/// - `file_path`:证书文件路径
|
||
pub fn load_certificate<P: AsRef<Path>>(file_path: P) -> anyhow::Result<X509> {
|
||
let file_path = file_path.as_ref();
|
||
let cert_pem = fs::read(file_path)?;
|
||
let cert = X509::from_pem(&cert_pem)?;
|
||
Ok(cert)
|
||
}
|
||
|
||
/// 从.key文件中读取RSA私钥
|
||
///
|
||
/// - `file_path`:私钥文件路径
|
||
pub fn load_private_key<R: AsRef<Path>>(
|
||
file_path: R,
|
||
) -> anyhow::Result<PKey<openssl::pkey::Private>> {
|
||
let file_path = file_path.as_ref();
|
||
let key_pem = fs::read(file_path)?;
|
||
let key = PKey::private_key_from_pem(&key_pem)?;
|
||
Ok(key)
|
||
}
|