From 5e933c462b57a1fcbd9902fe47fbe786aca953e9 Mon Sep 17 00:00:00 2001
From: unknown <1592085657@qq.com>
Date: Fri, 5 Dec 2025 16:36:18 +0800
Subject: [PATCH] support cms with timestamp
---
app/src/pages/listShow/ImportX509.vue | 20 --
scripts/initialize-user-and-keys.sh | 6 +-
src/application/datakey.rs | 52 ++-
src/client/cmd/add.rs | 32 ++
src/client/file_handler/kernel_module.rs | 19 +-
src/client/file_handler/p7s.rs | 14 +
src/domain/datakey/repository.rs | 2 +-
src/domain/sign_plugin.rs | 2 +-
src/domain/sign_service.rs | 2 +
.../database/model/datakey/repository.rs | 18 +-
src/infra/sign_backend/memory/backend.rs | 18 +-
src/infra/sign_plugin/cms.rs | 224 ++++++++++++-
src/infra/sign_plugin/openpgp.rs | 20 +-
src/infra/sign_plugin/signers.rs | 5 +-
src/infra/sign_plugin/x509.rs | 306 ++++++++++++++++--
src/presentation/handler/data/sign_handler.rs | 2 +-
src/util/options.rs | 3 +
src/util/sign.rs | 3 +
18 files changed, 669 insertions(+), 79 deletions(-)
diff --git a/app/src/pages/listShow/ImportX509.vue b/app/src/pages/listShow/ImportX509.vue
index 0bfbc7f..7a516a4 100644
--- a/app/src/pages/listShow/ImportX509.vue
+++ b/app/src/pages/listShow/ImportX509.vue
@@ -60,26 +60,6 @@
/>
-
-
-
-
-
-
-
-
-
-
,
data: Vec,
) -> Result>;
- async fn get_by_type_and_name(&self, key_type: String, key_name: String) -> Result;
-
+ async fn get_by_type_and_name(
+ &self,
+ key_type: Option,
+ key_name: String,
+ ) -> Result;
+ async fn get_timestamp_key_by_type_and_name(
+ &self,
+ key_type: Option,
+ key_name: String,
+ ) -> Result;
//method below used for maintenance
fn start_key_rotate_loop(&self, cancel_token: CancellationToken) -> Result<()>;
@@ -90,6 +99,7 @@ where
repository: R,
sign_service: Arc>>,
container: TimedFixedSizeCache,
+ timestamp_container: TimedFixedSizeCache,
}
impl DBKeyService
@@ -102,6 +112,7 @@ where
repository,
sign_service: Arc::new(RwLock::new(sign_service)),
container: TimedFixedSizeCache::new(Some(100), None, None, None),
+ timestamp_container: TimedFixedSizeCache::new(Some(100), None, None, None),
}
}
@@ -538,15 +549,28 @@ where
options: &HashMap,
data: Vec,
) -> Result> {
- let datakey = self.get_by_type_and_name(key_type, key_name).await?;
+ let mut timekey: Option = None;
+ if let Some(timestamp_key) = options.get(options::TIMESTAMP_KEY) {
+ if !timestamp_key.is_empty() {
+ timekey = Some(
+ self.get_timestamp_key_by_type_and_name(None, timestamp_key.to_string())
+ .await?,
+ );
+ }
+ }
+ let datakey = self.get_by_type_and_name(Some(key_type), key_name).await?;
self.sign_service
.read()
.await
- .sign(&datakey, data, options.clone())
+ .sign(&datakey, timekey, data, options.clone())
.await
}
- async fn get_by_type_and_name(&self, key_type: String, key_name: String) -> Result {
+ async fn get_by_type_and_name(
+ &self,
+ key_type: Option,
+ key_name: String,
+ ) -> Result {
if let Some(datakey) = self.container.get_sign_datakey(&key_name).await {
return Ok(datakey);
}
@@ -560,6 +584,24 @@ where
Ok(key)
}
+ async fn get_timestamp_key_by_type_and_name(
+ &self,
+ key_type: Option,
+ key_name: String,
+ ) -> Result {
+ if let Some(datakey) = self.timestamp_container.get_sign_datakey(&key_name).await {
+ return Ok(datakey);
+ }
+ let key = self
+ .repository
+ .get_enabled_key_by_type_and_name_with_parent_key(key_type, key_name.clone())
+ .await?;
+ self.timestamp_container
+ .update_sign_datakey(&key_name, key.clone())
+ .await?;
+ Ok(key)
+ }
+
fn start_key_rotate_loop(&self, cancel_token: CancellationToken) -> Result<()> {
let sign_service = self.sign_service.clone();
let mut interval = time::interval(Duration::seconds(60 * 60 * 2).to_std()?);
diff --git a/src/client/cmd/add.rs b/src/client/cmd/add.rs
index 90e2301..f18163f 100644
--- a/src/client/cmd/add.rs
+++ b/src/client/cmd/add.rs
@@ -21,6 +21,7 @@ use clap::Args;
use config::Config;
use regex::Regex;
use std::collections::HashMap;
+use std::fs;
use std::path::PathBuf;
use std::sync::{atomic::AtomicBool, Arc, RwLock};
use tokio::runtime;
@@ -88,6 +89,14 @@ pub struct CommandAdd {
help = "force create rpm v3 signature, default is false. only support when file type is rpm"
)]
rpm_v3: bool,
+ #[arg(long, default_value = "")]
+ #[arg(
+ help = "specify the path to the CRL file, which is used in CMS signing. only PEM format is supported"
+ )]
+ crl: String,
+ #[arg(long, default_value = "")]
+ #[arg(help = "Specify the timestamp key name, which is used in CMS signing")]
+ timestamp_key: String,
}
#[derive(Clone)]
@@ -106,6 +115,8 @@ pub struct CommandAddHandler {
sign_type: SignType,
token: Option,
rpm_v3: bool,
+ timestamp_key: String,
+ crl: String,
sign_options: Option>,
}
@@ -120,6 +131,11 @@ impl CommandAddHandler {
options::RPM_V3_SIGNATURE.to_string(),
self.rpm_v3.to_string(),
),
+ (
+ options::TIMESTAMP_KEY.to_string(),
+ self.timestamp_key.to_string(),
+ ),
+ (options::CRL.to_string(), self.crl.to_string()),
])
} else {
self.sign_options.clone().unwrap()
@@ -223,6 +239,19 @@ impl SignCommand for CommandAddHandler {
token = Some(t);
}
}
+
+ let mut crl: String = String::new();
+ if !command.crl.is_empty() {
+ crl = match fs::read_to_string(command.crl.clone()) {
+ Ok(crl) => crl,
+ Err(e) => {
+ return Err(error::Error::FileFoundError(format!(
+ "crl file: {} read failed {}",
+ command.crl, e
+ )));
+ }
+ };
+ }
Ok(CommandAddHandler {
worker_threads,
buffer_size: config.read()?.get_string("buffer_size")?.parse()?,
@@ -238,6 +267,8 @@ impl SignCommand for CommandAddHandler {
sign_type: command.sign_type,
token,
rpm_v3: command.rpm_v3,
+ timestamp_key: command.timestamp_key,
+ crl: crl,
sign_options: None,
})
}
@@ -302,6 +333,7 @@ impl SignCommand for CommandAddHandler {
Ok(f) => f,
Err(err) => return Some(err),
};
+ // key_attributes(digest_algo) need to be obtained when generate cms signature
info!("starting to sign {} files", files.len());
let mut signer =
RemoteSigner::new(channel.unwrap(), self.buffer_size, self.token.clone());
diff --git a/src/client/file_handler/kernel_module.rs b/src/client/file_handler/kernel_module.rs
index 081a155..2efc824 100644
--- a/src/client/file_handler/kernel_module.rs
+++ b/src/client/file_handler/kernel_module.rs
@@ -167,11 +167,11 @@ impl FileHandler for KernelModuleFileHandler {
}
if let Some(sign_type) = sign_options.get(options::SIGN_TYPE) {
- if sign_type != SignType::Cms.to_string().as_str()
+ if sign_type != SignType::KernelCms.to_string().as_str()
&& sign_type != SignType::PKCS7.to_string().as_str()
{
return Err(Error::InvalidArgumentError(
- "kernel module file only support cms or pkcs7 sign type".to_string(),
+ "kernel module file only support kernel-cms or pkcs7 sign type".to_string(),
));
}
}
@@ -265,7 +265,7 @@ mod test {
#[test]
fn test_get_raw_content_with_small_unsigned_content() {
- let mut sign_options = HashMap::new();
+ let sign_options = HashMap::new();
let file_handler = KernelModuleFileHandler::new();
let (name, original_content) = generate_unsigned_kernel_module(SIGNATURE_SIZE - 1)
.expect("generate unsigned kernel module failed");
@@ -279,7 +279,7 @@ mod test {
#[test]
fn test_get_raw_content_with_large_unsigned_content() {
- let mut sign_options = HashMap::new();
+ let sign_options = HashMap::new();
let file_handler = KernelModuleFileHandler::new();
let (name, original_content) = generate_unsigned_kernel_module(SIGNATURE_SIZE + 100)
.expect("generate unsigned kernel module failed");
@@ -293,7 +293,7 @@ mod test {
#[test]
fn test_get_raw_content_with_signed_content() {
- let mut sign_options = HashMap::new();
+ let sign_options = HashMap::new();
let file_handler = KernelModuleFileHandler::new();
let (name, original_content) = generate_signed_kernel_module(100, false)
.expect("generate signed kernel module failed");
@@ -307,7 +307,7 @@ mod test {
#[test]
fn test_get_raw_content_with_invalid_signed_content() {
- let mut sign_options = HashMap::new();
+ let sign_options = HashMap::new();
let file_handler = KernelModuleFileHandler::new();
let (name, _) =
generate_signed_kernel_module(100, true).expect("generate signed kernel module failed");
@@ -340,10 +340,13 @@ mod test {
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
- "invalid argument: kernel module file only support cms or pkcs7 sign type"
+ "invalid argument: kernel module file only support kernel-cms or pkcs7 sign type"
);
- options.insert(options::SIGN_TYPE.to_string(), SignType::Cms.to_string());
+ options.insert(
+ options::SIGN_TYPE.to_string(),
+ SignType::KernelCms.to_string(),
+ );
let result = handler.validate_options(&mut options);
assert!(result.is_ok());
diff --git a/src/client/file_handler/p7s.rs b/src/client/file_handler/p7s.rs
index 3391bb9..72a9527 100644
--- a/src/client/file_handler/p7s.rs
+++ b/src/client/file_handler/p7s.rs
@@ -15,10 +15,12 @@
*/
use super::traits::FileHandler;
+use crate::util::attributes::PkeyHashAlgo;
use crate::util::error::{Error, Result};
use crate::util::options;
use crate::util::sign::{KeyType, SignType};
use async_trait::async_trait;
+use openssl::hash::hash;
use std::collections::HashMap;
use std::path::PathBuf;
use tokio::fs;
@@ -79,6 +81,18 @@ impl FileHandler for CmsFileHandler {
format!("{}.{}", path.as_path().display(), CMS_EXTENSION),
))
}
+
+ async fn split_data(
+ &self,
+ path: &PathBuf,
+ _sign_options: &mut HashMap,
+ key_attributes: &HashMap,
+ ) -> Result>> {
+ let content = fs::read(path).await?;
+ let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(key_attributes);
+ let digest = hash(digest_algo, &content)?;
+ Ok(vec![digest.to_vec()])
+ }
}
#[cfg(test)]
diff --git a/src/domain/datakey/repository.rs b/src/domain/datakey/repository.rs
index a2694b0..26b855b 100644
--- a/src/domain/datakey/repository.rs
+++ b/src/domain/datakey/repository.rs
@@ -41,7 +41,7 @@ pub trait Repository: Send + Sync {
async fn update_key_data(&self, data_key: DataKey) -> Result<()>;
async fn get_enabled_key_by_type_and_name_with_parent_key(
&self,
- key_type: String,
+ key_type: Option,
name: String,
) -> Result;
async fn request_delete_key(
diff --git a/src/domain/sign_plugin.rs b/src/domain/sign_plugin.rs
index dd2c901..4b907a7 100644
--- a/src/domain/sign_plugin.rs
+++ b/src/domain/sign_plugin.rs
@@ -20,7 +20,7 @@ use chrono::{DateTime, Utc};
use std::collections::HashMap;
pub trait SignPlugins: Send + Sync {
- fn new(db: SecDataKey) -> Result
+ fn new(db: SecDataKey, timestamp_key: Option) -> Result
where
Self: Sized;
fn validate_and_update(key: &mut DataKey) -> Result<()>
diff --git a/src/domain/sign_service.rs b/src/domain/sign_service.rs
index ab7ed34..b3a59c7 100644
--- a/src/domain/sign_service.rs
+++ b/src/domain/sign_service.rs
@@ -49,10 +49,12 @@ pub trait SignBackend: Send + Sync {
async fn sign(
&self,
data_key: &DataKey,
+ timestamp_key: Option,
content: Vec,
options: HashMap,
) -> Result>;
async fn decode_public_keys(&self, data_key: &mut DataKey) -> Result<()>;
+ async fn decode_private_keys(&self, data_key: &mut DataKey) -> Result<()>;
async fn generate_crl_content(
&self,
data_key: &DataKey,
diff --git a/src/infra/database/model/datakey/repository.rs b/src/infra/database/model/datakey/repository.rs
index bf735c0..ce8d3e3 100644
--- a/src/infra/database/model/datakey/repository.rs
+++ b/src/infra/database/model/datakey/repository.rs
@@ -576,9 +576,16 @@ impl<'a> Repository for DataKeyRepository<'a> {
async fn get_enabled_key_by_type_and_name_with_parent_key(
&self,
- key_type: String,
+ key_type: Option,
name: String,
) -> Result {
+ let mut cond = Condition::all();
+ cond = cond.add(datakey_dto::Column::Name.eq(name));
+ if let Some(t) = key_type {
+ cond = cond.add(datakey_dto::Column::KeyType.eq(t));
+ }
+ cond = cond.add(datakey_dto::Column::KeyState.eq(KeyState::Enabled.to_string()));
+
match datakey_dto::Entity::find()
.select_only()
.columns(datakey_dto::Column::iter().filter(|col| {
@@ -590,12 +597,7 @@ impl<'a> Repository for DataKeyRepository<'a> {
| datakey_dto::Column::X509CrlUpdateAt
)
}))
- .filter(
- Condition::all()
- .add(datakey_dto::Column::Name.eq(name))
- .add(datakey_dto::Column::KeyType.eq(key_type))
- .add(datakey_dto::Column::KeyState.eq(KeyState::Enabled.to_string())),
- )
+ .filter(cond)
.one(self.db_connection)
.await?
{
@@ -1452,7 +1454,7 @@ mod tests {
assert_eq!(
datakey_repository
.get_enabled_key_by_type_and_name_with_parent_key(
- "openpgp".to_string(),
+ Some("openpgp".to_string()),
"fake_name".to_string()
)
.await?,
diff --git a/src/infra/sign_backend/memory/backend.rs b/src/infra/sign_backend/memory/backend.rs
index 8f914b5..729c14c 100644
--- a/src/infra/sign_backend/memory/backend.rs
+++ b/src/infra/sign_backend/memory/backend.rs
@@ -106,7 +106,7 @@ impl SignBackend for MemorySignBackend {
async fn generate_keys(&self, data_key: &mut DataKey) -> Result<()> {
let sec_key = SecDataKey::load(data_key, &self.engine).await?;
- let content = Signers::load_from_data_key(&data_key.key_type, sec_key)?
+ let content = Signers::load_from_data_key(&data_key.key_type, sec_key, None)?
.generate_keys(&data_key.key_type, &self.infra_configs)?;
data_key.private_key = self.engine.encode(content.private_key).await?;
data_key.public_key = self.engine.encode(content.public_key).await?;
@@ -123,11 +123,17 @@ impl SignBackend for MemorySignBackend {
async fn sign(
&self,
data_key: &DataKey,
+ timestamp_key: Option,
content: Vec,
options: HashMap,
) -> Result> {
let sec_key = SecDataKey::load(data_key, &self.engine).await?;
- Signers::load_from_data_key(&data_key.key_type, sec_key)?.sign(content, options)
+ let mut timestamp_sec_key: Option = None;
+ if let Some(ref key) = timestamp_key {
+ timestamp_sec_key = Some(SecDataKey::load(&key, &self.engine).await?);
+ }
+ Signers::load_from_data_key(&data_key.key_type, sec_key, timestamp_sec_key)?
+ .sign(content, options)
}
async fn decode_public_keys(&self, data_key: &mut DataKey) -> Result<()> {
@@ -136,6 +142,12 @@ impl SignBackend for MemorySignBackend {
Ok(())
}
+ async fn decode_private_keys(&self, data_key: &mut DataKey) -> Result<()> {
+ data_key.private_key = self.engine.decode(data_key.private_key.clone()).await?;
+ data_key.certificate = self.engine.decode(data_key.certificate.clone()).await?;
+ Ok(())
+ }
+
async fn generate_crl_content(
&self,
data_key: &DataKey,
@@ -144,7 +156,7 @@ impl SignBackend for MemorySignBackend {
next_update: DateTime,
) -> Result> {
let sec_key = SecDataKey::load(data_key, &self.engine).await?;
- Signers::load_from_data_key(&data_key.key_type, sec_key)?.generate_crl_content(
+ Signers::load_from_data_key(&data_key.key_type, sec_key, None)?.generate_crl_content(
revoked_keys,
last_update,
next_update,
diff --git a/src/infra/sign_plugin/cms.rs b/src/infra/sign_plugin/cms.rs
index e99db55..06dbe8f 100644
--- a/src/infra/sign_plugin/cms.rs
+++ b/src/infra/sign_plugin/cms.rs
@@ -3,6 +3,7 @@ use crate::util::attributes::PkeyHashAlgo;
use crate::util::error::{Error, Result};
use crate::util::options;
use foreign_types_shared::{ForeignType, ForeignTypeRef};
+use openssl::hash::hash;
use openssl::pkey;
use openssl::x509;
use openssl_sys::{
@@ -346,6 +347,10 @@ fn generate_timestamp_req(
));
}
+ let data: &[u8] = std::slice::from_raw_parts(data_ptr, data_len.try_into().unwrap());
+ let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(&attributes);
+ let digest = hash(digest_algo, &data)?;
+
// step2. generate ts_req from signature
let ts_req = TS_REQ_new();
let mut ts_guard = TsGuard(ts_req);
@@ -376,7 +381,12 @@ fn generate_timestamp_req(
));
}
- if TS_MSG_IMPRINT_set_msg(msg_imprint, data_ptr, data_len) != 1 {
+ if TS_MSG_IMPRINT_set_msg(
+ msg_imprint,
+ digest.to_vec().as_ptr(),
+ digest.len().try_into().unwrap(),
+ ) != 1
+ {
return Err(Error::RemoteSignError(
"TS_MSG_IMPRINT_set_msg failed".to_string(),
));
@@ -779,3 +789,215 @@ impl CmsPlugin {
}
}
}
+
+#[cfg(test)]
+const SM2_CRT: &str = "-----BEGIN CERTIFICATE-----
+MIIB1DCCAXoCFGfoVD/6iDpHYUbmTA0+LH/b4tfuMAoGCCqBHM9VAYN1MGwxCzAJ
+BgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRIw
+EAYDVQQKDAlNeUNvbXBhbnkxDzANBgNVBAsMBlJvb3RDQTEUMBIGA1UEAwwLU00y
+IFJvb3QgQ0EwHhcNMjUxMTAzMDgxODEyWhcNMzUxMTAxMDgxODEyWjBsMQswCQYD
+VQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzESMBAG
+A1UECgwJTXlDb21wYW55MQ8wDQYDVQQLDAZSb290Q0ExFDASBgNVBAMMC1NNMiBS
+b290IENBMFowFAYIKoEcz1UBgi0GCCqBHM9VAYItA0IABNYo1OwvitLruiU3oRAc
+uaLSplc2Vrj19z2oPicvx8hn3fQLYlqKrKcFvKOWllL3ByQVcMJ4HmRylmOrk24q
+4xYwCgYIKoEcz1UBg3UDSAAwRQIhAMnIl0Em/3b8hhR9Ly/FGlt3q2IN1EHLg64+
+JGLqK0DFAiAULqROgRSmSWpJgMzU8KMoPfDM7CJ5/NCnDqI3oM9uTw==
+-----END CERTIFICATE-----";
+#[cfg(test)]
+const SM2_KEY: &str = "-----BEGIN PRIVATE KEY-----
+MIGIAgEAMBQGCCqBHM9VAYItBggqgRzPVQGCLQRtMGsCAQEEIDUaoPl+RCqHV/Un
+qWcBnNWXVAOM7BMiiPWQFFotA1h0oUQDQgAE1ijU7C+K0uu6JTehEBy5otKmVzZW
+uPX3Pag+Jy/HyGfd9AtiWoqspwW8o5aWUvcHJBVwwngeZHKWY6uTbirjFg==
+-----END PRIVATE KEY-----";
+#[cfg(test)]
+const RSA_CRT: &str = "-----BEGIN CERTIFICATE-----
+MIIDYTCCAkkCFElnH8LftfLwEwPJRQ3i0hF0XQl4MA0GCSqGSIb3DQEBCwUAMG0x
+CzAJBgNVBAYTAkNOMQ0wCwYDVQQIDAR0ZXN0MQ0wCwYDVQQHDAR0ZXN0MQ0wCwYD
+VQQKDAR0ZXN0MQ0wCwYDVQQLDAR0ZXN0MQ0wCwYDVQQDDAR0ZXN0MRMwEQYJKoZI
+hvcNAQkBFgR0ZXN0MB4XDTI1MTEwMzA3MDkyM1oXDTM1MTEwMTA3MDkyM1owbTEL
+MAkGA1UEBhMCQ04xDTALBgNVBAgMBHRlc3QxDTALBgNVBAcMBHRlc3QxDTALBgNV
+BAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxDTALBgNVBAMMBHRlc3QxEzARBgkqhkiG
+9w0BCQEWBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRFYHs
+vLSybVatQSyKjNFkoiKpYvy8eD2SwJtUhX1aXgNi2JVV8YKf7ok0NxXQKFovhlP0
+rB7NOFbPw9DYceS9UG6M/PgyhhxAAP/oY8Sr7+Kyz1uf85Q/gGM0wzA27OdgBhGe
+BLv6Xg5MDSgXGAScnHTgTP4Ibt9b6xqV3jlw4dPYmaVnD24hBj1afsimgCEN2Iic
+vE3cxFBd6zxTSQdU7tcrPlspAS1rmL5E0opIavE8RTOLnjlcFZH1Hcyl89V1u0vn
+pRFllrTOtIKX7Px1yTlNXd6G1iaQgwzdLh8N1NxTgeFRwTOjW9cjeGbsfQwERdY6
+4bBhaTQVBraR0FqjAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGNwWCzbVFD1bgST
+lGXMSiCqi9H1UaQxJ4goq742qtoyCKkDEY+7289GmFPWufbK366kZBj52ibEMm7w
+4Tp9jVnlf9amkSC15JdBLeHuDT8QRNWMwj+PgVHhcBt+9thwYTiM/mQoxCBJeFiS
+vPDWEQxY9/OAh7lkZb+ZWrBxdz7wMa4UiBcfzpmT15vYkG0CvoLQPe73PDIszeF7
+utx8jfSPlUWhLtZ72qcwNeh5QNPr4dclAPIf+mxkipS0QiuWLMmBOLg0AXIi7YeG
+2E8IAhEIhpya/SkDBvGiju/8bp5r9x9OdOe61NGuqj86IqLoNWA3/bd03klZq1RA
+WD1O4V0=
+-----END CERTIFICATE-----";
+#[cfg(test)]
+const RSA_KEY: &str = "-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRFYHsvLSybVat
+QSyKjNFkoiKpYvy8eD2SwJtUhX1aXgNi2JVV8YKf7ok0NxXQKFovhlP0rB7NOFbP
+w9DYceS9UG6M/PgyhhxAAP/oY8Sr7+Kyz1uf85Q/gGM0wzA27OdgBhGeBLv6Xg5M
+DSgXGAScnHTgTP4Ibt9b6xqV3jlw4dPYmaVnD24hBj1afsimgCEN2IicvE3cxFBd
+6zxTSQdU7tcrPlspAS1rmL5E0opIavE8RTOLnjlcFZH1Hcyl89V1u0vnpRFllrTO
+tIKX7Px1yTlNXd6G1iaQgwzdLh8N1NxTgeFRwTOjW9cjeGbsfQwERdY64bBhaTQV
+BraR0FqjAgMBAAECggEAHm3CDAlF8Pu+/sBP8lVeegYArmSEwgxrxZ+jW4XWlvVn
+X8DZMaSlgo9tchHG6mryU8f5uvo7vK6DhSw13keWZxjoEJEWXAqu3NUTE/1FF6gg
+oYOnuDEeX67YgUrGNGV+TOLFOJlEHu1bqPoh3gPgU/Hkg+pLRECX4CGTVmv83G5g
+c/tPT0HMOdXeKpJTzmepLN1QFSnrRbIT1D5uzx4VurVgM+eKXcWN8y2hfZkyeaG7
+ZBW4efepN8gJRysVVAmNQGorMfp6gs26WRS8Se4OyiUthlRy47y1RDNO1mZl8jb/
+im2X5BNiKocnLd08/oFozufrax7RqVoDnFhk0XhYTQKBgQDZOGYjT2IklXW2XAN+
+9FMRk1LDPAJ4S8H1ITTO8K3AqgQJrE+4TJQSNuecq/wOHi1mTChlIdPkoMIWJHEa
+vdQgKE4R/2UxLbnpS2uIwtk7UbOFBBicR3Fn/1eB4eFBr97Wv/Pw/8KngGkRANmL
+aYnCEsssBvr0IL3Ri+um9C38pQKBgQD2aUGUBAJHMpFmS4gl0mqyjT1X6NoRIHnA
+SZNZjsMREqBATrF8fj6+Cm8TptbL0rwheSOc+hugNW4XBOjt9qppe8TVxTj10lq1
+Yi34DCdJ1qoPd7Nn/UKsv1jd0tvW0J7dKBcsq8lHzG7TOVa38iFzVO9WD62doKV0
+RHl3ziFvpwKBgH3z/v2AfUb7RwsbpYdKwpQRWc8ND92TB/9MZuOLmSR7MOYu/Pa/
+qKg7H+evrfK9utNzW4TwrX4HXSMbtF2uLr8Kv+Idth5jBkbpTYw6d123DSIW8vJD
+VtXXsHUGdefxw4PAQAHBO6yGf+W1GW+GHbPj091OmttN1OMZf+YJ9lRlAoGAOey4
+W8Etf+slPvTWhn2WU27cUsQMLyaBOHCTUOQ8etD0Funo0ykiOq5dOjNoHvXk/8Fo
+W8h3ogutW3/t+bKYkL9loBMCttbCOA1iXQMOYU8zHvu2kuV4PP+mNk8RGshj7/0y
+pW+km1o1WzYJaqhisKfwszxwRbOz8Ub/fuhX99UCgYEAjNSTN7iShFW5jQM8L5Pm
+BlBogxETPW4TtTgI1xyFHfAY3ZYj+HO9vXLAmuwO6acgDzCSm7h+bfqOLSNPP5Ii
+MnUQOZ522LfVOBzi42Hm3aobR4jex/X+3O+NJTi+UtTtfSNBeagkTdh6xORV8XOz
+yQO9OpVlZivvOX7n6gjX1jM=
+-----END PRIVATE KEY-----";
+
+#[test]
+fn test_generate_cms_with_rsa() {
+ let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate");
+ let pkey =
+ pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key");
+ let content = b"test content";
+
+ let mut attributes = HashMap::new();
+ attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string());
+ let options = HashMap::new();
+
+ let result = generate_cms_with_hash(&cert, &pkey, content, options, attributes);
+ assert!(result.is_ok(), "CMS generation failed: {:?}", result.err());
+}
+#[test]
+fn test_generate_cms_with_sm2_cert() {
+ let cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate");
+ let pkey =
+ pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key");
+ let content = b"test content";
+
+ let mut attributes = HashMap::new();
+ attributes.insert(attributes::DIGEST_ALGO.to_string(), "sm3".to_string());
+ let options = HashMap::new();
+
+ let result = generate_cms_with_hash(&cert, &pkey, content, options, attributes);
+ assert!(result.is_ok(), "CMS generation failed: {:?}", result.err());
+}
+#[test]
+fn test_generate_cms_with_incorret_alg() {
+ let cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate");
+ let pkey =
+ pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key");
+ let content = b"test content";
+
+ let mut attributes = HashMap::new();
+ attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string());
+ let options = HashMap::new();
+
+ let result = generate_cms_with_hash(&cert, &pkey, content, options, attributes);
+ assert!(
+ result.is_err(),
+ "Expected error due to use incorret digest algorithm"
+ );
+}
+#[test]
+fn test_generate_timestamp_req() {
+ let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate");
+ let pkey =
+ pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key");
+ let content = b"test content";
+
+ let mut attributes = HashMap::new();
+ attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string());
+ let options = HashMap::new();
+
+ let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone())
+ .expect("CMS generation failed");
+ let ts_req = generate_timestamp_req(cms, attributes.clone());
+ assert!(
+ ts_req.is_ok(),
+ "Timestamp request generation failed: {:?}",
+ ts_req.err()
+ );
+}
+
+#[test]
+fn test_generate_timestamp_tst() {
+ let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate");
+ let pkey =
+ pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key");
+ let content = b"test content";
+
+ let mut attributes = HashMap::new();
+ attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string());
+ let options = HashMap::new();
+
+ let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone())
+ .expect("CMS generation failed");
+ let ts_req =
+ generate_timestamp_req(cms, attributes.clone()).expect("Failed to generate TS request");
+ let tst_info = generate_timestamp_tst(ts_req, &cert).expect("TST info generation failed");
+ assert!(!tst_info.is_null(), "TST info is null");
+}
+
+#[test]
+fn test_generate_timestamp_signature() {
+ let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate");
+ let pkey =
+ pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key");
+ let content = b"test content";
+
+ let mut attributes = HashMap::new();
+ attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string());
+ let options = HashMap::new();
+
+ let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone())
+ .expect("CMS generation failed");
+ let ts_req =
+ generate_timestamp_req(cms, attributes.clone()).expect("Failed to generate TS request");
+ let tst_info = generate_timestamp_tst(ts_req, &cert).expect("TST info generation failed");
+
+ let tsa_cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate");
+ let tsa_pkey =
+ pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key");
+ let mut tsa_pattributes = HashMap::new();
+ tsa_pattributes.insert(attributes::DIGEST_ALGO.to_string(), "sm3".to_string());
+
+ let tst = generate_timestamp_signature(&tsa_cert, &tsa_pkey, tst_info, tsa_pattributes)
+ .expect("TST generation failed");
+ assert!(!tst.is_null(), "TST is null");
+}
+
+#[test]
+fn test_attach_timestamp_to_cms() {
+ let cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate");
+ let pkey =
+ pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key");
+ let content = b"test content";
+
+ let mut attributes = HashMap::new();
+ attributes.insert(attributes::DIGEST_ALGO.to_string(), "sm3".to_string());
+ let options = HashMap::new();
+
+ let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone())
+ .expect("CMS generation failed");
+ let ts_req =
+ generate_timestamp_req(cms, attributes.clone()).expect("Failed to generate TS request");
+ let tst_info = generate_timestamp_tst(ts_req, &cert).expect("TST info generation failed");
+
+ let tsa_cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate");
+ let tsa_pkey =
+ pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key");
+ let mut tsa_pattributes = HashMap::new();
+ tsa_pattributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string());
+
+ let tst = generate_timestamp_signature(&tsa_cert, &tsa_pkey, tst_info, tsa_pattributes)
+ .expect("TST generation failed");
+ attach_timestamp_to_cms(cms, tst).expect("CMS Attach Timestamp failed");
+}
diff --git a/src/infra/sign_plugin/openpgp.rs b/src/infra/sign_plugin/openpgp.rs
index 4421c54..7674a36 100644
--- a/src/infra/sign_plugin/openpgp.rs
+++ b/src/infra/sign_plugin/openpgp.rs
@@ -134,7 +134,7 @@ impl OpenPGPPlugin {
}
impl SignPlugins for OpenPGPPlugin {
- fn new(db: SecDataKey) -> Result {
+ fn new(db: SecDataKey, _timestamp_key: Option) -> Result {
let mut secret_key = None;
let mut public_key = None;
if !db.private_key.unsecure().is_empty() {
@@ -466,7 +466,7 @@ mod test {
.await
.expect("load sec datakey successfully");
let plugin =
- OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully");
+ OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully");
plugin
.generate_keys(&KeyType::OpenPGP, &HashMap::new())
.expect(
@@ -488,7 +488,7 @@ mod test {
.await
.expect("load sec datakey successfully");
let plugin =
- OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully");
+ OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully");
plugin
.generate_keys(&KeyType::OpenPGP, &HashMap::new())
.expect(format!("generate key with key size {} successfully", key_size).as_str());
@@ -508,7 +508,7 @@ mod test {
.await
.expect("load sec datakey successfully");
let plugin =
- OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully");
+ OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully");
plugin
.generate_keys(&KeyType::OpenPGP, &HashMap::new())
.expect(format!("generate key with key type {} successfully", key_type).as_str());
@@ -525,7 +525,8 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully");
+ let plugin =
+ OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully");
plugin
.generate_keys(&KeyType::OpenPGP, &HashMap::new())
.expect(format!("generate key with no passphrase successfully").as_str());
@@ -536,7 +537,8 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully");
+ let plugin =
+ OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully");
plugin
.generate_keys(&KeyType::OpenPGP, &HashMap::new())
.expect(format!("generate key with passphrase successfully").as_str());
@@ -633,7 +635,8 @@ j0AZp6WE
)
.await
.expect("load sec datakey successfully");
- let plugin = OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully");
+ let plugin =
+ OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully");
let keys = plugin
.generate_keys(&KeyType::OpenPGP, &HashMap::new())
.expect(format!("generate key successfully").as_str());
@@ -646,7 +649,8 @@ j0AZp6WE
attributes: Default::default(),
parent: None,
};
- let instance = OpenPGPPlugin::new(sec_keys).expect("create openpgp instance successfully");
+ let instance =
+ OpenPGPPlugin::new(sec_keys, None).expect("create openpgp instance successfully");
let signature = instance
.sign(content.to_vec(), parameter)
.expect("sign successfully");
diff --git a/src/infra/sign_plugin/signers.rs b/src/infra/sign_plugin/signers.rs
index fe791f2..1de616a 100644
--- a/src/infra/sign_plugin/signers.rs
+++ b/src/infra/sign_plugin/signers.rs
@@ -29,11 +29,12 @@ impl Signers {
pub fn load_from_data_key(
key_type: &KeyType,
data_key: SecDataKey,
+ timestamp_key: Option,
) -> Result> {
match key_type {
- KeyType::OpenPGP => Ok(Box::new(OpenPGPPlugin::new(data_key)?)),
+ KeyType::OpenPGP => Ok(Box::new(OpenPGPPlugin::new(data_key, None)?)),
KeyType::X509CA | KeyType::X509ICA | KeyType::X509EE => {
- Ok(Box::new(X509Plugin::new(data_key)?))
+ Ok(Box::new(X509Plugin::new(data_key, timestamp_key)?))
}
}
}
diff --git a/src/infra/sign_plugin/x509.rs b/src/infra/sign_plugin/x509.rs
index 4aef025..5ee7e64 100644
--- a/src/infra/sign_plugin/x509.rs
+++ b/src/infra/sign_plugin/x509.rs
@@ -22,6 +22,7 @@ use openssl::cms::{CMSOptions, CmsContentInfo};
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkcs7::{Pkcs7, Pkcs7Flags};
+use openssl::pkey;
use openssl::pkey::PKey;
use openssl::stack::Stack;
use openssl::x509;
@@ -37,6 +38,7 @@ use openssl_sys::{
use secstr::SecVec;
use serde::Deserialize;
use std::collections::HashMap;
+use std::ffi::CString;
use std::str::FromStr;
use std::time::{Duration, SystemTime};
@@ -49,6 +51,7 @@ use crate::domain::datakey::plugins::x509::{
X509DigestAlgorithm, X509EEUsage, X509KeyType, X509_SM2_VALID_KEY_SIZE, X509_VALID_KEY_SIZE,
};
use crate::domain::sign_plugin::SignPlugins;
+use crate::infra::sign_plugin::cms::{CmsContext, CmsPlugin, EVP_PKEY_is_a, Step};
use crate::util::attributes;
use crate::util::error::{Error, Result};
use crate::util::key::{decode_hex_string_to_u8, encode_u8_to_hex_string};
@@ -178,8 +181,11 @@ pub struct X509Plugin {
private_key: SecVec,
public_key: SecVec,
certificate: SecVec,
+ tsa_cert: SecVec,
+ tsa_key: SecVec,
identity: String,
attributes: HashMap,
+ tsa_attributes: HashMap,
parent_key: Option,
}
@@ -512,10 +518,32 @@ impl X509Plugin {
serial_number: Some(encode_u8_to_hex_string(&serial_number.to_vec())),
})
}
+
+ fn detect_key_type(private_key: &PKey) -> Result {
+ unsafe {
+ let rsa = CString::new("RSA").unwrap();
+ let rsa_pss = CString::new("RSA-PSS").unwrap();
+ let dsa = CString::new("DSA").unwrap();
+ let sm2 = CString::new("SM2").unwrap();
+ if EVP_PKEY_is_a(private_key.as_ptr(), rsa.as_ptr()) == 1 {
+ Ok(X509KeyType::Rsa.as_str().to_string())
+ } else if EVP_PKEY_is_a(private_key.as_ptr(), dsa.as_ptr()) == 1 {
+ Ok(X509KeyType::Dsa.as_str().to_string())
+ } else if EVP_PKEY_is_a(private_key.as_ptr(), sm2.as_ptr()) == 1 {
+ Ok(X509KeyType::Sm2.as_str().to_string())
+ } else if EVP_PKEY_is_a(private_key.as_ptr(), rsa_pss.as_ptr()) == 1 {
+ Ok(X509KeyType::Rsa.as_str().to_string())
+ } else {
+ Err(Error::InvalidArgumentError(
+ "key type only support RSA,DSA,SM2".to_string(),
+ ))
+ }
+ }
+ }
}
impl SignPlugins for X509Plugin {
- fn new(db: SecDataKey) -> Result {
+ fn new(db: SecDataKey, timestamp_key: Option) -> Result {
let mut plugin = Self {
name: db.name.clone(),
private_key: db.private_key.clone(),
@@ -524,10 +552,19 @@ impl SignPlugins for X509Plugin {
identity: db.identity.clone(),
attributes: db.attributes,
parent_key: None,
+ tsa_cert: SecVec::new(Vec::new()),
+ tsa_key: SecVec::new(Vec::new()),
+ tsa_attributes: HashMap::new(),
};
if let Some(parent) = db.parent {
plugin.parent_key = Some(parent);
}
+
+ if let Some(ref key) = timestamp_key {
+ plugin.tsa_cert = key.certificate.clone();
+ plugin.tsa_key = key.private_key.clone();
+ plugin.tsa_attributes = key.attributes.clone();
+ }
Ok(plugin)
}
@@ -536,11 +573,25 @@ impl SignPlugins for X509Plugin {
Self: Sized,
{
let _ = attributes_validate::(&key.attributes)?;
- let _private_key = PKey::private_key_from_pem(&key.private_key)?;
+ let private_key = PKey::private_key_from_pem(&key.private_key)?;
let certificate = x509::X509::from_pem(&key.certificate)?;
if !key.public_key.is_empty() {
let _public_key = PKey::public_key_from_pem(&key.public_key)?;
}
+ match X509Plugin::detect_key_type(&private_key) {
+ Ok(key_type) => {
+ key.attributes
+ .insert("key_type".to_string(), key_type.to_string());
+ }
+ Err(_e) => {
+ return Err(Error::InvalidArgumentError(
+ "key type only support RSA,DSA,SM2".to_string(),
+ ));
+ }
+ }
+ let bit = private_key.bits();
+ key.attributes
+ .insert("key_length".to_string(), bit.to_string());
let unix_time = Asn1Time::from_unix(0)?.diff(certificate.not_after())?;
let expire = SystemTime::UNIX_EPOCH
+ Duration::from_secs(unix_time.days as u64 * 86400 + unix_time.secs as u64);
@@ -644,7 +695,7 @@ impl SignPlugins for X509Plugin {
)?;
Ok(pkcs7.to_der()?)
}
- SignType::Cms => {
+ SignType::KernelCms => {
//cms option reference: https://man.openbsd.org/CMS_sign.3
let cms_signature = CmsContentInfo::sign(
Some(&certificate),
@@ -659,6 +710,39 @@ impl SignPlugins for X509Plugin {
)?;
Ok(cms_signature.to_der()?)
}
+ SignType::Cms => {
+ let tsa_cert_pem = self.tsa_cert.unsecure();
+ let tsa_key_pem = self.tsa_key.unsecure();
+ let mut ctx = CmsContext::new(
+ &certificate,
+ &private_key,
+ content.as_slice(),
+ &options,
+ &self.attributes,
+ &self.tsa_attributes,
+ tsa_cert_pem,
+ tsa_key_pem,
+ );
+
+ let need_ts = !tsa_cert_pem.is_empty() && !tsa_key_pem.is_empty();
+ if need_ts {
+ info!("tsa cert & key present, timestamp will be attached");
+ let steps: &[Step] = &[
+ &CmsPlugin::step_generate_cms,
+ &CmsPlugin::step_generate_ts_req,
+ &CmsPlugin::step_load_tsa_cert_key,
+ &CmsPlugin::step_generate_tst_info,
+ &CmsPlugin::step_generate_timestamp_token,
+ &CmsPlugin::step_attach_timestamp,
+ ];
+ CmsPlugin::run_steps(&mut ctx, steps)?;
+ } else {
+ info!("tsa cert or key is empty, skip timestamp");
+ let steps: &[Step] = &[&CmsPlugin::step_generate_cms];
+ CmsPlugin::run_steps(&mut ctx, steps)?;
+ }
+ CmsPlugin::cms_to_vec(ctx.cms)
+ }
SignType::RsaHash => {
// rust-openssl/openssl/src/pkey_ctx.rs
let mut signature = vec![];
@@ -825,7 +909,7 @@ mod test {
let sec_datakey = SecDataKey::load(&ca_key, &dummy_engine)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
let ca_content = plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(format!("generate ca key with no passphrase successfully").as_str());
@@ -846,7 +930,7 @@ mod test {
let sec_datakey = SecDataKey::load(&ica_key, &dummy_engine)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
let ica_content = plugin
.generate_keys(&KeyType::X509ICA, &infra_config)
.expect(format!("generate ica key with no passphrase successfully").as_str());
@@ -867,7 +951,7 @@ mod test {
let sec_datakey = SecDataKey::load(&ica_key, &dummy_engine)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
let ee_content = plugin
.generate_keys(&KeyType::X509EE, &infra_config)
.expect(format!("generate ee key with no passphrase successfully").as_str());
@@ -878,10 +962,20 @@ mod test {
public_key: SecVec::new(ee_content.public_key.clone()),
certificate: SecVec::new(ee_content.certificate.clone()),
identity: "".to_string(),
- attributes: Default::default(),
+ attributes: parameter.clone(),
parent: None,
};
- X509Plugin::new(sec_keys).expect("create x509 instance successfully")
+
+ let timestamp_key = SecDataKey {
+ name: "".to_string(),
+ private_key: SecVec::new(ee_content.private_key.clone()),
+ public_key: SecVec::new(ee_content.public_key.clone()),
+ certificate: SecVec::new(ee_content.certificate.clone()),
+ identity: "".to_string(),
+ attributes: parameter.clone(),
+ parent: None,
+ };
+ X509Plugin::new(sec_keys, Some(timestamp_key)).expect("create x509 instance successfully")
}
#[test]
@@ -991,7 +1085,7 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(format!("generate ca key with digest {} successfully", hash).as_str());
@@ -1012,7 +1106,7 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(format!("generate ca key with digest sm3 successfully").as_str());
@@ -1031,7 +1125,7 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
let result = plugin.generate_keys(&KeyType::X509CA, &infra_config);
assert!(result.is_err());
}
@@ -1049,7 +1143,7 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(
@@ -1073,7 +1167,7 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(
@@ -1094,7 +1188,7 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(format!("generate ca key with no passphrase successfully").as_str());
@@ -1106,7 +1200,7 @@ mod test {
)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(format!("generate ca key with passphrase successfully").as_str());
@@ -1182,9 +1276,185 @@ X5BboR/QJakEK+H+EUQAiDs=
);
}
+ #[test]
+ fn test_validate_and_update_with_sm2cert() {
+ let certificate = "-----BEGIN CERTIFICATE-----
+MIIB1DCCAXoCFGfoVD/6iDpHYUbmTA0+LH/b4tfuMAoGCCqBHM9VAYN1MGwxCzAJ
+BgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRIw
+EAYDVQQKDAlNeUNvbXBhbnkxDzANBgNVBAsMBlJvb3RDQTEUMBIGA1UEAwwLU00y
+IFJvb3QgQ0EwHhcNMjUxMTAzMDgxODEyWhcNMzUxMTAxMDgxODEyWjBsMQswCQYD
+VQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzESMBAG
+A1UECgwJTXlDb21wYW55MQ8wDQYDVQQLDAZSb290Q0ExFDASBgNVBAMMC1NNMiBS
+b290IENBMFowFAYIKoEcz1UBgi0GCCqBHM9VAYItA0IABNYo1OwvitLruiU3oRAc
+uaLSplc2Vrj19z2oPicvx8hn3fQLYlqKrKcFvKOWllL3ByQVcMJ4HmRylmOrk24q
+4xYwCgYIKoEcz1UBg3UDSAAwRQIhAMnIl0Em/3b8hhR9Ly/FGlt3q2IN1EHLg64+
+JGLqK0DFAiAULqROgRSmSWpJgMzU8KMoPfDM7CJ5/NCnDqI3oM9uTw==
+-----END CERTIFICATE-----";
+ let private_key = "-----BEGIN PRIVATE KEY-----
+MIGIAgEAMBQGCCqBHM9VAYItBggqgRzPVQGCLQRtMGsCAQEEIDUaoPl+RCqHV/Un
+qWcBnNWXVAOM7BMiiPWQFFotA1h0oUQDQgAE1ijU7C+K0uu6JTehEBy5otKmVzZW
+uPX3Pag+Jy/HyGfd9AtiWoqspwW8o5aWUvcHJBVwwngeZHKWY6uTbirjFg==
+-----END PRIVATE KEY-----";
+ let mut datakey = get_default_datakey(None, None, None);
+ datakey.certificate = certificate.as_bytes().to_vec();
+ datakey.private_key = private_key.as_bytes().to_vec();
+ let _ = X509Plugin::validate_and_update(&mut datakey);
+ if let Some(value) = datakey.attributes.get("key_length") {
+ assert_eq!(value, "256");
+ } else {
+ panic!("Expected key 'key_length' not found in the map.");
+ }
+
+ if let Some(value) = datakey.attributes.get("key_type") {
+ assert_eq!(value, "sm2");
+ } else {
+ panic!("Expected key 'key_type' not found in the map.");
+ }
+ }
+
+ #[test]
+ fn test_validate_and_update_with_rsacert() {
+ let certificate = "-----BEGIN CERTIFICATE-----
+MIIDSzCCAjOgAwIBAgIUKTs/prIakrwRyyYbYcfoy6YC6rkwDQYJKoZIhvcNAQEL
+BQAwTjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAmNuMQswCQYDVQQHDAJjbjELMAkG
+A1UECgwCY24xCzAJBgNVBAsMAmNuMQswCQYDVQQDDAJDTjAeFw0yNTExMTgxMjAy
+MTBaFw0yNjExMTgxMjAyMTBaME4xCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJjbjEL
+MAkGA1UEBwwCY24xCzAJBgNVBAoMAmNuMQswCQYDVQQLDAJjbjELMAkGA1UEAwwC
+Q04wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCf87TFBu9fPQ31mjWA
+OcYMQeMmHsI/v1Sng4aBoLhWAWZZ+8AuV37wjBVuYGR4GkZHmIJBmi2YNBkPt/4A
+IqX4S8hYAAZUurXcVKub2aQOnfh9OLTIxT/GR56imlcRZDrQ+R/VrpVj3yN3qQHP
+M1oyuzWin8osCSDRHfEAWcmKVmvHfq6tYVbnmOSQfiPI8+zxMsaMT07RDogOfxEO
+/QkPyj16ih9zpK9N2rqbj7q00JIiTSBZ3P/zlshjiZSGZeVaj3bC4KnzKrdlxS46
+nfJ7tZ+sHxBqSuScO8X/ButvL7HMnB92Ut47C2SnvD/Or+z86G9KDD2pRxqXcfBo
+4cYXAgMBAAGjITAfMB0GA1UdDgQWBBSwe9s1aUMAib5BAffASj6l7+5D1DANBgkq
+hkiG9w0BAQsFAAOCAQEAPKEWCfyNxRdaemcYEXgp6/AlmFoFaNBLi0ewyKOTuy+N
+zQvvgdvFbkdsg9BygNQeZQ/WFpNwrODxMZgcGFLpfxPgq1JrIVrU4CQLn8AgTvkc
+2sw/1u4xw4ufyKIYxQdsaOPLXOwedUtY96X0e622oIrr7tmUIse8502WnhllRtHL
+aGhroMLSVZrNoAU7KHbC3fnHmQPl9HWx9m37u3DGVtLf8/uXsX74RyV6U9ESHbKw
+ptZaepLoHk4fIixgMEVMAlHrZPdtUs7X0B520fi2/BwdChqfewf6thkBK9pzGcss
+Z8qL7OaQxFqgD2qi7jjWqkqmYe/yxyIowvu1bWRtbA==
+-----END CERTIFICATE-----";
+ let private_key = "-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCf87TFBu9fPQ31
+mjWAOcYMQeMmHsI/v1Sng4aBoLhWAWZZ+8AuV37wjBVuYGR4GkZHmIJBmi2YNBkP
+t/4AIqX4S8hYAAZUurXcVKub2aQOnfh9OLTIxT/GR56imlcRZDrQ+R/VrpVj3yN3
+qQHPM1oyuzWin8osCSDRHfEAWcmKVmvHfq6tYVbnmOSQfiPI8+zxMsaMT07RDogO
+fxEO/QkPyj16ih9zpK9N2rqbj7q00JIiTSBZ3P/zlshjiZSGZeVaj3bC4KnzKrdl
+xS46nfJ7tZ+sHxBqSuScO8X/ButvL7HMnB92Ut47C2SnvD/Or+z86G9KDD2pRxqX
+cfBo4cYXAgMBAAECggEABZvMCOSXXCWN6cDAg4CDG0bsKhgGA6o306/e9YinLgza
+g+k58eYLg2/GCJrEqxlwwW3tk1NOqfmZr11qQKL2YuB1Y/CMSEhLvDAT3GEjSYfs
+gKeOX0PbWp6ER3tV9jwne9Bgd2OpxVi7q6R3dcZ9MS4zUUJ9GlIvnmWIX9TGJl2X
+NI8tY2SG6J+ow+OOX98IUzLcyVaZR/AJBKUgFl0PjExLfWBDSLJqJJYEamDy1INh
+1PLmPjx3Hz+6r5iaVw3OqPJcZzo+9Bj16RhybOlRvrXMAUbD7w2RzfXI096b4De+
+HgyYHFNxj5KpC6qnihkGZVBcbeIjXXPgwexfDipbQQKBgQDNei3y3l0kOXgUgfhp
+GMxnmyiHKZmtHzXJJnlNHib7wVPh6YiGDnxVl50MekxSqo53ASv0cpamcDy5TM6N
+R5ZeBJW9//MqA0OGbNOCDjYBg2E8jGvyXVPp76ouRxeO5aDordJof4isp6VAPImv
+djx3CxxNJKJhzKo5oSJZ9iD+RwKBgQDHR+zsifUDimqg33mrJ92HUMiFxM+haYMV
+RySnQZXlDPnVsuZYAdJBUKAMG8ObHy2R2KMnxDdFRRhW6AVzkMkZaQXTUh4M7sx0
+QKWHCXRYWNqoS7qOzxS9O6dW6+kmv/bMCjX6l+z2pIe3bpKuHgOPFVyJuNhz285R
+RtbxSrbRsQKBgHLRPA3DfY55YoUrHzEy/z1BsULd1xarIvX0vsF+ANCa9hF92qD2
+RTnaz5IiYLWswpDzIamlwlLc0sHEjoLZpseAjmAuPqWST1A1TXcWE82CqXoZCVTU
+G8jT+GeFqD9cRy7dun5UDX5U631alqFqU1094yGkP+ygXdp4FObqJwOPAoGACWMC
+7vVknCEV+rPsGDrNfYU5nMtzeEfvC76JJHO7asmcrws5PGYBkGAK2eco5JKoY9lP
+fh0I+XNSvS06rIHiZxcCVjzk+3j4GnW9FkpEt7CfxBOlGvr4IB3COR7toYyjRGMq
+vb4QRGHlnqdPs3HoewHnlPknAPYWls9+amk5iVECgYEAu90xfsQzVSiQc4FiDC4+
+VU0SuEtd8KmguwckE0RProzJXTs4BhooUB4uwgKA4+IP6cPG9my4vaBL055fHiYb
+xpmKp7lj+xPgtXIekR+vzIma2nXiD4Adrs2ITwcjY7dtKLyoLiVJqXQRxWeBoaDS
+hlaQghVl9wUq5TwOJgwJDsQ=
+-----END PRIVATE KEY-----";
+ let mut datakey = get_default_datakey(None, None, None);
+ datakey.certificate = certificate.as_bytes().to_vec();
+ datakey.private_key = private_key.as_bytes().to_vec();
+ let _ = X509Plugin::validate_and_update(&mut datakey);
+ if let Some(value) = datakey.attributes.get("key_length") {
+ assert_eq!(value, "2048");
+ } else {
+ panic!("Expected key 'key_length' not found in the map.");
+ }
+
+ if let Some(value) = datakey.attributes.get("key_type") {
+ assert_eq!(value, "rsa");
+ } else {
+ panic!("Expected key 'key_type' not found in the map.");
+ }
+ }
+
+ #[test]
+ fn test_validate_and_update_with_dsacert() {
+ let certificate = "-----BEGIN CERTIFICATE-----
+MIIEkzCCBEGgAwIBAgIUHw8WUb9beKVreRngMZyZUrP8UqgwCwYJYIZIAWUDBAMC
+MEUxCzAJBgNVBAYTAkNOMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ
+bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjUxMTE4MTI1MjUxWhcNMjYxMTE4
+MTI1MjUxWjBFMQswCQYDVQQGEwJDTjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G
+A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIDRDCCAjYGByqGSM44BAEw
+ggIpAoIBAQCrEexqKCpPOzXK9ZyZZeSLCEFoheoAOMTxLDXcTw9h88J7GZ70udGZ
+KinBbT/L9yZimzH3FSwyWbR8gjJxzL84q2qv8rLTtXXgjL93uF+1lDcfITgswCKm
+Uvm4uMwPCpved7U+Ni5E5E7FJHEY8MVQQdBnQsKhLpIWyi4xTEK1vHVwS3gyL7D9
+HvxaZInZ5x7tOeqKeNsXOS9zXMdkvhyvfDoMtCrq9CwAd/QNkizQ+jyiQP9/Q8xf
+crCp4RMHfMTM+c7KkB34w0FYVWg8GOMpWJw55wQ4VpSCxSrc0JC6159vxtLk/CzW
+k9IqfA7f7mr/7sR8StKEhioyQ2Z45Z3nAh0AkhAfQfPdW+1ub60zvSsjfOLso36I
+fT7TbWvhAQKCAQEAg7xfk8S1E+4NKgxypwtvvt/FVnfbgbkZ0pkQUrD8bbYZamiN
+vdMvDauEj16AEwbvUkLdZa5Ht/EcknYimgGG305Ah/hvqUayVd9C22s/7JxOyg/W
+Iy1Y6vScDWNO6Svlu9ZU8RmTEXgL9vIekbo1hAnni3zmC9/cDOuMWVdkL0vWqirw
+oY8p2QJ3hJXZTD1pgSQaI9VsnnwU+hmCrhRQQUZf6xcDWe6kpgBnCD3kJu3GDr4I
+EsA8Q4rGv+Cr4z29Dl7BiSLnJVSVQ9HOSdqvE73CYHJbeaWk0ASNBkimeqzY+5lP
+tqGHoZhDw0V6V9xmE6yA+4RA1lCZSn8aLwoEfQOCAQYAAoIBAQCEWY0OioYoUwTx
+qOtrDxUypntePUafTAjp3ZsEy54eLGVBWC4Dd9T78Zn98x/9dt/leH+7f40Dv1tA
+d2ok5cjMkeMqUEcq9iEC4SkqZfTg6seaOmMaeOSRSfIw2JR0g8RLUCtEvxyfbBNF
+2T6aka4PMqSIx4IPKpRENY2/ICYrFAjTHUFyE7d3ER/zbE+r/J8KpBXi0nDMkYcv
+1RkZhkDkxoWnVWO6BPDnbe1yL2linCNUhRtmpYHfe9DO16st7MYBdjLS7YEed9Zv
+SoBy1UasYaCEoLROzRuUDWXc+mpY73G6B1cAmZd9OU1d+lQ3K5SdIVSrej5YksTE
+5QQsoQlUoyEwHzAdBgNVHQ4EFgQUooMZNGLs7K3I1LZBzsX8gbi3fgEwCwYJYIZI
+AWUDBAMCAz8AMDwCHGdHB/xu+SQC92KUgXcW2H67qGTg5jsKYMAJBlcCHEbV7+7K
+TdRvdJ2ZKZjSsC39ZPCP7KnaP881vaI=
+-----END CERTIFICATE-----";
+ let private_key = "-----BEGIN PRIVATE KEY-----
+MIICXgIBADCCAjYGByqGSM44BAEwggIpAoIBAQCrEexqKCpPOzXK9ZyZZeSLCEFo
+heoAOMTxLDXcTw9h88J7GZ70udGZKinBbT/L9yZimzH3FSwyWbR8gjJxzL84q2qv
+8rLTtXXgjL93uF+1lDcfITgswCKmUvm4uMwPCpved7U+Ni5E5E7FJHEY8MVQQdBn
+QsKhLpIWyi4xTEK1vHVwS3gyL7D9HvxaZInZ5x7tOeqKeNsXOS9zXMdkvhyvfDoM
+tCrq9CwAd/QNkizQ+jyiQP9/Q8xfcrCp4RMHfMTM+c7KkB34w0FYVWg8GOMpWJw5
+5wQ4VpSCxSrc0JC6159vxtLk/CzWk9IqfA7f7mr/7sR8StKEhioyQ2Z45Z3nAh0A
+khAfQfPdW+1ub60zvSsjfOLso36IfT7TbWvhAQKCAQEAg7xfk8S1E+4NKgxypwtv
+vt/FVnfbgbkZ0pkQUrD8bbYZamiNvdMvDauEj16AEwbvUkLdZa5Ht/EcknYimgGG
+305Ah/hvqUayVd9C22s/7JxOyg/WIy1Y6vScDWNO6Svlu9ZU8RmTEXgL9vIekbo1
+hAnni3zmC9/cDOuMWVdkL0vWqirwoY8p2QJ3hJXZTD1pgSQaI9VsnnwU+hmCrhRQ
+QUZf6xcDWe6kpgBnCD3kJu3GDr4IEsA8Q4rGv+Cr4z29Dl7BiSLnJVSVQ9HOSdqv
+E73CYHJbeaWk0ASNBkimeqzY+5lPtqGHoZhDw0V6V9xmE6yA+4RA1lCZSn8aLwoE
+fQQfAh0Ajn5YKCFR13WmbVb52M44GN50U+cJ24RFKPaunA==
+-----END PRIVATE KEY-----";
+ let mut datakey = get_default_datakey(None, None, None);
+ datakey.certificate = certificate.as_bytes().to_vec();
+ datakey.private_key = private_key.as_bytes().to_vec();
+ let _ = X509Plugin::validate_and_update(&mut datakey);
+ if let Some(value) = datakey.attributes.get("key_length") {
+ assert_eq!(value, "2048");
+ } else {
+ panic!("Expected key 'key_length' not found in the map.");
+ }
+
+ if let Some(value) = datakey.attributes.get("key_type") {
+ assert_eq!(value, "dsa");
+ } else {
+ panic!("Expected key 'key_type' not found in the map.");
+ }
+ }
+
+ #[tokio::test]
+ async fn test_sign_cms_with_timestamp_process_successful() {
+ let mut parameter = get_default_parameter();
+ parameter.insert("sign_type".to_string(), "cms".to_string());
+ let content = "hello world".as_bytes();
+ let instance = get_default_plugin().await;
+ let _signature = instance
+ .sign(content.to_vec(), parameter)
+ .expect("sign successfully");
+ }
+
#[tokio::test]
async fn test_sign_whole_process_successful() {
- let parameter = get_default_parameter();
+ let mut parameter = get_default_parameter();
+ parameter.insert("sign_type".to_string(), "kernel-cms".to_string());
let content = "hello world".as_bytes();
let instance = get_default_plugin().await;
let _signature = instance
@@ -1206,7 +1476,7 @@ X5BboR/QJakEK+H+EUQAiDs=
let sec_datakey = SecDataKey::load(&ca_key, &dummy_engine)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully");
let ca_content = plugin
.generate_keys(&KeyType::X509CA, &infra_config)
.expect(format!("generate ca key with no passphrase successfully").as_str());
@@ -1218,7 +1488,7 @@ X5BboR/QJakEK+H+EUQAiDs=
let crl_sec_datakey = SecDataKey::load(&ca_key, &dummy_engine)
.await
.expect("load sec datakey successfully");
- let plugin = X509Plugin::new(crl_sec_datakey).expect("create plugin successfully");
+ let plugin = X509Plugin::new(crl_sec_datakey, None).expect("create plugin successfully");
let revoke_time = Utc::now();
let last_update = Utc::now() + Duration::days(1);
let next_update = Utc::now() + Duration::days(2);
diff --git a/src/presentation/handler/data/sign_handler.rs b/src/presentation/handler/data/sign_handler.rs
index b0f144a..dbd0fa9 100644
--- a/src/presentation/handler/data/sign_handler.rs
+++ b/src/presentation/handler/data/sign_handler.rs
@@ -101,7 +101,7 @@ where
let key_id_or_name = request.key_id.to_string();
return match self
.key_service
- .get_by_type_and_name(request.key_type, request.key_id)
+ .get_by_type_and_name(Some(request.key_type), request.key_id)
.await
{
Ok(datakey) => {
diff --git a/src/util/options.rs b/src/util/options.rs
index 399ceeb..8507037 100644
--- a/src/util/options.rs
+++ b/src/util/options.rs
@@ -19,4 +19,7 @@ pub const KEY_TYPE: &str = "key_type";
pub const SIGN_TYPE: &str = "sign_type";
pub const RPM_V3_SIGNATURE: &str = "rpm_signature_type";
pub const INCLUDE_PARENT_CERT: &str = "include_parent_cert";
+pub const TIMESTAMP_KEY: &str = "timestampe_key";
+pub const TSA_CERT: &str = "tsa_cert";
+pub const TSA_KET: &str = "tsa_key";
pub const CRL: &str = "crl";
diff --git a/src/util/sign.rs b/src/util/sign.rs
index 2ba901f..38a358e 100644
--- a/src/util/sign.rs
+++ b/src/util/sign.rs
@@ -5,6 +5,7 @@ use std::str::FromStr;
#[derive(clap::ValueEnum, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SignType {
Cms, // signed method for a CMS signed data
+ KernelCms, // signed method for a kernel CMS signed data
Authenticode, // signed method for signing EFI image using authenticode spec
PKCS7, // signed method for a pkcs7 signed data
RsaHash, // signed method for a ima eam using rsa hash
@@ -14,6 +15,7 @@ impl Display for SignType {
fn fmt(&self, f: &mut Formatter) -> fmtResult {
match self {
SignType::Cms => write!(f, "cms"),
+ SignType::KernelCms => write!(f, "kernel-cms"),
SignType::Authenticode => write!(f, "authenticode"),
SignType::PKCS7 => write!(f, "pkcs7"),
SignType::RsaHash => write!(f, "rsahash"),
@@ -27,6 +29,7 @@ impl FromStr for SignType {
fn from_str(s: &str) -> Result {
match s {
"cms" => Ok(SignType::Cms),
+ "kernel-cms" => Ok(SignType::KernelCms),
"authenticode" => Ok(SignType::Authenticode),
"pkcs7" => Ok(SignType::PKCS7),
"rsahash" => Ok(SignType::RsaHash),
--
Gitee