keytool 0.1.0

A command-line tool for managing certificates, similar to Java keytool.
Documentation
use crate::commands::command::Cmd;
use crate::error::KeyToolError;
use crate::utils::fake_cert::generate_fake_cert;
use clap::Args;
use openssl::pkcs12::Pkcs12;
use openssl::pkey::PKey;
use std::fs;

/**
cargo run -- genseckey \
  --alias mysecret \
  --keyalg AES \
  --keysize 256 \
  --keystore myks.p12 \
  --storepass password
**/

/**
keytool -v -list -keystore myks.p12 -storepass password -storetype PKCS12
**/

#[derive(Args, Debug, Clone)]
pub struct GenSecKeyCmd {
    /// Alias name for the key
    #[arg(long)]
    pub alias: String,

    /// Key algorithm (AES, HmacSHA256, etc.)
    #[arg(long, default_value = "AES")]
    pub keyalg: String,

    /// Key size in bits (AES defaults to 256)
    #[arg(long)]
    pub keysize: Option<u32>,

    /// Output keystore file
    #[arg(long)]
    pub keystore: String,

    /// Store type (only PKCS12 supported)
    #[arg(long, default_value = "PKCS12")]
    pub storetype: String,

    /// Keystore password
    #[arg(long)]
    pub storepass: String,
}

impl Cmd for GenSecKeyCmd {
    fn run(&self) -> Result<(), KeyToolError> {
        if self.storetype.to_uppercase() != "PKCS12" {
            return Err(KeyToolError::new(
                "Only PKCS12 storetype is supported (like Java keytool)",
            ));
        }

        // Determine key size
        let keysize = self.keysize.unwrap_or_else(|| {
            match self.keyalg.to_uppercase().as_str() {
                "AES" => 256,
                "HMACSHA256" => 256,
                _ => 256, // default
            }
        });

        let key_bytes = keysize / 8;

        // Generate random symmetric key
        let mut key_data = vec![0u8; key_bytes as usize];
        openssl::rand::rand_bytes(&mut key_data)
            .map_err(|e| KeyToolError::new(format!("Failed to generate key: {}", e)))?;

        // Wrap in OpenSSL PKey (use raw HMAC key for symmetric key simulation)
        let pkey = PKey::hmac(&key_data)
            .map_err(|e| KeyToolError::new(format!("Failed to create PKey: {}", e)))?;

        // PKCS12 requires a certificate: so we generate a fake one (same as Java keytool)
        let (cert, pkey) = generate_fake_cert(&self.alias)?;

        // Build PKCS12
        let pkcs12 = Pkcs12::builder()
            .name(&self.alias)
            .pkey(&pkey) // RSA 私钥
            .cert(&cert) // 对应公钥证书
            .build2(&self.storepass)?;

        // Save to file
        fs::write(&self.keystore, pkcs12.to_der().unwrap())
            .map_err(|e| KeyToolError::new(format!("Failed to save keystore: {}", e)))?;

        // Java keytool output style
        println!("Generated secret key entry with alias {}", self.alias);
        println!("Keystore stored at: {}", self.keystore);
        println!("Key algorithm: {}", self.keyalg);
        println!("Key size: {} bits", keysize);
        println!("Storetype: PKCS12");

        Ok(())
    }
}