From 946ec2360694b9e83c907140c6ff378feff87b32 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Tue, 8 Oct 2024 21:18:30 +1100 Subject: [PATCH] update ssl utils --- Cargo.toml | 15 +--- package.json | 2 +- src/ssl.rs | 247 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 201 insertions(+), 63 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2976f54..83f68a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,19 +15,8 @@ rayon = "1.10.0" serde = "1.0.210" ciborium = "0.2.2" time = "0.3.36" - -[dependencies.x509-verify] -version = "0.4.6" -features = [ - "pem", - "ecdsa" -] - -[dependencies.rcgen] -version = "0.13.1" -features = [ - "x509-parser" -] +openssl = "0.10.66" +hex = "0.4.3" [dependencies.uuid] version = "1.10.0" diff --git a/package.json b/package.json index 278ff8a..8341d8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@drop/droplet", - "version": "0.2.0", + "version": "0.3.0", "main": "index.js", "types": "index.d.ts", "napi": { diff --git a/src/ssl.rs b/src/ssl.rs index e18291d..d50a99a 100644 --- a/src/ssl.rs +++ b/src/ssl.rs @@ -1,37 +1,80 @@ use napi::Error; -use rcgen::{ - date_time_ymd, generate_simple_self_signed, BasicConstraints, Certificate, CertificateParams, - DistinguishedName, DnType, Ia5String, IsCa, KeyIdMethod, KeyPair, KeyUsagePurpose, SanType, - PKCS_ECDSA_P384_SHA384, +use openssl::{ + asn1::Asn1Time, + bn::{BigNum, MsbOption}, + 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] pub fn generate_root_ca() -> Result, 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(); - name.push(DnType::CommonName, "Drop Root CA"); - name.push(DnType::OrganizationName, "Drop"); + let mut x509_builder = X509Builder::new().unwrap(); + x509_builder.set_version(2).unwrap(); - params.distinguished_name = name; - params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - params.key_usages = vec![ - KeyUsagePurpose::DigitalSignature, - KeyUsagePurpose::KeyCertSign, - ]; + 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(); - 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(¬_before).unwrap(); + let not_after = Asn1Time::days_from_now(365 * 1000).unwrap(); + x509_builder.set_not_after(¬_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![ - key_pair.serialize_pem(), - key_pair.public_key_pem(), - root_ca.pem(), + String::from_utf8(x509.to_pem().unwrap()).unwrap(), + String::from_utf8(key_pair.private_key_to_pem_pkcs8().unwrap()).unwrap(), ]); } @@ -42,43 +85,149 @@ pub fn generate_client_certificate( root_ca: String, root_ca_private: String, ) -> Result, Error> { - let root_key_pair = KeyPair::from_pem(&root_ca_private).unwrap(); - let root_ca_params = CertificateParams::from_ca_cert_pem(&root_ca).unwrap(); - let root_ca = root_ca_params.self_signed(&root_key_pair).unwrap(); + let root_ca_cert = X509::from_pem(root_ca.as_bytes()).unwrap(); + let root_ca_key = EcKey::private_key_from_pem(root_ca_private.as_bytes()).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(); - name.push(DnType::CommonName, client_id); - name.push(DnType::OrganizationName, "Drop"); + /* Generate req and sign it */ + let mut req_builder = X509ReqBuilder::new().unwrap(); + req_builder.set_pubkey(&key_pair).unwrap(); - params.distinguished_name = name; - params.subject_alt_names = vec![SanType::DnsName(Ia5String::try_from(client_name).unwrap())]; - params.key_usages = vec![ - KeyUsagePurpose::DigitalSignature, - KeyUsagePurpose::KeyCertSign, - ]; + let mut x509_name = X509NameBuilder::new().unwrap(); + x509_name + .append_entry_by_nid(Nid::COMMONNAME, &client_id) + .unwrap(); + 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(); - let client_certificate = params - .signed_by(&client_key_pair, &root_ca, &root_key_pair) + req_builder.set_subject_name(&x509_name_built).unwrap(); + req_builder + .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(); + let not_before = Asn1Time::days_from_now(0).unwrap(); + x509_builder.set_not_before(¬_before).unwrap(); + let not_after = Asn1Time::days_from_now(365 * 100).unwrap(); + x509_builder.set_not_after(¬_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![ - root_key_pair.serialize_pem(), - root_key_pair.public_key_pem(), - client_certificate.pem(), + String::from_utf8(x509.to_pem().unwrap()).unwrap(), + String::from_utf8(key_pair.private_key_to_pem_pkcs8().unwrap()).unwrap(), ]); } #[napi] pub fn verify_client_certificate(client_cert: String, root_ca: String) -> Result { - let ca = x509_verify::x509_cert::Certificate::from_pem(root_ca).unwrap(); - let client = x509_verify::x509_cert::Certificate::from_pem(client_cert).unwrap(); + let root_ca_cert = X509::from_pem(root_ca.as_bytes()).unwrap(); - let key = x509_verify::VerifyingKey::try_from(&ca).unwrap(); - return Ok(match key.verify(&client) { - Ok(_) => true, - Err(_) => false, - }); + let mut store_builder = X509StoreBuilder::new().unwrap(); + store_builder.add_cert(root_ca_cert).unwrap(); + let store = store_builder.build(); + + 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 { + 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 { + 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); }