From e939f7aaff4a7377c1a1c1625b3c94e23f0a7298 Mon Sep 17 00:00:00 2001
From: unknown <1592085657@qq.com>
Date: Thu, 20 Nov 2025 11:15:55 +0800
Subject: [PATCH 1/2] support generate cms by hash
---
app/src/pages/listShow/ImportX509.vue | 20 -
src/application/datakey.rs | 2 +-
src/client/cmd/add.rs | 9 +-
src/client/file_handler/kernel_module.rs | 19 +-
src/client/file_handler/p7s.rs | 15 +
src/client/worker/signer.rs | 21 +-
src/domain/datakey/repository.rs | 3 +-
.../database/model/datakey/repository.rs | 53 ++-
src/infra/sign_plugin/x509.rs | 403 ++++++++++++++++--
src/presentation/server/control_server.rs | 1 -
src/util/attributes.rs | 24 +-
src/util/sign.rs | 3 +
12 files changed, 511 insertions(+), 62 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 @@
/>
-
-
-
-
-
-
-
-
-
-
Result {
- self.repository.get_all_keys(user_id, query).await
+ self.repository.get_keys_by_condition(user_id, query).await
}
async fn get_one(&self, user: Option, id_or_name: String) -> Result {
diff --git a/src/client/cmd/add.rs b/src/client/cmd/add.rs
index 90e2301..a6ed07a 100644
--- a/src/client/cmd/add.rs
+++ b/src/client/cmd/add.rs
@@ -303,8 +303,13 @@ impl SignCommand for CommandAddHandler {
Err(err) => return Some(err),
};
info!("starting to sign {} files", files.len());
- let mut signer =
- RemoteSigner::new(channel.unwrap(), self.buffer_size, self.token.clone());
+ // key_attributes(digest_algo) need to be obtained when generate cms signature
+ let mut signer = RemoteSigner::new(
+ channel.unwrap(),
+ self.buffer_size,
+ self.token.clone(),
+ key_attributes.clone(),
+ );
//split file
let send_handlers = files
.into_iter()
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..9bddf6c 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,19 @@ 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?;
+ debug!("key_attributes: {:?}", key_attributes);
+ 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/client/worker/signer.rs b/src/client/worker/signer.rs
index 65b32a6..f188b96 100644
--- a/src/client/worker/signer.rs
+++ b/src/client/worker/signer.rs
@@ -18,7 +18,7 @@ use crate::client::file_handler::traits::FileHandler;
use crate::client::sign_identity::SignIdentity;
use crate::client::worker::traits::SignHandler;
use async_trait::async_trait;
-
+use std::collections::HashMap;
pub mod signatrust {
tonic::include_proto!("signatrust");
}
@@ -33,14 +33,21 @@ pub struct RemoteSigner {
client: SignatrustClient,
buffer_size: usize,
token: Option,
+ key_attributes: HashMap,
}
impl RemoteSigner {
- pub fn new(channel: Channel, buffer_size: usize, token: Option) -> Self {
+ pub fn new(
+ channel: Channel,
+ buffer_size: usize,
+ token: Option,
+ key_attributes: HashMap,
+ ) -> Self {
Self {
client: SignatrustClient::new(channel),
buffer_size,
token,
+ key_attributes,
}
}
}
@@ -54,6 +61,14 @@ impl SignHandler for RemoteSigner {
) -> SignIdentity {
let mut signed_content = Vec::new();
let read_data = item.raw_content.borrow().clone();
+ let mut sign_options: HashMap = HashMap::new();
+ for (k, v) in self.key_attributes.iter() {
+ sign_options.insert(k.clone(), (*v).to_string());
+ }
+ for (k, v) in item.sign_options.borrow().iter() {
+ sign_options.insert(k.clone(), (*v).to_string());
+ }
+
for sign_content in read_data.into_iter() {
let mut sign_segments: Vec = Vec::new();
let mut buffer = vec![0; self.buffer_size];
@@ -65,7 +80,7 @@ impl SignHandler for RemoteSigner {
let content = buffer[0..length].to_vec();
sign_segments.push(SignStreamRequest {
data: content,
- options: item.sign_options.borrow().clone(),
+ options: sign_options.clone(),
key_type: format!("{}", item.key_type),
key_id: item.key_id.clone(),
token: self.token.clone(),
diff --git a/src/domain/datakey/repository.rs b/src/domain/datakey/repository.rs
index a2694b0..30f830c 100644
--- a/src/domain/datakey/repository.rs
+++ b/src/domain/datakey/repository.rs
@@ -26,11 +26,12 @@ use chrono::Duration;
pub trait Repository: Send + Sync {
async fn create(&self, data_key: DataKey) -> Result;
async fn delete(&self, id: i32) -> Result<()>;
- async fn get_all_keys(
+ async fn get_keys_by_condition(
&self,
user_id: i32,
query: DatakeyPaginationQuery,
) -> Result;
+ async fn get_all_keys(&self) -> Result;
async fn get_by_id_or_name(
&self,
id: Option,
diff --git a/src/infra/database/model/datakey/repository.rs b/src/infra/database/model/datakey/repository.rs
index bf735c0..6f3f299 100644
--- a/src/infra/database/model/datakey/repository.rs
+++ b/src/infra/database/model/datakey/repository.rs
@@ -205,7 +205,7 @@ impl<'a> Repository for DataKeyRepository<'a> {
Ok(())
}
- async fn get_all_keys(
+ async fn get_keys_by_condition(
&self,
user_id: i32,
query: DatakeyPaginationQuery,
@@ -281,6 +281,57 @@ impl<'a> Repository for DataKeyRepository<'a> {
})
}
+ async fn get_all_keys(&self) -> Result {
+ let results = datakey_dto::Entity::find()
+ .select_only()
+ .columns(datakey_dto::Column::iter().filter(|col| {
+ !matches!(
+ col,
+ datakey_dto::Column::UserEmail
+ | datakey_dto::Column::RequestDeleteUsers
+ | datakey_dto::Column::RequestRevokeUsers
+ | datakey_dto::Column::X509CrlUpdateAt
+ )
+ }))
+ .exprs([
+ Expr::cust("user_table.email as user_email"),
+ Expr::cust("GROUP_CONCAT(request_delete_table.user_email) as request_delete_users"),
+ Expr::cust("GROUP_CONCAT(request_revoke_table.user_email) as request_revoke_users"),
+ ])
+ .join_as_rev(
+ JoinType::InnerJoin,
+ user_dto::Relation::Datakey.def(),
+ Alias::new("user_table"),
+ )
+ .join_as_rev(
+ JoinType::LeftJoin,
+ self.get_pending_operation_relation(RequestType::Delete)
+ .into(),
+ Alias::new("request_delete_table"),
+ )
+ .join_as_rev(
+ JoinType::LeftJoin,
+ self.get_pending_operation_relation(RequestType::Revoke)
+ .into(),
+ Alias::new("request_revoke_table"),
+ )
+ .group_by(datakey_dto::Column::Id)
+ .all(self.db_connection)
+ .await?;
+
+ let mut results_vec = vec![];
+ for dto in results {
+ results_vec.push(DataKey::try_from(dto)?);
+ }
+ let total_numbers = results_vec.len();
+ Ok(PagedDatakey {
+ data: results_vec,
+ meta: PagedMeta {
+ total_count: total_numbers as u64,
+ },
+ })
+ }
+
async fn get_keys_for_crl_update(&self, duration: Duration) -> Result> {
let now = Utc::now();
match datakey_dto::Entity::find()
diff --git a/src/infra/sign_plugin/x509.rs b/src/infra/sign_plugin/x509.rs
index 4aef025..7f37bed 100644
--- a/src/infra/sign_plugin/x509.rs
+++ b/src/infra/sign_plugin/x509.rs
@@ -14,7 +14,24 @@
*
*/
+use super::util::{attributes_validate, validate_utc_time, validate_utc_time_not_expire};
+use crate::domain::datakey::entity::{
+ DataKey, DataKeyContent, KeyType, RevokedKey, SecDataKey, SecParentDateKey,
+ INFRA_CONFIG_DOMAIN_NAME,
+};
+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::util::attributes;
+use crate::util::attributes::PkeyHashAlgo;
+use crate::util::error::{Error, Result};
+use crate::util::key::{decode_hex_string_to_u8, encode_u8_to_hex_string};
+use crate::util::options;
+use crate::util::sign::SignType;
use chrono::{DateTime, Utc};
+#[allow(unused_imports)]
+use enum_iterator::all;
use foreign_types_shared::{ForeignType, ForeignTypeRef};
use openssl::asn1::{Asn1Integer, Asn1Time};
use openssl::bn::{BigNum, MsbOption};
@@ -22,6 +39,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;
@@ -30,33 +48,54 @@ use openssl::x509::extension::{
};
use openssl::x509::{X509Crl, X509Extension};
use openssl_sys::{
- X509_CRL_add0_revoked, X509_CRL_new, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate,
- X509_CRL_set_issuer_name, X509_CRL_sign, X509_REVOKED_new, X509_REVOKED_set_revocationDate,
- X509_REVOKED_set_serialNumber,
+ BIO_free_all, BIO_get_mem_data, BIO_new, BIO_new_mem_buf, BIO_s_mem, CMS_ContentInfo,
+ CMS_ContentInfo_free, CMS_sign, X509_CRL_add0_revoked, X509_CRL_new, X509_CRL_set1_lastUpdate,
+ X509_CRL_set1_nextUpdate, X509_CRL_set_issuer_name, X509_CRL_sign, X509_REVOKED_new,
+ X509_REVOKED_set_revocationDate, X509_REVOKED_set_serialNumber, BIO, CMS_BINARY, CMS_DETACHED, CMS_KEY_PARAM,
+ CMS_NOSMIMECAP, CMS_PARTIAL, EVP_MD, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_CTX_set_rsa_padding, X509, X509_CRL, RSA_PKCS1_PSS_PADDING,
};
use secstr::SecVec;
use serde::Deserialize;
use std::collections::HashMap;
+use std::ffi::CString;
+use std::ffi::{c_char, c_int, c_uchar, c_uint, c_void};
+use std::ptr;
+use std::slice;
use std::str::FromStr;
use std::time::{Duration, SystemTime};
-
-use super::util::{attributes_validate, validate_utc_time, validate_utc_time_not_expire};
-use crate::domain::datakey::entity::{
- DataKey, DataKeyContent, KeyType, RevokedKey, SecDataKey, SecParentDateKey,
- INFRA_CONFIG_DOMAIN_NAME,
-};
-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::util::attributes;
-use crate::util::error::{Error, Result};
-use crate::util::key::{decode_hex_string_to_u8, encode_u8_to_hex_string};
-use crate::util::options;
-use crate::util::sign::SignType;
-#[allow(unused_imports)]
-use enum_iterator::all;
use validator::{Validate, ValidationError};
+#[repr(C)]
+pub struct CMS_SignerInfo {
+ // 根据 OpenSSL 头文件添加字段
+ pub cert: *mut X509,
+ pub pkey: *mut EVP_PKEY,
+ pub md: *const EVP_MD,
+ pub sig: *mut u8,
+ pub siglen: i32,
+}
+
+extern "C" {
+ pub fn CMS_final_digest(
+ cms: *mut CMS_ContentInfo,
+ md: *const c_uchar,
+ mdlen: c_uint,
+ dcont: *mut BIO,
+ flags: c_uint,
+ ) -> c_int;
+
+ pub fn CMS_add1_signer(
+ cms: *mut CMS_ContentInfo,
+ cert: *mut X509,
+ pkey: *mut EVP_PKEY,
+ md: *const EVP_MD,
+ flags: u32,
+ ) -> *mut CMS_SignerInfo;
+ pub fn CMS_SignerInfo_get0_pkey_ctx(si: *mut CMS_SignerInfo) -> *mut EVP_PKEY_CTX;
+ pub fn i2d_CMS_bio(out: *mut BIO, cms: *mut CMS_ContentInfo) -> c_int;
+ pub fn EVP_PKEY_is_a(pkey: *const EVP_PKEY, name: *const c_char) -> c_int;
+ pub fn CMS_add1_crl(cms: *mut CMS_ContentInfo,
+ crl: *mut X509_CRL) -> c_int;
+}
#[derive(Debug, Validate, Deserialize)]
#[validate(schema(function = "validate_x509_key_size_for_generation"))]
@@ -512,6 +551,139 @@ impl X509Plugin {
serial_number: Some(encode_u8_to_hex_string(&serial_number.to_vec())),
})
}
+ // openssl3.0 has adopted a new framework, rust openssl openssl::pkey::Pky::id(EVP_PKEY_get_id()) may not return the traditional NID you expect.
+ // This requires using the C interface EVP_PKEY_is_a to detect key type
+ 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(),
+ ))
+ }
+ }
+ }
+}
+
+fn cms_sign_with_hash(
+ cert: &x509::X509Ref,
+ pkey: &PKey,
+ digest: &[u8],
+ attribute: HashMap,
+) -> Result> {
+ unsafe {
+ let data_bio = BIO_new_mem_buf(digest.as_ptr() as *const c_void, digest.len() as i32);
+ if data_bio.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "BIO_new_mem_buf failed".to_string(),
+ ));
+ }
+
+ struct BioGuard(*mut openssl_sys::BIO);
+ impl Drop for BioGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { BIO_free_all(self.0) };
+ }
+ }
+ }
+ let _data_bio_guard = BioGuard(data_bio);
+
+ let flags = CMS_DETACHED | CMS_BINARY | CMS_PARTIAL | CMS_NOSMIMECAP | CMS_KEY_PARAM;
+ let cms: *mut CMS_ContentInfo = CMS_sign(
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ data_bio,
+ flags,
+ );
+ if cms.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_sign (partial) failed".to_string(),
+ ));
+ }
+
+ struct CmsGuard(*mut CMS_ContentInfo);
+ impl Drop for CmsGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { CMS_ContentInfo_free(self.0) };
+ }
+ }
+ }
+ let _cms_guard = CmsGuard(cms);
+
+ let md = PkeyHashAlgo::get_openssl_c_digest_algo(&attribute);
+ if md.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "EVP_sha256 returned null".to_string(),
+ ));
+ }
+
+ let si = CMS_add1_signer(cms, cert.as_ptr(), pkey.as_ptr(), md, flags);
+ if si.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_add1_signer failed".to_string(),
+ ));
+ }
+
+ let pk_ctx = CMS_SignerInfo_get0_pkey_ctx(si);
+ if pk_ctx.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_SignerInfo_get0_pkey_ctx failed".to_string(),
+ ));
+ }
+ EVP_PKEY_CTX_set_rsa_padding(pk_ctx, RSA_PKCS1_PSS_PADDING);
+
+ let ret = CMS_final_digest(
+ cms,
+ digest.as_ptr(),
+ digest.len() as u32,
+ ptr::null_mut(),
+ flags,
+ );
+ if ret != 1 {
+ return Err(Error::InvalidArgumentError(
+ "CMS_final_digest failed".to_string(),
+ ));
+ }
+
+ let out_bio = BIO_new(BIO_s_mem());
+ if out_bio.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "BIO_new(BIO_s_mem) failed".to_string(),
+ ));
+ }
+ let _out_bio_guard = BioGuard(out_bio);
+
+ if i2d_CMS_bio(out_bio, cms) != 1 {
+ return Err(Error::InvalidArgumentError(
+ "i2d_CMS_bio failed".to_string(),
+ ));
+ }
+
+ let mut ptr: *mut c_char = ptr::null_mut();
+ let len = BIO_get_mem_data(out_bio, &mut ptr) as usize;
+ if len == 0 || ptr.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "BIO_get_mem_data got empty buffer".to_string(),
+ ));
+ }
+ let data_ptr = ptr as *const u8;
+ let buf = slice::from_raw_parts(data_ptr, len).to_vec();
+ Ok(buf)
+ }
}
impl SignPlugins for X509Plugin {
@@ -536,11 +708,22 @@ 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,8 +827,10 @@ impl SignPlugins for X509Plugin {
)?;
Ok(pkcs7.to_der()?)
}
- SignType::Cms => {
+ SignType::KernelCms => {
//cms option reference: https://man.openbsd.org/CMS_sign.3
+ //the cms signature of the kernel module doesn't have the signedAttrs field
+ //it can only send all data to data-server for signing
let cms_signature = CmsContentInfo::sign(
Some(&certificate),
Some(&private_key),
@@ -659,6 +844,12 @@ impl SignPlugins for X509Plugin {
)?;
Ok(cms_signature.to_der()?)
}
+ SignType::Cms => {
+ // common cms signature have the signedAttrs field
+ // after calculating the hash on the client side, it is sent to the server for signing
+ cms_sign_with_hash(&certificate, &private_key, &content, options)
+ }
+
SignType::RsaHash => {
// rust-openssl/openssl/src/pkey_ctx.rs
let mut signature = vec![];
@@ -1112,6 +1303,170 @@ mod test {
.expect(format!("generate ca key with passphrase successfully").as_str());
}
+ #[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.");
+ }
+ }
+
#[test]
fn test_validate_and_update() {
let public_key = "-----BEGIN PUBLIC KEY-----
diff --git a/src/presentation/server/control_server.rs b/src/presentation/server/control_server.rs
index 6311b62..36cc9bb 100644
--- a/src/presentation/server/control_server.rs
+++ b/src/presentation/server/control_server.rs
@@ -183,7 +183,6 @@ impl ControlServer {
.get_string("control-server.server_port")?
.parse()?,
)?;
-
//prepare redis store
let store = RedisSessionStore::new(&redis_connection).await?;
let limiter = web::Data::new(
diff --git a/src/util/attributes.rs b/src/util/attributes.rs
index 35c34a4..199ee0b 100644
--- a/src/util/attributes.rs
+++ b/src/util/attributes.rs
@@ -21,7 +21,9 @@ use openssl::md::Md;
use openssl::pkey::PKey;
use openssl::pkey_ctx::PkeyCtx;
use openssl::rsa::Rsa;
-
+use openssl_sys::{
+ EVP_md5, EVP_sha1, EVP_sha224, EVP_sha256, EVP_sha384, EVP_sha512, EVP_sm3, EVP_MD,
+};
pub const DIGEST_ALGO: &str = "digest_algorithm";
#[repr(u8)]
@@ -84,6 +86,26 @@ impl PkeyHashAlgo {
digest_algo
}
+ pub fn get_openssl_c_digest_algo(attributes: &HashMap) -> *const EVP_MD {
+ unsafe {
+ let digest_algo = match attributes
+ .get(DIGEST_ALGO)
+ .expect("get algo failed")
+ .as_str()
+ {
+ "md5" => EVP_md5(),
+ "sha1" => EVP_sha1(),
+ "sha2_224" => EVP_sha224(),
+ "sha2_256" => EVP_sha256(),
+ "sha2_384" => EVP_sha384(),
+ "sha2_512" => EVP_sha512(),
+ "sm3" => EVP_sm3(),
+ _ => EVP_sha256(),
+ };
+ digest_algo
+ }
+ }
+
pub fn to_u8(self) -> u8 {
self as u8
}
diff --git a/src/util/sign.rs b/src/util/sign.rs
index 2ba901f..38070ae 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 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
From f67ed80ecf70bf0a4531bb0dae1ee317da4b65be Mon Sep 17 00:00:00 2001
From: unknown <1592085657@qq.com>
Date: Tue, 2 Dec 2025 14:45:50 +0800
Subject: [PATCH 2/2] support timeStamp
---
scripts/initialize-user-and-keys.sh | 6 +-
src/application/datakey.rs | 33 +-
src/client/cmd/add.rs | 24 +
src/domain/datakey/repository.rs | 2 +-
src/domain/sign_plugin.rs | 2 +-
src/domain/sign_service.rs | 2 +
.../database/model/datakey/repository.rs | 20 +-
src/infra/sign_backend/memory/backend.rs | 18 +-
src/infra/sign_plugin/cms.rs | 851 ++++++++++++++++++
src/infra/sign_plugin/mod.rs | 1 +
src/infra/sign_plugin/openpgp.rs | 2 +-
src/infra/sign_plugin/signers.rs | 5 +-
src/infra/sign_plugin/x509.rs | 208 ++---
src/presentation/handler/data/sign_handler.rs | 2 +-
src/util/attributes.rs | 7 +-
src/util/options.rs | 4 +
16 files changed, 1001 insertions(+), 186 deletions(-)
create mode 100644 src/infra/sign_plugin/cms.rs
diff --git a/scripts/initialize-user-and-keys.sh b/scripts/initialize-user-and-keys.sh
index 5aa974f..5660662 100755
--- a/scripts/initialize-user-and-keys.sh
+++ b/scripts/initialize-user-and-keys.sh
@@ -40,19 +40,19 @@ function create_default_x509_ee {
function create_default_x509_ca_sm2 {
echo "start to create default x509_sm2 CA identified with default-x509ca-sm2"
- RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ca-sm2 --description "used for test purpose only" --key-type x509ca --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 2048 \
+ RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ca-sm2 --description "used for test purpose only" --key-type x509ca --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 256 \
--param-x509-common-name Infra --param-x509-organization Huawei --param-x509-locality ShenZhen --param-x509-province-name GuangDong --param-x509-country-name CN --param-x509-organizational-unit "Infra CA" --digest-algorithm sm3 --visibility public
}
function create_default_x509_ia_sm2 {
echo "start to create default x509 sm2 ICA identified with default-x509"
- RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ica-sm2 --description "used for test purpose only" --key-type x509ica --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 2048 \
+ RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ica-sm2 --description "used for test purpose only" --key-type x509ica --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 256 \
--param-x509-common-name Infra --param-x509-organization Huawei --param-x509-locality ShenZhen --param-x509-province-name GuangDong --param-x509-country-name CN --param-x509-organizational-unit "Infra ICA" --digest-algorithm sm3 --param-x509-parent-name default-x509ca-sm2 --visibility public
}
function create_default_x509_ee_sm2 {
echo "start to create default x509 sm2 EE certificate identified with default-x509"
- RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ee-sm2 --description "used for test purpose only" --key-type x509ee --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 2048 \
+ RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ee-sm2 --description "used for test purpose only" --key-type x509ee --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 256 \
--param-x509-common-name Infra --param-x509-organization Huawei --param-x509-locality ShenZhen --param-x509-province-name GuangDong --param-x509-country-name CN --param-x509-organizational-unit "Infra EE" --digest-algorithm sm3 --param-x509-parent-name default-x509ica-sm2 --visibility public
}
diff --git a/src/application/datakey.rs b/src/application/datakey.rs
index 19f9106..52b8ad3 100644
--- a/src/application/datakey.rs
+++ b/src/application/datakey.rs
@@ -21,6 +21,7 @@ use crate::domain::datakey::entity::{
use crate::domain::datakey::repository::Repository as DatakeyRepository;
use crate::domain::sign_service::SignBackend;
use crate::util::error::{Error, Result};
+use crate::util::options;
use async_trait::async_trait;
use tokio::time::{self};
@@ -69,8 +70,8 @@ pub trait KeyService: Send + Sync {
options: &HashMap,
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 +91,7 @@ where
repository: R,
sign_service: Arc>>,
container: TimedFixedSizeCache,
+ timestamp_container: TimedFixedSizeCache,
}
impl DBKeyService
@@ -102,6 +104,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 +541,21 @@ 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 +569,20 @@ 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 a6ed07a..f911427 100644
--- a/src/client/cmd/add.rs
+++ b/src/client/cmd/add.rs
@@ -23,6 +23,7 @@ use regex::Regex;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{atomic::AtomicBool, Arc, RwLock};
+use std::fs;
use tokio::runtime;
use crate::client::file_handler::factory::FileHandlerFactory;
@@ -88,6 +89,12 @@ 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 +113,8 @@ pub struct CommandAddHandler {
sign_type: SignType,
token: Option,
rpm_v3: bool,
+ timestamp_key: String,
+ crl: String,
sign_options: Option>,
}
@@ -120,6 +129,8 @@ 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 +234,17 @@ 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 +260,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,
})
}
diff --git a/src/domain/datakey/repository.rs b/src/domain/datakey/repository.rs
index 30f830c..28afabc 100644
--- a/src/domain/datakey/repository.rs
+++ b/src/domain/datakey/repository.rs
@@ -42,7 +42,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 6f3f299..8f18209 100644
--- a/src/infra/database/model/datakey/repository.rs
+++ b/src/infra/database/model/datakey/repository.rs
@@ -627,9 +627,17 @@ 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()
+ .add(datakey_dto::Column::Name.eq(name))
+ .add(datakey_dto::Column::KeyState.eq(KeyState::Enabled.to_string()));
+
+ if let Some(t) = key_type {
+ cond = cond.add(datakey_dto::Column::KeyType.eq(t));
+ }
match datakey_dto::Entity::find()
.select_only()
.columns(datakey_dto::Column::iter().filter(|col| {
@@ -641,14 +649,10 @@ 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?
+
{
None => Err(Error::NotFoundError),
Some(datakey) => {
@@ -1503,7 +1507,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..7779dd8 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> {
+ info!("staring!!!");
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
new file mode 100644
index 0000000..72f55fd
--- /dev/null
+++ b/src/infra/sign_plugin/cms.rs
@@ -0,0 +1,851 @@
+use crate::util::error::{Result, Error};
+use crate::util::attributes;
+use crate::util::attributes::PkeyHashAlgo;
+use crate::util::options;
+use foreign_types_shared::{ForeignType, ForeignTypeRef};
+use openssl_sys::{
+ ASN1_INTEGER_free, ASN1_OBJECT_free, BIO_free_all, BIO_get_mem_data, BIO_new,
+ BIO_new_mem_buf, BIO_s_mem, CMS_ContentInfo, CMS_ContentInfo_free, CMS_sign, EVP_MD_type,
+ EVP_PKEY_CTX_set_rsa_padding, OBJ_nid2obj, OBJ_txt2obj, X509_ALGOR_free,
+ ASN1_BOOLEAN, ASN1_GENERALIZEDTIME, ASN1_INTEGER, ASN1_OBJECT,
+ ASN1_OCTET_STRING, BIO, CMS_BINARY, CMS_DETACHED, CMS_KEY_PARAM, CMS_NOSMIMECAP, CMS_PARTIAL,
+ EVP_MD, EVP_PKEY, EVP_PKEY_CTX, GENERAL_NAME, RSA_PKCS1_PSS_PADDING, V_ASN1_NULL,
+ V_ASN1_SEQUENCE, X509, X509_ALGOR, X509_CRL, ERR_get_error,
+};
+use openssl::x509;
+use openssl::pkey;
+use rand::rngs::OsRng;
+use rand::Rng;
+use std::collections::HashMap;
+use std::ffi::CString;
+use std::ffi::{c_char, c_int, c_uchar, c_uint, c_void};
+use std::ptr;
+use std::slice;
+use std::time::{SystemTime, UNIX_EPOCH};
+
+#[repr(C)]
+pub struct CMS_SignerInfo {
+ cert: *mut X509,
+ pkey: *mut EVP_PKEY,
+ md: *const EVP_MD,
+ sig: *mut u8,
+ siglen: i32,
+ }
+
+#[repr(C)]
+pub struct TS_MSG_IMPRINT {
+ algo: *mut X509_ALGOR,
+ hash: *mut ASN1_OCTET_STRING,
+}
+
+#[repr(C)]
+pub struct TS_REQ {
+ version: *mut ASN1_INTEGER,
+ tst: *mut TS_MSG_IMPRINT,
+ policy_id: *mut ASN1_OBJECT,
+ nonce: *mut ASN1_INTEGER,
+ cert_req: *mut ASN1_BOOLEAN,
+ extensions: *mut c_void,
+}
+
+#[repr(C)]
+pub struct TS_TST_INFO {
+ version: *mut ASN1_INTEGER,
+ policy_id: *mut ASN1_OBJECT,
+ tst: *mut TS_MSG_IMPRINT,
+ serial: *mut ASN1_INTEGER,
+ time: *mut ASN1_GENERALIZEDTIME,
+ accuracy: *mut c_void,
+ ordering: *mut ASN1_BOOLEAN,
+ nonce: *mut ASN1_INTEGER,
+ tsa: *mut GENERAL_NAME,
+ extensions: *mut c_void,
+}
+
+extern "C" {
+ pub fn ASN1_INTEGER_new() -> *mut ASN1_INTEGER;
+ pub fn ASN1_INTEGER_set_uint64(a: *mut ASN1_INTEGER, r: u64) -> i32;
+ pub fn ASN1_GENERALIZEDTIME_new() -> *mut ASN1_GENERALIZEDTIME;
+ pub fn ASN1_GENERALIZEDTIME_free(time: *mut ASN1_GENERALIZEDTIME);
+ pub fn ASN1_GENERALIZEDTIME_set(
+ s: *mut ASN1_GENERALIZEDTIME,
+ t: i64,
+ ) -> *mut ASN1_GENERALIZEDTIME;
+ pub fn ASN1_STRING_data(octet_str: *mut c_void) -> *const u8;
+ pub fn ASN1_STRING_length(octet_str: *mut c_void) -> i32;
+ pub fn CMS_final_digest(
+ cms: *mut CMS_ContentInfo,
+ md: *const c_uchar,
+ mdlen: c_uint,
+ dcont: *mut BIO,
+ flags: c_uint,
+ ) -> c_int;
+ pub fn CMS_final(
+ cms: *mut CMS_ContentInfo,
+ bio: *mut BIO,
+ dcont: *mut BIO,
+ flags: c_uint,
+ ) -> c_int;
+ pub fn CMS_add1_signer(
+ cms: *mut CMS_ContentInfo,
+ cert: *mut X509,
+ pkey: *mut EVP_PKEY,
+ md: *const EVP_MD,
+ flags: u32,
+ ) -> *mut CMS_SignerInfo;
+ pub fn CMS_unsigned_add1_attr_by_NID(
+ si: *mut CMS_SignerInfo,
+ nid: i32,
+ attr_type: i32,
+ bytes: *const c_void,
+ len: i32,
+ ) -> i32;
+ pub fn CMS_get0_SignerInfos(cms: *mut CMS_ContentInfo) -> *mut c_void;
+ pub fn CMS_add1_crl(cms: *mut CMS_ContentInfo, crl: *mut X509_CRL) -> c_int;
+ pub fn CMS_SignerInfo_get0_signature(si: *mut c_void) -> *mut c_void;
+ pub fn CMS_SignerInfo_get0_pkey_ctx(si: *mut CMS_SignerInfo) -> *mut EVP_PKEY_CTX;
+ pub fn CMS_set1_eContentType(cms: *mut CMS_ContentInfo, oid: *mut ASN1_OBJECT) -> i32;
+ pub fn i2d_CMS_bio(out: *mut BIO, cms: *mut CMS_ContentInfo) -> c_int;
+ pub fn i2d_CMS_ContentInfo(cms: *mut CMS_ContentInfo, out: *mut *mut u8) -> c_int;
+ pub fn OPENSSL_sk_num(stack: *const c_void) -> i32;
+ pub fn OPENSSL_sk_value(stack: *const c_void, idx: i32) -> *mut c_void;
+ pub fn free(ptr: *mut c_void);
+ pub fn EVP_PKEY_is_a(key: *const EVP_PKEY, name: *const c_char) -> c_int;
+
+ pub fn TS_REQ_new() -> *mut TS_REQ;
+ pub fn TS_REQ_free(req: *mut TS_REQ);
+ pub fn TS_REQ_set_version(req: *mut TS_REQ, version: i32) -> i32;
+ pub fn TS_MSG_IMPRINT_new() -> *mut TS_MSG_IMPRINT;
+ pub fn TS_MSG_IMPRINT_free(imprint: *mut TS_MSG_IMPRINT);
+ pub fn TS_MSG_IMPRINT_dup(imprint: *mut TS_MSG_IMPRINT) -> *mut TS_MSG_IMPRINT;
+ pub fn TS_MSG_IMPRINT_set_algo(imprint: *mut TS_MSG_IMPRINT, algo: *mut X509_ALGOR) -> i32;
+ pub fn TS_MSG_IMPRINT_set_msg(imprint: *mut TS_MSG_IMPRINT, msg: *const u8, len: i32) -> i32;
+ pub fn TS_REQ_set_msg_imprint(req: *mut TS_REQ, imprint: *mut TS_MSG_IMPRINT) -> i32;
+ pub fn TS_REQ_get_msg_imprint(req: *mut TS_REQ) -> *mut TS_MSG_IMPRINT;
+ pub fn TS_REQ_set_cert_req(req: *mut TS_REQ, cert_req: i32) -> i32;
+
+ pub fn TS_TST_INFO_free(info: *mut TS_TST_INFO);
+ pub fn TS_TST_INFO_new() -> *mut TS_TST_INFO;
+ pub fn TS_TST_INFO_set_version(tst: *mut TS_TST_INFO, version: i32) -> i32;
+ pub fn TS_TST_INFO_set_policy_id(tst: *mut TS_TST_INFO, policy: *mut ASN1_OBJECT) -> i32;
+ pub fn TS_TST_INFO_set_msg_imprint(tst: *mut TS_TST_INFO, msg: *mut TS_MSG_IMPRINT) -> i32;
+ pub fn TS_TST_INFO_set_serial(tst: *mut TS_TST_INFO, serial: *mut ASN1_INTEGER) -> i32;
+ pub fn TS_TST_INFO_set_time(tst: *mut TS_TST_INFO, gen_time: *mut ASN1_GENERALIZEDTIME) -> i32;
+ pub fn TS_TST_INFO_set_ordering(tst: *mut TS_TST_INFO, ordering: i32) -> i32;
+ pub fn TS_TST_INFO_set_tsa(tst: *mut TS_TST_INFO, tsa_name: *mut GENERAL_NAME) -> i32;
+ pub fn i2d_TS_TST_INFO(tst: *mut TS_TST_INFO, out: *mut *mut u8) -> i32;
+
+ pub fn X509_ALGOR_new() -> *mut X509_ALGOR;
+ pub fn X509_ALGOR_set0(
+ alg: *mut X509_ALGOR,
+ obj: *mut ASN1_OBJECT,
+ algo_type: i32,
+ val: *mut c_void,
+ ) -> i32;
+}
+
+unsafe fn drain_err_stack() {
+ loop {
+ let code = ERR_get_error();
+ if code == 0 { break; }
+ info!("openssl code=0x{:08x}", code);
+ }
+}
+
+fn generate_cms_with_hash(
+ cert: &x509::X509Ref,
+ pkey: &pkey::PKey,
+ digest: &[u8],
+ attributes: HashMap,
+) -> Result<*mut CMS_ContentInfo> {
+ struct BioGuard(*mut BIO);
+ impl Drop for BioGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { BIO_free_all(self.0) };
+ }
+ }
+ }
+
+ struct CmsGuard(*mut CMS_ContentInfo);
+ impl Drop for CmsGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { CMS_ContentInfo_free(self.0) };
+ }
+ }
+ }
+ unsafe {
+ // step1. generate cms structure
+ let data_bio = BIO_new_mem_buf(
+ digest.as_ptr() as *const c_void,
+ digest.len()
+ .try_into()
+ .map_err(|_| Error::InvalidArgumentError("digest too large".to_string()))?,
+ );
+ let _data_bio_guard = BioGuard(data_bio);
+ let flags = CMS_DETACHED | CMS_BINARY | CMS_PARTIAL | CMS_NOSMIMECAP | CMS_KEY_PARAM;
+ let cms: *mut CMS_ContentInfo = CMS_sign(
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ data_bio,
+ flags,
+ );
+ if cms.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_sign (partial) failed".to_string(),
+ ));
+ }
+ let mut cms_guard = CmsGuard(cms);
+ // step2. specify the hash algorithm used, certificates, CRL and encryption
+ let digest_algo = attributes
+ .get(attributes::DIGEST_ALGO)
+ .ok_or_else(|| {
+ Error::InvalidArgumentError("missing digest algorithm attribute".to_string())
+ })?;
+ let md = PkeyHashAlgo::get_openssl_c_digest_algo(digest_algo);
+ let si = CMS_add1_signer(cms, cert.as_ptr(), pkey.as_ptr(), md, flags);
+ if si.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_add1_signer failed".to_string(),
+ ));
+ }
+ if let Some(crl_data) = attributes.get(options::CRL).filter(|s| !s.is_empty()) {
+ let crl = x509::X509Crl::from_pem(crl_data.as_bytes())?;
+ CMS_add1_crl(cms, crl.as_ptr());
+ }
+ let pk_ctx = CMS_SignerInfo_get0_pkey_ctx(si);
+ if pk_ctx.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_SignerInfo_get0_pkey_ctx failed".to_string(),
+ ));
+ }
+ EVP_PKEY_CTX_set_rsa_padding(pk_ctx, RSA_PKCS1_PSS_PADDING);
+ // step3. generate signature
+ let ret = CMS_final_digest(
+ cms,
+ digest.as_ptr(),
+ digest.len() as u32,
+ ptr::null_mut(),
+ flags,
+ );
+ if ret != 1 {
+ drain_err_stack();
+ return Err(Error::InvalidArgumentError(
+ "CMS_final_digest failed".to_string(),
+ ));
+ }
+ let cms_ptr = cms_guard.0;
+ cms_guard.0 = ptr::null_mut();
+ Ok(cms_ptr)
+ }
+}
+
+fn generate_timestamp_req(
+ cms: *mut CMS_ContentInfo,
+ attributes: HashMap,
+) -> Result<*mut TS_REQ> {
+ struct TsGuard(*mut TS_REQ);
+ impl Drop for TsGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { TS_REQ_free(self.0) };
+ }
+ }
+ }
+
+ struct MsgImprintGuard(*mut TS_MSG_IMPRINT);
+ impl Drop for MsgImprintGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { TS_MSG_IMPRINT_free(self.0) };
+ }
+ }
+ }
+
+ struct AlgoGuard(*mut X509_ALGOR);
+ impl Drop for AlgoGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { X509_ALGOR_free(self.0) };
+ }
+ }
+ }
+
+ unsafe {
+ // step1. get signature from cms
+ let signatures = CMS_get0_SignerInfos(cms);
+ if signatures.is_null() {
+ return Err(Error::RemoteSignError(
+ "CMS_get0_SignerInfos failed".to_string(),
+ ));
+ }
+
+ let count = OPENSSL_sk_num(signatures);
+ if count <= 0 {
+ return Err(Error::RemoteSignError(
+ "no signer in CMS".to_string(),
+ ));
+ }
+
+ let si = OPENSSL_sk_value(signatures, 0);
+ if si.is_null() {
+ return Err(Error::RemoteSignError(
+ "OPENSSL_sk_value returned null signer".to_string(),
+ ));
+ }
+
+ let signature = CMS_SignerInfo_get0_signature(si);
+ if signature.is_null() {
+ return Err(Error::RemoteSignError(
+ "CMS_SignerInfo_get0_signature failed".to_string(),
+ ));
+ }
+ let data_len = ASN1_STRING_length(signature);
+ let data_ptr = ASN1_STRING_data(signature);
+ if data_len <= 0 || data_ptr.is_null() {
+ return Err(Error::RemoteSignError(
+ "invalid signature ASN1 string".to_string(),
+ ));
+ }
+
+ // step2. generate ts_req from signature
+ let ts_req = TS_REQ_new();
+ let mut ts_guard = TsGuard(ts_req);
+
+ if TS_REQ_set_version(ts_req, 1) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_REQ_set_version failed".to_string(),
+ ));
+ }
+
+ let msg_imprint = TS_MSG_IMPRINT_new();
+ let _msg_guard = MsgImprintGuard(msg_imprint);
+ let algo = X509_ALGOR_new();
+ let _algo_guard = AlgoGuard(algo);
+
+ let digest_algo = attributes
+ .get(attributes::DIGEST_ALGO)
+ .ok_or_else(|| {
+ Error::RemoteSignError("missing digest algorithm attribute".to_string())
+ })?;
+
+ let md = PkeyHashAlgo::get_openssl_c_digest_algo(digest_algo);
+ let nid = EVP_MD_type(md);
+ let obj = OBJ_nid2obj(nid);
+ X509_ALGOR_set0(algo, obj, V_ASN1_NULL, ptr::null_mut());
+
+ if TS_MSG_IMPRINT_set_algo(msg_imprint, algo) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_MSG_IMPRINT_set_algo failed".to_string(),
+ ));
+ }
+
+ if TS_MSG_IMPRINT_set_msg(
+ msg_imprint,
+ data_ptr,
+ data_len,
+ ) != 1
+ {
+ return Err(Error::RemoteSignError(
+ "TS_MSG_IMPRINT_set_msg failed".to_string(),
+ ));
+ }
+
+ if TS_REQ_set_msg_imprint(ts_req, msg_imprint) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_REQ_set_msg_imprint failed".to_string(),
+ ));
+ }
+
+ let ts_ptr = ts_guard.0;
+ ts_guard.0 = ptr::null_mut();
+ Ok(ts_ptr)
+ }
+}
+
+fn generate_timestamp_tst(
+ req: *mut TS_REQ,
+ tsa_cert: &x509::X509Ref,
+) -> Result<*mut TS_TST_INFO> {
+ let _ = tsa_cert;
+ struct TstGuard(*mut TS_TST_INFO);
+ impl Drop for TstGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { TS_TST_INFO_free(self.0) };
+ }
+ }
+ }
+
+ struct Asn1IntGuard(*mut ASN1_INTEGER);
+ impl Drop for Asn1IntGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { ASN1_INTEGER_free(self.0) };
+ }
+ }
+ }
+
+ struct GenTimeGuard(*mut ASN1_GENERALIZEDTIME);
+ impl Drop for GenTimeGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { ASN1_GENERALIZEDTIME_free(self.0) };
+ }
+ }
+ }
+
+ struct Asn1ObjGuard(*mut ASN1_OBJECT);
+ impl Drop for Asn1ObjGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { ASN1_OBJECT_free(self.0) };
+ }
+ }
+ }
+
+ if req.is_null() {
+ return Err(Error::RemoteSignError("TS_REQ is null".to_string()));
+ }
+
+ unsafe {
+ let tst = TS_TST_INFO_new();
+ let mut tst_guard = TstGuard(tst);
+ if TS_TST_INFO_set_version(tst, 1) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_TST_INFO_set_version failed".to_string(),
+ ));
+ }
+
+ let policy_str = CString::new("1.2.3.4.1")
+ .map_err(|_| Error::RemoteSignError("invalid policy OID".to_string()))?;
+ let policy = OBJ_txt2obj(policy_str.as_ptr(), 1);
+ let _policy_guard = Asn1ObjGuard(policy);
+
+ if TS_TST_INFO_set_policy_id(tst, policy) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_TST_INFO_set_policy_id failed".to_string(),
+ ));
+ }
+
+
+ let msg_imprint = TS_REQ_get_msg_imprint(req);
+ if msg_imprint.is_null() {
+ return Err(Error::RemoteSignError(
+ "TS_REQ_get_msg_imprint failed".to_string(),
+ ));
+ }
+
+ let msg_imprint_copy = TS_MSG_IMPRINT_dup(msg_imprint);
+ if msg_imprint_copy.is_null() {
+ return Err(Error::RemoteSignError(
+ "TS_MSG_IMPRINT_dup failed".to_string(),
+ ));
+ }
+
+ if TS_TST_INFO_set_msg_imprint(tst, msg_imprint_copy) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_TST_INFO_set_msg_imprint failed".to_string(),
+ ));
+ }
+
+ let serial = ASN1_INTEGER_new();
+ let _serial_guard = Asn1IntGuard(serial);
+
+ let random = OsRng.gen();
+
+ if ASN1_INTEGER_set_uint64(serial, random) != 1 {
+ return Err(Error::RemoteSignError(
+ "ASN1_INTEGER_set_uint64 failed".to_string(),
+ ));
+ }
+
+ if TS_TST_INFO_set_serial(tst, serial) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_TST_INFO_set_serial failed".to_string(),
+ ));
+ }
+
+ let gen_time = ASN1_GENERALIZEDTIME_new();
+ let _gen_time_guard = GenTimeGuard(gen_time);
+
+ let now = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .map_err(|_| {
+ Error::RemoteSignError("system time before UNIX_EPOCH".to_string())
+ })?
+ .as_secs() as i64;
+
+ if ASN1_GENERALIZEDTIME_set(gen_time, now).is_null() {
+ return Err(Error::RemoteSignError(
+ "ASN1_GENERALIZEDTIME_set failed".to_string(),
+ ));
+ }
+
+ if TS_TST_INFO_set_time(tst, gen_time) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_TST_INFO_set_time failed".to_string(),
+ ));
+ }
+
+ if TS_TST_INFO_set_ordering(tst, 1) != 1 {
+ return Err(Error::RemoteSignError(
+ "TS_TST_INFO_set_ordering failed".to_string(),
+ ));
+ }
+
+ let tst_ptr = tst_guard.0;
+ tst_guard.0 = ptr::null_mut();
+ Ok(tst_ptr)
+ }
+}
+
+fn generate_timestamp_signature(
+ tsa_cert: &x509::X509Ref,
+ tsa_key: &pkey::PKey,
+ tst_info: *mut TS_TST_INFO,
+ attributes: HashMap,
+) -> Result<*mut CMS_ContentInfo> {
+ struct BioGuard(*mut BIO);
+ impl Drop for BioGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { BIO_free_all(self.0) };
+ }
+ }
+ }
+
+ struct CmsGuard(*mut CMS_ContentInfo);
+ impl Drop for CmsGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { CMS_ContentInfo_free(self.0) };
+ }
+ }
+ }
+
+ struct Asn1ObjGuard(*mut ASN1_OBJECT);
+ impl Drop for Asn1ObjGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { ASN1_OBJECT_free(self.0) };
+ }
+ }
+ }
+
+ if tst_info.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "TS_TST_INFO is null".to_string(),
+ ));
+ }
+
+ unsafe {
+ let len: c_int = i2d_TS_TST_INFO(tst_info, ptr::null_mut());
+ if len <= 0 {
+ return Err(Error::InvalidArgumentError(
+ "i2d_TS_TST_INFO (size calc) failed".to_string(),
+ ));
+ }
+
+ let len_usize: usize = len
+ .try_into()
+ .map_err(|_| Error::InvalidArgumentError("TST_INFO too large".to_string()))?;
+
+ let mut tst_der = vec![0u8; len_usize];
+
+ let mut p: *mut u8 = tst_der.as_mut_ptr();
+ let written = i2d_TS_TST_INFO(tst_info, &mut p);
+ if written != len {
+ return Err(Error::InvalidArgumentError(
+ "i2d_TS_TST_INFO (encode) failed".to_string(),
+ ));
+ }
+
+ let content = BIO_new_mem_buf(
+ tst_der.as_ptr() as *const c_void,
+ tst_der
+ .len()
+ .try_into()
+ .map_err(|_| Error::InvalidArgumentError("DER too large".to_string()))?,
+ );
+ if content.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "BIO_new_mem_buf failed".to_string(),
+ ));
+ }
+ let _bio_guard = BioGuard(content);
+
+ let flags: c_uint =
+ (CMS_BINARY | CMS_PARTIAL | CMS_KEY_PARAM | CMS_NOSMIMECAP) as c_uint;
+
+ let cms = CMS_sign(
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ flags,
+ );
+ if cms.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_sign (partial) failed".to_string(),
+ ));
+ }
+ let mut cms_guard = CmsGuard(cms);
+ let digest_algo = attributes
+ .get(attributes::DIGEST_ALGO)
+ .ok_or_else(|| {
+ Error::InvalidArgumentError(
+ "missing digest algorithm attribute".to_string(),
+ )
+ })?;
+
+ let md = PkeyHashAlgo::get_openssl_c_digest_algo(digest_algo);
+
+ let si: *mut CMS_SignerInfo =
+ CMS_add1_signer(cms, tsa_cert.as_ptr(), tsa_key.as_ptr(), md, flags);
+ if si.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_add1_signer failed".to_string(),
+ ));
+ }
+
+ let pctx: *mut EVP_PKEY_CTX = CMS_SignerInfo_get0_pkey_ctx(si);
+ if pctx.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "CMS_SignerInfo_get0_pkey_ctx failed".to_string(),
+ ));
+ }
+ EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING);
+
+ let oid_str = CString::new("1.2.840.113549.1.9.16.1.4")
+ .map_err(|_| Error::InvalidArgumentError("invalid OID string".to_string()))?;
+ let tst_oid: *mut ASN1_OBJECT = OBJ_txt2obj(oid_str.as_ptr(), 1);
+ if tst_oid.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "OBJ_txt2obj failed".to_string(),
+ ));
+ }
+ let _oid_guard = Asn1ObjGuard(tst_oid);
+
+ if CMS_set1_eContentType(cms, tst_oid) != 1 {
+ return Err(Error::InvalidArgumentError(
+ "CMS_set1_eContentType failed".to_string(),
+ ));
+ }
+
+ if CMS_final(cms, content, ptr::null_mut(), flags) != 1 {
+ return Err(Error::InvalidArgumentError(
+ "CMS_final failed".to_string(),
+ ));
+ }
+
+ let cms_ptr = cms_guard.0;
+ cms_guard.0 = ptr::null_mut();
+ Ok(cms_ptr)
+ }
+}
+
+fn attach_timestamp_to_cms(
+ cms: *mut CMS_ContentInfo,
+ ts_token: *mut CMS_ContentInfo,
+) -> Result<()> {
+ struct DerGuard(*mut u8);
+ impl Drop for DerGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { free(self.0 as *mut c_void) };
+ }
+ }
+ }
+
+ unsafe {
+ let signers = CMS_get0_SignerInfos(cms);
+ let signer_info = OPENSSL_sk_value(signers, 0);
+ if signer_info.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "Failed to get SignerInfo from CMS.".to_string(),
+ ));
+ }
+
+ let mut ts_der: *mut u8 = ptr::null_mut();
+ let ts_len = i2d_CMS_ContentInfo(ts_token, &mut ts_der);
+ if ts_len <= 0 || ts_der.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "Failed to convert TS token to DER format.".to_string(),
+ ));
+ }
+
+ let _der_guard = DerGuard(ts_der);
+ let nid = 225;
+ let result = CMS_unsigned_add1_attr_by_NID(
+ signer_info as *mut CMS_SignerInfo,
+ nid,
+ V_ASN1_SEQUENCE,
+ ts_der as *mut _,
+ ts_len,
+ );
+
+ if result != 1 {
+ return Err(Error::InvalidArgumentError(
+ "Failed to add timestamp token to CMS.".to_string(),
+ ));
+ }
+ Ok(())
+ }
+}
+
+
+pub struct CmsContext<'a> {
+ certificate: &'a x509::X509Ref,
+ private_key: &'a pkey::PKey,
+ content: &'a [u8],
+ options: &'a HashMap,
+ pub cms: *mut CMS_ContentInfo,
+
+ // timpstamp
+ ts_req: *mut TS_REQ,
+ tst_info: *mut TS_TST_INFO,
+ timestamp: *mut CMS_ContentInfo,
+ timestamp_options: &'a HashMap,
+ tsa_cert_pem: &'a [u8],
+ tsa_key_pem: &'a [u8],
+ tsa_cert: Option,
+ tsa_key: Option>,
+}
+
+impl<'a> CmsContext<'a> {
+ pub fn new(
+ certificate: &'a x509::X509Ref,
+ private_key: &'a pkey::PKey,
+ content: &'a [u8],
+ options: &'a HashMap,
+ timestamp_options: &'a HashMap,
+ tsa_cert_pem: &'a [u8],
+ tsa_key_pem: &'a [u8],
+ ) -> Self {
+ Self {
+ certificate,
+ private_key,
+ content,
+ options,
+ cms: std::ptr::null_mut(),
+ ts_req: std::ptr::null_mut(),
+ tst_info: std::ptr::null_mut(),
+ timestamp: std::ptr::null_mut(),
+ timestamp_options,
+ tsa_cert_pem,
+ tsa_key_pem,
+ tsa_cert: None,
+ tsa_key: None,
+ }
+ }
+}
+
+pub type Step<'a> = &'a dyn Fn(&mut CmsContext) -> Result<()>;
+
+pub struct CmsPlugin;
+impl CmsPlugin {
+ pub fn run_steps(ctx: &mut CmsContext, steps: &[Step]) -> Result<()> {
+ for (_idx, step) in steps.iter().enumerate() {
+ step(ctx)?;
+ }
+ Ok(())
+ }
+
+ pub fn step_generate_cms(ctx: &mut CmsContext) -> Result<()> {
+ let cms = generate_cms_with_hash(
+ ctx.certificate,
+ ctx.private_key,
+ ctx.content,
+ ctx.options.clone(),
+ )?;
+ ctx.cms = cms;
+ Ok(())
+ }
+
+ pub fn step_generate_ts_req(ctx: &mut CmsContext) -> Result<()> {
+ let ts_req = generate_timestamp_req(ctx.cms, ctx.options.clone())?;
+ ctx.ts_req = ts_req;
+ Ok(())
+ }
+
+ pub fn step_load_tsa_cert_key(ctx: &mut CmsContext) -> Result<()> {
+ let cert = x509::X509::from_pem(ctx.tsa_cert_pem).map_err(|_e| {
+ Error::RemoteSignError("load tsa certificate failed".to_string())
+ })?;
+ let key = pkey::PKey::private_key_from_pem(ctx.tsa_key_pem).map_err(|_e| {
+ Error::RemoteSignError("load tsa private key failed".to_string())
+ })?;
+
+ ctx.tsa_cert = Some(cert);
+ ctx.tsa_key = Some(key);
+ Ok(())
+ }
+
+ pub fn step_generate_tst_info(ctx: &mut CmsContext) -> Result<()> {
+ let tsa_cert = ctx.tsa_cert.as_ref().ok_or_else(|| {
+ Error::RemoteSignError("tsa_cert not loaded".to_string())
+ })?;
+ let tst_info = generate_timestamp_tst(ctx.ts_req, tsa_cert)?;
+ ctx.tst_info = tst_info;
+ Ok(())
+ }
+
+ pub fn step_generate_timestamp_token(ctx: &mut CmsContext) -> Result<()> {
+ let tsa_cert = ctx.tsa_cert.as_ref().ok_or_else(|| {
+ Error::RemoteSignError("tsa_cert not loaded".to_string())
+ })?;
+ let tsa_key = ctx.tsa_key.as_ref().ok_or_else(|| {
+ Error::RemoteSignError("tsa_key not loaded".to_string())
+ })?;
+
+ let timestamp = generate_timestamp_signature(
+ tsa_cert,
+ tsa_key,
+ ctx.tst_info,
+ ctx.timestamp_options.clone(),
+ )?;
+ ctx.timestamp = timestamp;
+ Ok(())
+ }
+
+ pub fn step_attach_timestamp(ctx: &mut CmsContext) -> Result<()> {
+ attach_timestamp_to_cms(ctx.cms, ctx.timestamp)?;
+ Ok(())
+ }
+
+
+ pub fn cms_to_vec(cms: *mut CMS_ContentInfo) -> Result> {
+ struct BioGuard(*mut openssl_sys::BIO);
+ impl Drop for BioGuard {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe { openssl_sys::BIO_free_all(self.0) };
+ }
+ }
+ }
+ unsafe {
+ let out_bio = BIO_new(BIO_s_mem());
+ let _guard = BioGuard(out_bio);
+ if i2d_CMS_bio(out_bio, cms) != 1 {
+ return Err(Error::InvalidArgumentError(
+ "i2d_CMS_bio failed".to_string(),
+ ));
+ }
+
+ let mut ptr: *mut c_char = ptr::null_mut();
+ let len = BIO_get_mem_data(out_bio, &mut ptr) as usize;
+ if len == 0 || ptr.is_null() {
+ return Err(Error::InvalidArgumentError(
+ "BIO_get_mem_data got empty buffer".to_string(),
+ ));
+ }
+
+ let data_ptr = ptr as *const u8;
+ let buf = slice::from_raw_parts(data_ptr, len).to_vec();
+ Ok(buf)
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/infra/sign_plugin/mod.rs b/src/infra/sign_plugin/mod.rs
index 4fa0dcc..1445acb 100644
--- a/src/infra/sign_plugin/mod.rs
+++ b/src/infra/sign_plugin/mod.rs
@@ -2,3 +2,4 @@ pub mod openpgp;
pub mod signers;
pub mod util;
pub mod x509;
+pub mod cms;
diff --git a/src/infra/sign_plugin/openpgp.rs b/src/infra/sign_plugin/openpgp.rs
index 4421c54..3a98e63 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() {
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 7f37bed..7b7bb7b 100644
--- a/src/infra/sign_plugin/x509.rs
+++ b/src/infra/sign_plugin/x509.rs
@@ -24,11 +24,12 @@ use crate::domain::datakey::plugins::x509::{
};
use crate::domain::sign_plugin::SignPlugins;
use crate::util::attributes;
-use crate::util::attributes::PkeyHashAlgo;
use crate::util::error::{Error, Result};
use crate::util::key::{decode_hex_string_to_u8, encode_u8_to_hex_string};
use crate::util::options;
use crate::util::sign::SignType;
+use crate::infra::sign_plugin::cms::{CmsPlugin, EVP_PKEY_is_a, CmsContext, Step};
+
use chrono::{DateTime, Utc};
#[allow(unused_imports)]
use enum_iterator::all;
@@ -48,57 +49,21 @@ use openssl::x509::extension::{
};
use openssl::x509::{X509Crl, X509Extension};
use openssl_sys::{
- BIO_free_all, BIO_get_mem_data, BIO_new, BIO_new_mem_buf, BIO_s_mem, CMS_ContentInfo,
- CMS_ContentInfo_free, CMS_sign, X509_CRL_add0_revoked, X509_CRL_new, X509_CRL_set1_lastUpdate,
- X509_CRL_set1_nextUpdate, X509_CRL_set_issuer_name, X509_CRL_sign, X509_REVOKED_new,
- X509_REVOKED_set_revocationDate, X509_REVOKED_set_serialNumber, BIO, CMS_BINARY, CMS_DETACHED, CMS_KEY_PARAM,
- CMS_NOSMIMECAP, CMS_PARTIAL, EVP_MD, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_CTX_set_rsa_padding, X509, X509_CRL, RSA_PKCS1_PSS_PADDING,
+ X509_CRL_add0_revoked,
+ X509_CRL_new, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate, X509_CRL_set_issuer_name,
+ X509_CRL_sign, X509_REVOKED_new, X509_REVOKED_set_revocationDate,
+ X509_REVOKED_set_serialNumber,
};
use secstr::SecVec;
use serde::Deserialize;
use std::collections::HashMap;
use std::ffi::CString;
-use std::ffi::{c_char, c_int, c_uchar, c_uint, c_void};
-use std::ptr;
-use std::slice;
use std::str::FromStr;
use std::time::{Duration, SystemTime};
use validator::{Validate, ValidationError};
-#[repr(C)]
-pub struct CMS_SignerInfo {
- // 根据 OpenSSL 头文件添加字段
- pub cert: *mut X509,
- pub pkey: *mut EVP_PKEY,
- pub md: *const EVP_MD,
- pub sig: *mut u8,
- pub siglen: i32,
-}
-
-extern "C" {
- pub fn CMS_final_digest(
- cms: *mut CMS_ContentInfo,
- md: *const c_uchar,
- mdlen: c_uint,
- dcont: *mut BIO,
- flags: c_uint,
- ) -> c_int;
-
- pub fn CMS_add1_signer(
- cms: *mut CMS_ContentInfo,
- cert: *mut X509,
- pkey: *mut EVP_PKEY,
- md: *const EVP_MD,
- flags: u32,
- ) -> *mut CMS_SignerInfo;
- pub fn CMS_SignerInfo_get0_pkey_ctx(si: *mut CMS_SignerInfo) -> *mut EVP_PKEY_CTX;
- pub fn i2d_CMS_bio(out: *mut BIO, cms: *mut CMS_ContentInfo) -> c_int;
- pub fn EVP_PKEY_is_a(pkey: *const EVP_PKEY, name: *const c_char) -> c_int;
- pub fn CMS_add1_crl(cms: *mut CMS_ContentInfo,
- crl: *mut X509_CRL) -> c_int;
-}
-
#[derive(Debug, Validate, Deserialize)]
#[validate(schema(function = "validate_x509_key_size_for_generation"))]
+
pub struct X509KeyGenerationParameter {
#[validate(length(min = 1, max = 30, message = "invalid x509 subject 'CommonName'"))]
common_name: String,
@@ -217,8 +182,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,
}
@@ -576,118 +544,8 @@ impl X509Plugin {
}
}
-fn cms_sign_with_hash(
- cert: &x509::X509Ref,
- pkey: &PKey,
- digest: &[u8],
- attribute: HashMap,
-) -> Result> {
- unsafe {
- let data_bio = BIO_new_mem_buf(digest.as_ptr() as *const c_void, digest.len() as i32);
- if data_bio.is_null() {
- return Err(Error::InvalidArgumentError(
- "BIO_new_mem_buf failed".to_string(),
- ));
- }
-
- struct BioGuard(*mut openssl_sys::BIO);
- impl Drop for BioGuard {
- fn drop(&mut self) {
- if !self.0.is_null() {
- unsafe { BIO_free_all(self.0) };
- }
- }
- }
- let _data_bio_guard = BioGuard(data_bio);
-
- let flags = CMS_DETACHED | CMS_BINARY | CMS_PARTIAL | CMS_NOSMIMECAP | CMS_KEY_PARAM;
- let cms: *mut CMS_ContentInfo = CMS_sign(
- ptr::null_mut(),
- ptr::null_mut(),
- ptr::null_mut(),
- data_bio,
- flags,
- );
- if cms.is_null() {
- return Err(Error::InvalidArgumentError(
- "CMS_sign (partial) failed".to_string(),
- ));
- }
-
- struct CmsGuard(*mut CMS_ContentInfo);
- impl Drop for CmsGuard {
- fn drop(&mut self) {
- if !self.0.is_null() {
- unsafe { CMS_ContentInfo_free(self.0) };
- }
- }
- }
- let _cms_guard = CmsGuard(cms);
-
- let md = PkeyHashAlgo::get_openssl_c_digest_algo(&attribute);
- if md.is_null() {
- return Err(Error::InvalidArgumentError(
- "EVP_sha256 returned null".to_string(),
- ));
- }
-
- let si = CMS_add1_signer(cms, cert.as_ptr(), pkey.as_ptr(), md, flags);
- if si.is_null() {
- return Err(Error::InvalidArgumentError(
- "CMS_add1_signer failed".to_string(),
- ));
- }
-
- let pk_ctx = CMS_SignerInfo_get0_pkey_ctx(si);
- if pk_ctx.is_null() {
- return Err(Error::InvalidArgumentError(
- "CMS_SignerInfo_get0_pkey_ctx failed".to_string(),
- ));
- }
- EVP_PKEY_CTX_set_rsa_padding(pk_ctx, RSA_PKCS1_PSS_PADDING);
-
- let ret = CMS_final_digest(
- cms,
- digest.as_ptr(),
- digest.len() as u32,
- ptr::null_mut(),
- flags,
- );
- if ret != 1 {
- return Err(Error::InvalidArgumentError(
- "CMS_final_digest failed".to_string(),
- ));
- }
-
- let out_bio = BIO_new(BIO_s_mem());
- if out_bio.is_null() {
- return Err(Error::InvalidArgumentError(
- "BIO_new(BIO_s_mem) failed".to_string(),
- ));
- }
- let _out_bio_guard = BioGuard(out_bio);
-
- if i2d_CMS_bio(out_bio, cms) != 1 {
- return Err(Error::InvalidArgumentError(
- "i2d_CMS_bio failed".to_string(),
- ));
- }
-
- let mut ptr: *mut c_char = ptr::null_mut();
- let len = BIO_get_mem_data(out_bio, &mut ptr) as usize;
- if len == 0 || ptr.is_null() {
- return Err(Error::InvalidArgumentError(
- "BIO_get_mem_data got empty buffer".to_string(),
- ));
- }
- let data_ptr = ptr as *const u8;
- let buf = slice::from_raw_parts(data_ptr, len).to_vec();
- Ok(buf)
- }
-}
-
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(),
@@ -696,10 +554,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)
}
@@ -779,6 +646,10 @@ impl SignPlugins for X509Plugin {
)?)?;
}
+ for (key, value) in &options {
+ info!("key = {}, value = {}", key, value);
+ }
+
match SignType::from_str(
options
.get(options::SIGN_TYPE)
@@ -845,9 +716,34 @@ impl SignPlugins for X509Plugin {
Ok(cms_signature.to_der()?)
}
SignType::Cms => {
- // common cms signature have the signedAttrs field
- // after calculating the hash on the client side, it is sent to the server for signing
- cms_sign_with_hash(&certificate, &private_key, &content, options)
+ 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.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 => {
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/attributes.rs b/src/util/attributes.rs
index 199ee0b..c6fc83f 100644
--- a/src/util/attributes.rs
+++ b/src/util/attributes.rs
@@ -86,12 +86,9 @@ impl PkeyHashAlgo {
digest_algo
}
- pub fn get_openssl_c_digest_algo(attributes: &HashMap) -> *const EVP_MD {
+ pub fn get_openssl_c_digest_algo(digest: &String) -> *const EVP_MD {
unsafe {
- let digest_algo = match attributes
- .get(DIGEST_ALGO)
- .expect("get algo failed")
- .as_str()
+ let digest_algo = match digest.as_str()
{
"md5" => EVP_md5(),
"sha1" => EVP_sha1(),
diff --git a/src/util/options.rs b/src/util/options.rs
index beff556..98122d2 100644
--- a/src/util/options.rs
+++ b/src/util/options.rs
@@ -19,3 +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";
\ No newline at end of file
--
Gitee