Skip to content

Commit c44ca53

Browse files
committed
Remove same-key limitation for CSR generation using PKCS11
This PR removes a limitation for generating CSRs using PKCS11 private keys that they can only be generated for the same certificate that is already present. This unlocks two usecases that were previously impossible: - using `tedge cert renew` to install a new certificate when we already have a certificate but a different keypair is used (previously SubjectPublicKeyInfo was reused from previous cert so using different key didn't work) - using `tedge cert download c8y` when we don't yet have a certificate and register the device to C8y CA and download the initial certificate (previously some fields of CSR were reused from older cert so had no way to fill these fields without some certificate already being present) Signed-off-by: Marcel Guzik <marcel.guzik@cumulocity.com>
1 parent 95af7dd commit c44ca53

File tree

11 files changed

+105
-42
lines changed

11 files changed

+105
-42
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/common/certificate/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ anyhow = { workspace = true }
1717
asn1-rs = { workspace = true }
1818
base64 = { workspace = true }
1919
camino = { workspace = true }
20+
pem.workspace = true
2021
rcgen = { workspace = true }
2122
reqwest = { workspace = true, optional = true, features = [
2223
"rustls-tls-native-roots",

crates/common/certificate/src/lib.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use sha1::Digest;
77
use sha1::Sha1;
88
use std::path::Path;
99
use std::path::PathBuf;
10+
use tedge_p11_server::service::ChooseSchemeRequest;
1011
use tedge_p11_server::CryptokiConfig;
1112
use time::Duration;
1213
use time::OffsetDateTime;
@@ -191,6 +192,45 @@ impl KeyKind {
191192
algorithm,
192193
}))
193194
}
195+
196+
pub fn from_cryptoki(cryptoki_config: CryptokiConfig) -> Result<Self, CertificateError> {
197+
let cryptoki = tedge_p11_server::tedge_p11_service(cryptoki_config.clone())?;
198+
let pubkey_pem = cryptoki.get_public_key_pem(None)?;
199+
let public_key = pem::parse(&pubkey_pem).unwrap();
200+
let public_key_raw = public_key.into_contents();
201+
202+
let signature_algorithm = cryptoki.choose_scheme(ChooseSchemeRequest {
203+
offered: vec![
204+
tedge_p11_server::service::SignatureScheme(
205+
rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
206+
),
207+
tedge_p11_server::service::SignatureScheme(
208+
rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
209+
),
210+
tedge_p11_server::service::SignatureScheme(
211+
rustls::SignatureScheme::RSA_PKCS1_SHA256,
212+
),
213+
],
214+
uri: None,
215+
})?;
216+
let signature_algorithm = signature_algorithm
217+
.scheme
218+
.context("No supported scheme found")?
219+
.0;
220+
221+
let algorithm = match signature_algorithm {
222+
rustls::SignatureScheme::ECDSA_NISTP256_SHA256 => SignatureAlgorithm::EcdsaP256Sha256,
223+
rustls::SignatureScheme::ECDSA_NISTP384_SHA384 => SignatureAlgorithm::EcdsaP384Sha384,
224+
rustls::SignatureScheme::RSA_PKCS1_SHA256 => SignatureAlgorithm::RsaPkcs1Sha256,
225+
_ => return Err(anyhow::anyhow!("Unsupported signature scheme").into()),
226+
};
227+
228+
Ok(Self::ReuseRemote(RemoteKeyPair {
229+
cryptoki_config,
230+
public_key_raw,
231+
algorithm,
232+
}))
233+
}
194234
}
195235

196236
/// A key pair using a remote private key.

