use std::path::PathBuf;
use base64::Engine;
use cosmian_kmip::{
kmip_0::kmip_types::BlockCipherMode,
kmip_2_1::{
kmip_objects::Object,
kmip_types::{CryptographicAlgorithm, CryptographicParameters},
},
};
use cosmian_kms_client::{ExportObjectParams, export_object};
use cosmian_logger::{info, log_init};
use openssl::x509::X509;
use test_kms_server::{
reexport::cosmian_kms_server::routes::google_cse::operations::PrivateKeySignRequest,
start_default_test_kms_server,
};
use crate::{
actions::kms::{
attributes::GetAttributesAction, google::key_pairs::create::CreateKeyPairsAction,
symmetric::keys::create_key::CreateKeyAction,
},
error::result::KmsCliResult,
tests::kms::certificates::certify::import_root_and_intermediate,
};
#[tokio::test]
async fn create_google_key_pair() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let cse_key_id = CreateKeyAction::default()
.run(ctx.get_owner_client())
.await?;
let (_root_id, _intermediate_id, issuer_private_key_id) =
Box::pin(import_root_and_intermediate(ctx)).await.unwrap();
let action = CreateKeyPairsAction {
user_id: "john.doe@acme.com".to_owned(),
cse_key_id: cse_key_id.to_string(),
issuer_private_key_id: None,
subject_name: "CN=John Doe,OU=Org Unit,O=Org Name,L=City,ST=State,C=US".to_owned(),
rsa_private_key_id: None,
sensitive: false,
wrapping_key_id: None,
leaf_certificate_extensions: None,
leaf_certificate_id: None,
leaf_certificate_pkcs12_file: None,
leaf_certificate_pkcs12_password: None,
number_of_days: 365,
dry_run: true,
};
action.run(ctx.get_owner_client()).await.unwrap_err();
let action = CreateKeyPairsAction {
issuer_private_key_id: Some(issuer_private_key_id.clone()),
leaf_certificate_extensions: Some(PathBuf::from(
"../../test_data/certificates/openssl/ext_leaf.cnf",
)),
..action
};
let certificate_1 = action.run(ctx.get_owner_client()).await.unwrap();
let action = CreateKeyPairsAction {
issuer_private_key_id: None,
leaf_certificate_extensions: None,
leaf_certificate_id: Some(certificate_1.to_string()),
..action
};
let _certificate_2 = action.run(ctx.get_owner_client()).await.unwrap();
let action = CreateKeyPairsAction {
user_id: "john.barry@acme.com".to_owned(),
leaf_certificate_id: None,
issuer_private_key_id: None,
leaf_certificate_extensions: None,
leaf_certificate_pkcs12_file: Some(PathBuf::from(
"../../test_data/certificates/csr/leaf.p12",
)),
leaf_certificate_pkcs12_password: Some("secret".to_owned()),
..action
};
let _certificate_3 = action.run(ctx.get_owner_client()).await.unwrap();
Ok(())
}
#[tokio::test]
async fn create_google_key_pair_and_sign_with_private_key() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let resp = GetAttributesAction {
id: Some("google_cse".to_owned()),
..Default::default()
}
.run(ctx.get_owner_client())
.await;
if resp.is_err() {
let _cse_key_id = CreateKeyAction {
key_id: Some("google_cse".to_owned()),
..Default::default()
}
.run(ctx.get_owner_client())
.await?;
}
let (_root_id, _intermediate_id, issuer_private_key_id) =
Box::pin(import_root_and_intermediate(ctx)).await.unwrap();
let action = CreateKeyPairsAction {
user_id: "marta.doe@acme.com".to_owned(),
cse_key_id: "google_cse".to_string(),
issuer_private_key_id: Some(issuer_private_key_id.to_string()),
subject_name: "CN=Marta Doe,OU=Org Unit,O=Org Name,L=City,ST=State,C=US".to_owned(),
rsa_private_key_id: None,
sensitive: false,
wrapping_key_id: None,
leaf_certificate_extensions: Some(PathBuf::from(
"../../test_data/certificates/openssl/ext_leaf.cnf",
)),
leaf_certificate_id: None,
leaf_certificate_pkcs12_file: None,
leaf_certificate_pkcs12_password: None,
number_of_days: 10,
dry_run: true,
};
let cert_id = action.run(ctx.get_owner_client()).await?;
info!("Created certificate ID: {cert_id}");
let owner_client = ctx.get_owner_client();
let (_, cert_object, _) = export_object(
&owner_client,
&cert_id.to_string(),
ExportObjectParams {
key_format_type: None,
..ExportObjectParams::default()
},
)
.await?;
if let Object::Certificate(certificate) = cert_object {
let cert_x509 = X509::from_der(&certificate.certificate_value)
.expect("Failed to parse certificate from DER");
let not_before = cert_x509.not_before();
let not_after = cert_x509.not_after();
let diff = not_before
.diff(not_after)
.expect("Failed to calculate date difference");
let actual_days = diff.days.abs();
info!(
"Certificate validity: {} days (requested: {} days)",
actual_days, action.number_of_days
);
info!("Not Before: {}", not_before);
info!("Not After: {}", not_after);
assert_eq!(
actual_days,
i32::try_from(action.number_of_days).unwrap(),
"Certificate validity ({} days) does not match requested validity ({} days)",
actual_days,
action.number_of_days
);
} else {
panic!("Expected Certificate object, got something else");
}
let owner_client = ctx.get_owner_client();
let attrs = owner_client
.get_attributes(cosmian_kmip::kmip_2_1::kmip_operations::GetAttributes {
unique_identifier: Some(
cosmian_kmip::kmip_2_1::kmip_types::UniqueIdentifier::TextString(
cert_id.to_string(),
),
),
attribute_reference: None,
})
.await?
.attributes;
let private_key_id = attrs
.get_link(cosmian_kmip::kmip_2_1::kmip_types::LinkType::PrivateKeyLink)
.expect("Certificate should be linked to a private key")
.to_string();
info!("Resolved private key ID: {private_key_id}");
let (_, wrapped_private_key, _attributes) = export_object(
&ctx.get_owner_client(),
&private_key_id,
ExportObjectParams {
wrapping_key_id: Some("google_cse"),
wrapping_cryptographic_parameters: Some(CryptographicParameters {
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
block_cipher_mode: Some(BlockCipherMode::GCM),
..CryptographicParameters::default()
}),
key_format_type: None,
..ExportObjectParams::default()
},
)
.await?;
let pkcs1_b64 = base64::engine::general_purpose::STANDARD.encode(
wrapped_private_key
.key_block()
.unwrap()
.wrapped_key_bytes()
.unwrap(),
);
info!(
"Exported private key in PKCS#8 DER (base64): {}",
&pkcs1_b64
);
let digest_b64 = "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=".to_owned();
let req = PrivateKeySignRequest {
authentication: "test".to_string(),
authorization: "test".to_string(),
algorithm: "SHA256withRSA".to_string(),
digest: digest_b64,
rsa_pss_salt_length: None,
reason: "CLI test".to_string(),
wrapped_private_key: pkcs1_b64,
};
owner_client
.post_no_ttlv::<_, serde_json::Value>("/google_cse/privatekeysign", Some(&req))
.await?;
Ok(())
}