update ssl utils

This commit is contained in:
DecDuck
2024-10-08 21:18:30 +11:00
parent 98f8801f57
commit 946ec23606
3 changed files with 201 additions and 63 deletions

View File

@ -15,19 +15,8 @@ rayon = "1.10.0"
serde = "1.0.210" serde = "1.0.210"
ciborium = "0.2.2" ciborium = "0.2.2"
time = "0.3.36" time = "0.3.36"
openssl = "0.10.66"
[dependencies.x509-verify] hex = "0.4.3"
version = "0.4.6"
features = [
"pem",
"ecdsa"
]
[dependencies.rcgen]
version = "0.13.1"
features = [
"x509-parser"
]
[dependencies.uuid] [dependencies.uuid]
version = "1.10.0" version = "1.10.0"

View File

@ -1,6 +1,6 @@
{ {
"name": "@drop/droplet", "name": "@drop/droplet",
"version": "0.2.0", "version": "0.3.0",
"main": "index.js", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
"napi": { "napi": {

View File

@ -1,37 +1,80 @@
use napi::Error; use napi::Error;
use rcgen::{ use openssl::{
date_time_ymd, generate_simple_self_signed, BasicConstraints, Certificate, CertificateParams, asn1::Asn1Time,
DistinguishedName, DnType, Ia5String, IsCa, KeyIdMethod, KeyPair, KeyUsagePurpose, SanType, bn::{BigNum, MsbOption},
PKCS_ECDSA_P384_SHA384, ec::{EcGroup, EcKey},
hash::MessageDigest,
nid::Nid,
pkey::PKey,
sign::{Signer, Verifier},
ssl::{SslConnector, SslContext, SslMethod},
stack::Stack,
x509::{
extension::{AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectKeyIdentifier},
store::X509StoreBuilder,
X509Builder, X509NameBuilder, X509ReqBuilder, X509StoreContext, X509,
},
}; };
use time::{Duration, OffsetDateTime};
use x509_verify::der::DecodePem;
const YEAR: i64 = 60 * 60 * 24 * 365;
#[napi] #[napi]
pub fn generate_root_ca() -> Result<Vec<String>, Error> { pub fn generate_root_ca() -> Result<Vec<String>, Error> {
let mut params = CertificateParams::new(Vec::new()).unwrap(); let nid = Nid::X9_62_PRIME256V1;
let group = EcGroup::from_curve_name(nid).unwrap();
let private_key = EcKey::generate(&group).unwrap();
let mut name = DistinguishedName::new(); let mut x509_builder = X509Builder::new().unwrap();
name.push(DnType::CommonName, "Drop Root CA"); x509_builder.set_version(2).unwrap();
name.push(DnType::OrganizationName, "Drop");
params.distinguished_name = name; let serial_number = {
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); let mut serial = BigNum::new().unwrap();
params.key_usages = vec![ serial.rand(159, MsbOption::MAYBE_ZERO, false).unwrap();
KeyUsagePurpose::DigitalSignature, serial.to_asn1_integer().unwrap()
KeyUsagePurpose::KeyCertSign, };
]; x509_builder.set_serial_number(&serial_number).unwrap();
let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap(); let mut x509_name = X509NameBuilder::new().unwrap();
x509_name
.append_entry_by_nid(Nid::COMMONNAME, "Drop Root Server")
.unwrap();
x509_name
.append_entry_by_nid(Nid::ORGANIZATIONNAME, "Drop")
.unwrap();
let x509_name_built = x509_name.build();
x509_builder.set_subject_name(&x509_name_built).unwrap();
x509_builder.set_issuer_name(&x509_name_built).unwrap();
let root_ca = CertificateParams::self_signed(params, &key_pair).unwrap(); let not_before = Asn1Time::days_from_now(0).unwrap();
x509_builder.set_not_before(&not_before).unwrap();
let not_after = Asn1Time::days_from_now(365 * 1000).unwrap();
x509_builder.set_not_after(&not_after).unwrap();
x509_builder
.append_extension(BasicConstraints::new().critical().ca().build().unwrap())
.unwrap();
x509_builder
.append_extension(
KeyUsage::new()
.critical()
.key_cert_sign()
.crl_sign()
.digital_signature()
.build()
.unwrap(),
)
.unwrap();
let key_pair = PKey::from_ec_key(private_key).unwrap();
x509_builder.set_pubkey(&key_pair).unwrap();
x509_builder
.sign(&key_pair, MessageDigest::sha256())
.unwrap();
let x509 = x509_builder.build();
return Ok(vec![ return Ok(vec![
key_pair.serialize_pem(), String::from_utf8(x509.to_pem().unwrap()).unwrap(),
key_pair.public_key_pem(), String::from_utf8(key_pair.private_key_to_pem_pkcs8().unwrap()).unwrap(),
root_ca.pem(),
]); ]);
} }
@ -42,43 +85,149 @@ pub fn generate_client_certificate(
root_ca: String, root_ca: String,
root_ca_private: String, root_ca_private: String,
) -> Result<Vec<String>, Error> { ) -> Result<Vec<String>, Error> {
let root_key_pair = KeyPair::from_pem(&root_ca_private).unwrap(); let root_ca_cert = X509::from_pem(root_ca.as_bytes()).unwrap();
let root_ca_params = CertificateParams::from_ca_cert_pem(&root_ca).unwrap(); let root_ca_key = EcKey::private_key_from_pem(root_ca_private.as_bytes()).unwrap();
let root_ca = root_ca_params.self_signed(&root_key_pair).unwrap(); let root_ca_key_pair = PKey::from_ec_key(root_ca_key).unwrap();
let mut params = CertificateParams::new(Vec::new()).unwrap(); let nid = Nid::X9_62_PRIME256V1;
let group = EcGroup::from_curve_name(nid).unwrap();
let private_key = EcKey::generate(&group).unwrap();
let key_pair = PKey::from_ec_key(private_key).unwrap();
let mut name = DistinguishedName::new(); /* Generate req and sign it */
name.push(DnType::CommonName, client_id); let mut req_builder = X509ReqBuilder::new().unwrap();
name.push(DnType::OrganizationName, "Drop"); req_builder.set_pubkey(&key_pair).unwrap();
params.distinguished_name = name; let mut x509_name = X509NameBuilder::new().unwrap();
params.subject_alt_names = vec![SanType::DnsName(Ia5String::try_from(client_name).unwrap())]; x509_name
params.key_usages = vec![ .append_entry_by_nid(Nid::COMMONNAME, &client_id)
KeyUsagePurpose::DigitalSignature, .unwrap();
KeyUsagePurpose::KeyCertSign, x509_name
]; .append_entry_by_nid(Nid::SUBJECT_ALT_NAME, &client_name)
.unwrap();
x509_name
.append_entry_by_nid(Nid::ORGANIZATIONNAME, "Drop")
.unwrap();
let x509_name_built = x509_name.build();
let client_key_pair = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap(); req_builder.set_subject_name(&x509_name_built).unwrap();
let client_certificate = params req_builder
.signed_by(&client_key_pair, &root_ca, &root_key_pair) .sign(&key_pair, MessageDigest::sha256())
.unwrap();
let req = req_builder.build();
/* Generate certificate from req and sign it using CA */
let mut x509_builder = X509Builder::new().unwrap();
x509_builder.set_version(2).unwrap();
x509_builder.set_pubkey(&key_pair).unwrap();
let serial_number = {
let mut serial = BigNum::new().unwrap();
serial.rand(159, MsbOption::MAYBE_ZERO, false).unwrap();
serial.to_asn1_integer().unwrap()
};
x509_builder.set_serial_number(&serial_number).unwrap();
x509_builder.set_subject_name(req.subject_name()).unwrap();
x509_builder
.set_issuer_name(root_ca_cert.issuer_name())
.unwrap(); .unwrap();
let not_before = Asn1Time::days_from_now(0).unwrap();
x509_builder.set_not_before(&not_before).unwrap();
let not_after = Asn1Time::days_from_now(365 * 100).unwrap();
x509_builder.set_not_after(&not_after).unwrap();
x509_builder
.append_extension(BasicConstraints::new().build().unwrap())
.unwrap();
x509_builder
.append_extension(
KeyUsage::new()
.critical()
.non_repudiation()
.digital_signature()
.data_encipherment()
.build()
.unwrap(),
)
.unwrap();
let subject_key_identifier = SubjectKeyIdentifier::new()
.build(&x509_builder.x509v3_context(Some(&root_ca_cert), None))
.unwrap();
x509_builder
.append_extension(subject_key_identifier)
.unwrap();
let auth_key_identifier = AuthorityKeyIdentifier::new()
.keyid(false)
.issuer(false)
.build(&x509_builder.x509v3_context(Some(&root_ca_cert), None))
.unwrap();
x509_builder.append_extension(auth_key_identifier).unwrap();
x509_builder
.sign(&root_ca_key_pair, MessageDigest::sha256())
.unwrap();
let x509 = x509_builder.build();
return Ok(vec![ return Ok(vec![
root_key_pair.serialize_pem(), String::from_utf8(x509.to_pem().unwrap()).unwrap(),
root_key_pair.public_key_pem(), String::from_utf8(key_pair.private_key_to_pem_pkcs8().unwrap()).unwrap(),
client_certificate.pem(),
]); ]);
} }
#[napi] #[napi]
pub fn verify_client_certificate(client_cert: String, root_ca: String) -> Result<bool, Error> { pub fn verify_client_certificate(client_cert: String, root_ca: String) -> Result<bool, Error> {
let ca = x509_verify::x509_cert::Certificate::from_pem(root_ca).unwrap(); let root_ca_cert = X509::from_pem(root_ca.as_bytes()).unwrap();
let client = x509_verify::x509_cert::Certificate::from_pem(client_cert).unwrap();
let key = x509_verify::VerifyingKey::try_from(&ca).unwrap(); let mut store_builder = X509StoreBuilder::new().unwrap();
return Ok(match key.verify(&client) { store_builder.add_cert(root_ca_cert).unwrap();
Ok(_) => true, let store = store_builder.build();
Err(_) => false,
}); let client_cert: X509 = X509::from_pem(client_cert.as_bytes()).unwrap();
let chain = Stack::new().unwrap();
let mut store_ctx = X509StoreContext::new().unwrap();
let result = store_ctx
.init(&store, &client_cert, &chain, |c| c.verify_cert())
.unwrap();
return Ok(result);
}
#[napi]
pub fn sign_nonce(private_key: String, nonce: String) -> Result<String, Error> {
let client_private_key = EcKey::private_key_from_pem(private_key.as_bytes()).unwrap();
let pkey_private_key = PKey::from_ec_key(client_private_key).unwrap();
let mut signer = Signer::new(MessageDigest::sha256(), &pkey_private_key).unwrap();
signer.update(nonce.as_bytes()).unwrap();
let signature = signer.sign_to_vec().unwrap();
let hex_signature = hex::encode(signature);
return Ok(hex_signature);
}
#[napi]
pub fn verify_nonce(public_cert: String, nonce: String, signature: String) -> Result<bool, Error> {
let client_public_cert = X509::from_pem(public_cert.as_bytes()).unwrap();
let client_public_key = client_public_cert.public_key().unwrap();
let signature = hex::decode(signature).unwrap();
let mut verifier = Verifier::new(
MessageDigest::sha256(),
&client_public_key,
)
.unwrap();
verifier.update(nonce.as_bytes()).unwrap();
let result = verifier.verify(&signature).unwrap();
return Ok(result);
} }