crates/core/tedge/src/cli/certificate/c8y/download.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ impl DownloadCertCmd {
8585
create_device_csr(
8686
common_name.clone(),
8787
self.key.clone(),
88-
None,
8988
self.csr_path.clone(),
9089
self.csr_template.clone(),
9190
)

crates/core/tedge/src/cli/certificate/c8y/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,13 @@ pub use upload::UploadCertCmd;
1818
async fn create_device_csr(
1919
common_name: String,
2020
key: super::create_csr::Key,
21-
current_cert: Option<Utf8PathBuf>,
2221
csr_path: Utf8PathBuf,
2322
csr_template: CsrTemplate,
2423
) -> Result<(), CertError> {
2524
let create_cmd = CreateCsrCmd {
2625
id: common_name,
2726
csr_path: csr_path.clone(),
2827
key,
29-
current_cert,
3028
user: "tedge".to_string(),
3129
group: "tedge".to_string(),
3230
csr_template,

crates/core/tedge/src/cli/certificate/c8y/renew.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ impl RenewCertCmd {
8484
create_device_csr(
8585
common_name,
8686
self.key.clone(),
87-
Some(self.cert_path.clone()),
8887
self.csr_path.clone(),
8988
self.csr_template.clone(),
9089
)

crates/core/tedge/src/cli/certificate/cli.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,9 @@ impl BuildCommand for TEdgeCertCli {
198198
));
199199
debug!(?key);
200200

201-
let current_cert = config
202-
.device_cert_path(cloud.as_ref())
203-
.map(|c| c.to_owned())
204-
.ok();
205-
debug!(?current_cert);
206201
let cmd = CreateCsrCmd {
207202
id: get_device_id(id, config, &cloud)?,
208203
key,
209-
current_cert,
210204
// Use output file instead of csr_path from tedge config if provided
211205
csr_path: if let Some(output_path) = output_path {
212206
output_path

crates/core/tedge/src/cli/certificate/create_csr.rs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use crate::log::MaybeFancy;
44
use crate::override_public_key;
55
use crate::persist_new_private_key;
66
use crate::reuse_private_key;
7-
use anyhow::Context;
87
use camino::Utf8PathBuf;
98
use certificate::parse_root_certificate::CryptokiConfig;
109
use certificate::CsrTemplate;
@@ -22,9 +21,6 @@ pub struct CreateCsrCmd {
2221
/// The path where the device private key will be stored
2322
pub key: Key,
2423

25-
/// Path to current certificate
26-
pub current_cert: Option<Utf8PathBuf>,
27-
2824
/// The path where the device CSR will be stored
2925
pub csr_path: Utf8PathBuf,
3026

@@ -67,13 +63,7 @@ impl CreateCsrCmd {
6763
.await
6864
.map_err(|e| CertError::IoError(e).key_context(key_path.clone()))?,
6965

70-
Key::Cryptoki(cryptoki) => {
71-
let current_cert = self
72-
.current_cert
73-
.clone()
74-
.context("Need an existing cert when using an HSM")?;
75-
KeyKind::from_cryptoki_and_existing_cert(cryptoki.clone(), &current_cert)?
76-
}
66+
Key::Cryptoki(config) => KeyKind::from_cryptoki(config.clone())?,
7767
};
7868
debug!(?previous_key);
7969

@@ -117,7 +107,6 @@ mod tests {
117107
let cmd = CreateCsrCmd {
118108
id: id.to_string(),
119109
key: Key::Local(key_path.clone()),
120-
current_cert: None,
121110
csr_path: csr_path.clone(),
122111
user: "mosquitto".to_string(),
123112
group: "mosquitto".to_string(),
@@ -161,7 +150,6 @@ mod tests {
161150
let cmd = CreateCsrCmd {
162151
id: id.to_string(),
163152
key: Key::Local(key_path.clone()),
164-
current_cert: None,
165153
csr_path: csr_path.clone(),
166154
user: "mosquitto".to_string(),
167155
group: "mosquitto".to_string(),

crates/extensions/tedge-p11-server/src/pkcs11/mod.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use cryptoki::mechanism::MechanismType;
2121
use cryptoki::object::Attribute;
2222
use cryptoki::object::AttributeType;
2323
use cryptoki::object::KeyType;
24+
use cryptoki::object::ObjectClass;
2425
use cryptoki::object::ObjectHandle;
2526
use cryptoki::session::Session;
2627
use cryptoki::session::UserType;
@@ -175,7 +176,8 @@ impl Cryptoki {
175176
let session = self.open_session(&uri_attributes)?;
176177

177178
// get the signing key
178-
let key = Self::find_key_by_attributes(&uri_attributes, &session)?;
179+
let key =
180+
Self::find_key_by_attributes(&uri_attributes, &session, ObjectClass::PRIVATE_KEY)?;
179181
let key_type = session
180182
.get_attributes(key, &[AttributeType::KeyType])?
181183
.into_iter()
@@ -214,12 +216,14 @@ impl Cryptoki {
214216
session,
215217
key,
216218
sigscheme,
219+
secondary_schemes: Vec::new(),
217220
}
218221
}
219222
KeyType::RSA => Pkcs11Signer {
220223
session,
221224
key,
222225
sigscheme: SigScheme::RsaPssSha256,
226+
secondary_schemes: vec![SigScheme::RsaPkcs1Sha256],
223227
},
224228
_ => anyhow::bail!("unsupported key type"),
225229
};
@@ -231,36 +235,32 @@ impl Cryptoki {
231235
let uri_attributes = self.request_uri(uri)?;
232236
let session = self.open_session(&uri_attributes)?;
233237

234-
let key =
235-
Self::find_key_by_attributes(&uri_attributes, &session).context("object not found")?;
238+
let key = Self::find_key_by_attributes(&uri_attributes, &session, ObjectClass::PUBLIC_KEY)?;
236239

237240
export_public_key_pem(&session, key)
238241
}
239242

240243
fn find_key_by_attributes(
241244
uri: &uri::Pkcs11Uri,
242245
session: &Session,
246+
class: ObjectClass,
243247
) -> anyhow::Result<ObjectHandle> {
244-
let mut key_template = vec![
245-
Attribute::Token(true),
246-
Attribute::Private(true),
247-
Attribute::Sign(true),
248-
];
248+
let mut key_template = vec![Attribute::Token(true), Attribute::Class(class)];
249249
if let Some(object) = &uri.object {
250250
key_template.push(Attribute::Label(object.as_bytes().to_vec()));
251251
}
252252
if let Some(id) = &uri.id {
253253
key_template.push(Attribute::Id(id.clone()));
254254
}
255255

256-
trace!(?key_template, "Finding a key");
256+
trace!(?key_template, ?uri.object, "Finding a key");
257257

258258
let mut keys = session
259259
.find_objects(&key_template)
260260
.context("Failed to find private key objects")?
261261
.into_iter();
262262

263-
let key = keys.next().context("Failed to find a private key")?;
263+
let key = keys.next().context("Failed to find a key")?;
264264
if keys.len() > 0 {
265265
warn!(
266266
"Multiple keys were found. If the wrong one was chosen, please use a URI that uniquely identifies a key."
@@ -364,6 +364,7 @@ pub struct Pkcs11Signer {
364364
session: Pkcs11Session,
365365
key: ObjectHandle,
366366
pub sigscheme: SigScheme,
367+
pub secondary_schemes: Vec<SigScheme>,
367368
}
368369

369370
impl Pkcs11Signer {
@@ -498,10 +499,20 @@ impl SigningKey for Pkcs11Signer {
498499
let key_scheme = self.sigscheme.into();
499500
if offered.contains(&key_scheme) {
500501
debug!("Matching scheme: {key_scheme:?}");
501-
Some(Box::new(self.clone()))
502-
} else {
503-
None
502+
return Some(Box::new(self.clone()));
504503
}
504+
505+
for scheme in &self.secondary_schemes {
506+
let key_scheme = (*scheme).into();
507+
if offered.contains(&key_scheme) {
508+
debug!("Matching scheme: {key_scheme:?}");
509+
let mut signer = self.clone();
510+
signer.sigscheme = *scheme;
511+
return Some(Box::new(signer));
512+
}
513+
}
514+
515+
None
505516
}
506517

507518
fn algorithm(&self) -> SignatureAlgorithm {

crates/extensions/tedge-p11-server/src/proxy/client.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,35 @@ impl TedgeP11Service for TedgeP11Client {
2929
&self,
3030
request: ChooseSchemeRequest,
3131
) -> anyhow::Result<crate::service::ChooseSchemeResponse> {
32+
let uri = request
33+
.uri
34+
.as_deref()
35+
.or(self.uri.as_deref())
36+
.map(ToString::to_string);
3237
let offered: Vec<_> = request.offered.iter().map(|s| s.0).collect();
33-
self.choose_scheme(&offered, request.uri)
38+
self.choose_scheme(&offered, uri)
3439
}
3540

3641
fn sign(
3742
&self,
3843
request: SignRequestWithSigScheme,
3944
) -> anyhow::Result<crate::service::SignResponse> {
45+
let uri = request
46+
.uri
47+
.as_deref()
48+
.or(self.uri.as_deref())
49+
.map(ToString::to_string);
4050
let response = match request.sigscheme {
41-
Some(sigscheme) => self.sign2(&request.to_sign, request.uri, sigscheme)?,
42-
None => self.sign(&request.to_sign, request.uri)?,
51+
Some(sigscheme) => self.sign2(&request.to_sign, uri, sigscheme)?,
52+
None => self.sign(&request.to_sign, uri)?,
4353
};
4454

4555
Ok(crate::service::SignResponse(response))
4656
}
4757

4858
fn get_public_key_pem(&self, uri: Option<&str>) -> anyhow::Result<String> {
49-
self.get_public_key_pem(uri.map(ToString::to_string))
59+
let uri = uri.or(self.uri.as_deref()).map(ToString::to_string);
60+
self.get_public_key_pem(uri)
5061
}
5162
}
5263

0 commit comments

Comments
 (0)