| Crates.io | cutil |
| lib.rs | cutil |
| version | 0.1.0 |
| created_at | 2025-11-24 13:34:04.841141+00 |
| updated_at | 2025-11-24 13:34:04.841141+00 |
| description | A complete internal PKI toolkit for Rust |
| homepage | https://github.com/kunalsinghdadhwal/cutil |
| repository | https://github.com/kunalsinghdadhwal/cutil |
| max_upload_size | |
| id | 1947827 |
| size | 133,953 |
cutil is designed for developers who need to manage their own internal PKI infrastructure. It provides both a robust Rust library and a command-line interface for certificate operations, making it suitable for development, testing, and production environments.
Certificate Authority Management
Certificate Issuance
Certificate Revocation
Remote Certificate Inspection
Export Formats
Add CUtil to your Cargo.toml:
[dependencies]
cutil = "0.1"
use cutil::ca::CertificateAuthority;
use cutil::cert::CertificateBuilder;
use cutil::types::{CertSigAlgo, DistinguishedName};
use cutil::error::Result;
fn main() -> Result<()> {
// Create a root CA
let subject = DistinguishedName::new("Example Root CA")
.with_organization("Example Corp")
.with_country("US");
let ca = CertificateAuthority::new_root(
subject,
CertSigAlgo::EcdsaP256,
3650, // 10 years
)?;
ca.save_pem("ca.pem", "ca-key.pem")?;
// Issue a server certificate
let mut loaded_ca = CertificateAuthority::load_pem(
"ca.pem",
"ca-key.pem",
CertSigAlgo::EcdsaP256,
)?;
let cert = CertificateBuilder::server("example.com")
.with_dns_san("www.example.com")
.with_dns_san("api.example.com")
.with_validity_days(365)
.issue(&mut loaded_ca)?;
cert.save_pem("server.pem", "server-key.pem")?;
Ok(())
}
# Initialize a root CA
cutil init --cn "My Root CA" --org "My Company" --country US
# Issue a server certificate
cutil cert --cn example.com --dns example.com,www.example.com --validity 365
# Fetch remote certificate chain
cutil fetch google.com:443 --format pretty
use cutil::ca::CertificateAuthority;
use cutil::types::{CertSigAlgo, DistinguishedName};
use cutil::error::Result;
fn create_root_ca() -> Result<CertificateAuthority> {
let subject = DistinguishedName::new("Example Root CA")
.with_organization("Example Corporation")
.with_organizational_unit("Security")
.with_country("US")
.with_state("California")
.with_locality("San Francisco");
let ca = CertificateAuthority::new_root(
subject,
CertSigAlgo::EcdsaP256,
3650, // Validity in days
)?;
ca.save_pem("root-ca.pem", "root-ca-key.pem")?;
Ok(ca)
}
use cutil::ca::CertificateAuthority;
use cutil::types::{CertSigAlgo, DistinguishedName};
use cutil::error::Result;
fn create_intermediate_ca() -> Result<CertificateAuthority> {
// Load the parent (root) CA
let parent_ca = CertificateAuthority::load_pem(
"root-ca.pem",
"root-ca-key.pem",
CertSigAlgo::EcdsaP256,
)?;
// Create intermediate CA subject
let subject = DistinguishedName::new("Example Intermediate CA")
.with_organization("Example Corporation")
.with_organizational_unit("Security");
// Create the intermediate CA
let intermediate_ca = CertificateAuthority::new_intermediate(
subject,
CertSigAlgo::EcdsaP256,
1825, // 5 years
&parent_ca,
)?;
intermediate_ca.save_pem("intermediate-ca.pem", "intermediate-ca-key.pem")?;
Ok(intermediate_ca)
}
use cutil::ca::CertificateAuthority;
use cutil::types::CertSigAlgo;
use cutil::error::Result;
fn load_ca() -> Result<CertificateAuthority> {
let ca = CertificateAuthority::load_pem(
"ca.pem",
"ca-key.pem",
CertSigAlgo::EcdsaP256,
)?;
Ok(ca)
}
use cutil::cert::CertificateBuilder;
use cutil::types::{CertSigAlgo, DistinguishedName};
use cutil::ca::CertificateAuthority;
use cutil::error::Result;
fn issue_server_certificate(ca: &mut CertificateAuthority) -> Result<()> {
let cert = CertificateBuilder::server("example.com")
.with_dns_san("example.com")
.with_dns_san("www.example.com")
.with_dns_san("api.example.com")
.with_dns_san("*.internal.example.com")
.with_validity_days(365)
.with_algorithm(CertSigAlgo::EcdsaP256)
.issue(ca)?;
cert.save_pem("server.pem", "server-key.pem")?;
Ok(())
}
use cutil::cert::CertificateBuilder;
use cutil::types::{CertType, DistinguishedName};
use cutil::ca::CertificateAuthority;
use cutil::error::Result;
fn issue_server_with_custom_subject(ca: &mut CertificateAuthority) -> Result<()> {
let subject = DistinguishedName::new("example.com")
.with_organization("Example Corporation")
.with_organizational_unit("Web Services")
.with_locality("San Francisco")
.with_state("California")
.with_country("US");
let cert = CertificateBuilder::new("example.com".to_string(), CertType::Server)
.with_subject(subject)
.with_dns_san("example.com")
.with_dns_san("www.example.com")
.with_validity_days(365)
.issue(ca)?;
cert.save_pem("server.pem", "server-key.pem")?;
Ok(())
}
use cutil::cert::CertificateBuilder;
use cutil::ca::CertificateAuthority;
use cutil::error::Result;
fn issue_client_certificate(ca: &mut CertificateAuthority) -> Result<()> {
let cert = CertificateBuilder::client("user@example.com")
.with_email_san("user@example.com")
.with_validity_days(365)
.issue(ca)?;
cert.save_pem("client.pem", "client-key.pem")?;
Ok(())
}
use cutil::cert::CertificateBuilder;
use cutil::ca::CertificateAuthority;
use cutil::error::Result;
use std::net::IpAddr;
fn issue_certificate_with_ip_sans(ca: &mut CertificateAuthority) -> Result<()> {
let cert = CertificateBuilder::server("internal-service")
.with_dns_san("internal-service.local")
.with_ip_san("192.168.1.100".parse::<IpAddr>().unwrap())
.with_ip_san("10.0.0.50".parse::<IpAddr>().unwrap())
.with_validity_days(365)
.issue(ca)?;
cert.save_pem("internal-service.pem", "internal-service-key.pem")?;
Ok(())
}
use cutil::cert::CertificateBuilder;
use cutil::ca::CertificateAuthority;
use cutil::error::Result;
fn issue_certificate_with_revocation_urls(ca: &mut CertificateAuthority) -> Result<()> {
let cert = CertificateBuilder::server("example.com")
.with_dns_san("example.com")
.with_crl_distribution_point("http://crl.example.com/ca.crl")
.with_ocsp_server("http://ocsp.example.com")
.with_validity_days(365)
.issue(ca)?;
cert.save_pem("server.pem", "server-key.pem")?;
Ok(())
}
use cutil::ca::{CertificateAuthority, IssuedCertificate};
use cutil::error::Result;
fn export_certificate_chain(cert: &IssuedCertificate) -> Result<()> {
// Save full certificate chain (cert + CA chain)
cert.save_chain("fullchain.pem")?;
Ok(())
}
use cutil::ca::IssuedCertificate;
use cutil::error::Result;
fn export_pkcs12(cert: &IssuedCertificate) -> Result<()> {
let p12_data = cert.export_pkcs12(
"secure_password_123",
"My Server Certificate",
)?;
std::fs::write("certificate.p12", p12_data)?;
Ok(())
}
use cutil::ca::CertificateAuthority;
use cutil::types::RevocationReason;
use cutil::error::Result;
fn revoke_certificate(ca: &mut CertificateAuthority, serial: Vec<u8>) -> Result<()> {
ca.revoke_certificate(serial, RevocationReason::KeyCompromise)?;
Ok(())
}
use cutil::ca::CertificateAuthority;
use cutil::error::Result;
fn generate_crl(ca: &CertificateAuthority) -> Result<()> {
let crl_pem = ca.generate_crl()?;
std::fs::write("ca.crl", crl_pem)?;
println!("CRL generated with {} revoked certificates",
ca.revoked_certificates().len());
Ok(())
}
use cutil::ca::CertificateAuthority;
use cutil::error::Result;
fn list_revoked_certificates(ca: &CertificateAuthority) -> Result<()> {
for revoked in ca.revoked_certificates() {
println!("Serial: {:?}", revoked.serial_number);
println!("Revocation Date: {}", revoked.revocation_date);
println!("Reason: {:?}", revoked.reason);
println!("---");
}
Ok(())
}
use cutil::fetch::fetch_certificate_chain;
use cutil::error::Result;
fn fetch_remote_chain() -> Result<()> {
let chain = fetch_certificate_chain("google.com", 443)?;
println!("Server: {}", chain.server);
println!("Number of certificates: {}", chain.certificates.len());
for (i, cert) in chain.certificates.iter().enumerate() {
println!("\nCertificate {}:", i);
println!(" Subject: {}", cert.subject);
println!(" Issuer: {}", cert.issuer);
println!(" Valid from: {}", cert.not_before);
println!(" Valid until: {}", cert.not_after);
println!(" Is CA: {}", cert.is_ca);
}
Ok(())
}
use cutil::fetch::{fetch_certificate_chain, display_certificate_chain, OutputFormat};
use cutil::error::Result;
fn display_remote_chain_pretty() -> Result<()> {
let chain = fetch_certificate_chain("github.com", 443)?;
let output = display_certificate_chain(&chain, OutputFormat::Pretty)?;
println!("{}", output);
Ok(())
}
fn display_remote_chain_json() -> Result<()> {
let chain = fetch_certificate_chain("github.com", 443)?;
let output = display_certificate_chain(&chain, OutputFormat::Json)?;
println!("{}", output);
Ok(())
}
use cutil::fetch::{fetch_certificate_chain, display_certificate_chain, OutputFormat};
use cutil::error::Result;
fn save_chain_info(host: &str, port: u16, output_file: &str) -> Result<()> {
let chain = fetch_certificate_chain(host, port)?;
let output = display_certificate_chain(&chain, OutputFormat::Json)?;
std::fs::write(output_file, output)?;
Ok(())
}
CUtil supports multiple signature algorithms with varying security levels and performance characteristics:
use cutil::types::CertSigAlgo;
let algorithm = CertSigAlgo::Ed25519;
use cutil::types::CertSigAlgo;
// P-256 (secp256r1) - Good balance of security and performance
let p256 = CertSigAlgo::EcdsaP256;
// P-384 (secp384r1) - Higher security level
let p384 = CertSigAlgo::EcdsaP384;
use cutil::types::CertSigAlgo;
let rsa2048 = CertSigAlgo::Rsa2048; // Minimum recommended
let rsa3072 = CertSigAlgo::Rsa3072; // Good for long-term security
let rsa4096 = CertSigAlgo::Rsa4096; // Maximum security
use cutil::types::DistinguishedName;
let dn = DistinguishedName::new("example.com")
.with_organization("Example Corporation")
.with_organizational_unit("IT Department")
.with_country("US")
.with_state("California")
.with_locality("San Francisco");
use cutil::types::RevocationReason;
let reasons = [
RevocationReason::Unspecified,
RevocationReason::KeyCompromise,
RevocationReason::CACompromise,
RevocationReason::AffiliationChanged,
RevocationReason::Superseded,
RevocationReason::CessationOfOperation,
];
CUtil uses a custom Result type for comprehensive error handling:
use cutil::error::{Result, Error};
fn handle_errors() -> Result<()> {
match some_operation() {
Ok(value) => {
println!("Success: {:?}", value);
Ok(())
}
Err(Error::IoError(e)) => {
eprintln!("I/O error: {}", e);
Err(Error::IoError(e))
}
Err(Error::CertificateGeneration(msg)) => {
eprintln!("Certificate generation failed: {}", msg);
Err(Error::CertificateGeneration(msg))
}
Err(e) => {
eprintln!("Other error: {}", e);
Err(e)
}
}
}
The CUtil CLI provides a complete command-line interface for all PKI operations.
cutil init \
--cn "Example Root CA" \
--org "Example Corporation" \
--ou "Security" \
--country US \
--state California \
--locality "San Francisco" \
--algorithm ecdsa-p256 \
--validity 3650 \
--cert-out root-ca.pem \
--key-out root-ca-key.pem
cutil init \
--cn "Example Intermediate CA" \
--org "Example Corporation" \
--algorithm ecdsa-p256 \
--validity 1825 \
--intermediate \
--parent-cert root-ca.pem \
--parent-key root-ca-key.pem \
--cert-out intermediate-ca.pem \
--key-out intermediate-ca-key.pem
cutil cert \
--cn example.com \
--cert-type server \
--dns example.com \
--dns www.example.com \
--dns api.example.com \
--org "Example Corporation" \
--validity 365 \
--ca-cert ca.pem \
--ca-key ca-key.pem \
--cert-out server.pem \
--key-out server-key.pem \
--chain-out fullchain.pem
cutil cert \
--cn "John Doe" \
--cert-type client \
--email john.doe@example.com \
--org "Example Corporation" \
--validity 365 \
--ca-cert ca.pem \
--ca-key ca-key.pem \
--cert-out client.pem \
--key-out client-key.pem
cutil cert \
--cn example.com \
--cert-type server \
--dns example.com \
--ca-cert ca.pem \
--ca-key ca-key.pem \
--cert-out server.pem \
--key-out server-key.pem \
--p12-out server.p12 \
--p12-password "secure_password"
# Pretty formatted output
cutil fetch google.com:443
# JSON output
cutil fetch github.com:443 --format json
# Save to file
cutil fetch example.com:443 --format json --output example-chain.json
cutil revoke \
--serial 01:02:03:04:05:06:07:08 \
--reason key-compromise \
--ca-cert ca.pem \
--ca-key ca-key.pem
Available revocation reasons:
unspecifiedkey-compromise or keycompromiseca-compromise or cacompromiseaffiliation-changed or affiliationchangedsupersededcessation or cessationofoperationcutil crl \
--ca-cert ca.pem \
--ca-key ca-key.pem \
--output ca.crl
cutil chain server.pem
use cutil::ca::CertificateAuthority;
use cutil::cert::CertificateBuilder;
use cutil::types::{CertSigAlgo, DistinguishedName};
use cutil::error::Result;
fn create_test_pki() -> Result<()> {
// Create test CA
let ca_subject = DistinguishedName::new("Test CA");
let mut ca = CertificateAuthority::new_root(
ca_subject,
CertSigAlgo::EcdsaP256,
365,
)?;
// Issue test certificate
let cert = CertificateBuilder::server("localhost")
.with_dns_san("localhost")
.with_ip_san("127.0.0.1".parse().unwrap())
.with_validity_days(30)
.issue(&mut ca)?;
cert.save_pem("test-cert.pem", "test-key.pem")?;
Ok(())
}
Complete working examples are available in the repository:
Run examples with:
cargo run --example basic_ca
cargo run --example fetch_remote
CUtil is built on industry-standard cryptographic libraries: