Skip to content

Commit 1bed94f

Browse files
committed
x509-cert: add SubjectKeyIdentifier and AuthorityKeyIdentifier conversions
1 parent 8589907 commit 1bed94f

File tree

4 files changed

+67
-27
lines changed

4 files changed

+67
-27
lines changed

x509-cert/src/builder.rs

+15-26
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
33
use alloc::vec;
44
use core::fmt;
5-
use der::{
6-
asn1::{BitString, OctetString},
7-
Encode,
8-
};
9-
use sha1::{Digest, Sha1};
5+
use der::{asn1::BitString, referenced::OwnedToRef, Encode};
106
use signature::{Keypair, SignatureEncoding, Signer};
11-
use spki::{DynSignatureAlgorithmIdentifier, EncodePublicKey, SubjectPublicKeyInfoOwned};
7+
use spki::{
8+
DynSignatureAlgorithmIdentifier, EncodePublicKey, SubjectPublicKeyInfoOwned,
9+
SubjectPublicKeyInfoRef,
10+
};
1211

1312
use crate::{
1413
certificate::{Certificate, TbsCertificate, Version},
@@ -112,8 +111,8 @@ impl Profile {
112111

113112
fn build_extensions(
114113
&self,
115-
spk: &SubjectPublicKeyInfoOwned,
116-
issuer_spk: &SubjectPublicKeyInfoOwned,
114+
spk: SubjectPublicKeyInfoRef<'_>,
115+
issuer_spk: SubjectPublicKeyInfoRef<'_>,
117116
tbs: &TbsCertificate,
118117
) -> Result<vec::Vec<Extension>> {
119118
#[cfg(feature = "hazmat")]
@@ -124,27 +123,14 @@ impl Profile {
124123

125124
let mut extensions: vec::Vec<Extension> = vec::Vec::new();
126125

127-
extensions.push({
128-
let result = Sha1::digest(spk.subject_public_key.raw_bytes());
129-
SubjectKeyIdentifier(OctetString::new(result.to_vec())?).to_extension(tbs)?
130-
});
126+
extensions.push(SubjectKeyIdentifier::try_from(spk)?.to_extension(tbs)?);
131127

132128
// Build Authority Key Identifier
133129
match self {
134130
Profile::Root => {}
135131
_ => {
136-
let mut hasher = Sha1::new();
137-
hasher.update(issuer_spk.subject_public_key.raw_bytes());
138-
let result = hasher.finalize();
139-
140-
extensions.push(
141-
AuthorityKeyIdentifier {
142-
key_identifier: Some(OctetString::new(result.to_vec())?),
143-
authority_cert_issuer: None,
144-
authority_cert_serial_number: None,
145-
}
146-
.to_extension(tbs)?,
147-
);
132+
extensions
133+
.push(AuthorityKeyIdentifier::try_from(issuer_spk.clone())?.to_extension(tbs)?);
148134
}
149135
}
150136

@@ -301,8 +287,11 @@ where
301287
};
302288

303289
if tbs.version == Version::V3 {
304-
let extensions =
305-
profile.build_extensions(&tbs.subject_public_key_info, &signer_pub, &tbs)?;
290+
let extensions = profile.build_extensions(
291+
tbs.subject_public_key_info.owned_to_ref(),
292+
signer_pub.owned_to_ref(),
293+
&tbs,
294+
)?;
306295
if !extensions.is_empty() {
307296
tbs.extensions = Some(extensions);
308297
}

x509-cert/src/ext/pkix.rs

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ impl AssociatedOid for SubjectKeyIdentifier {
4949

5050
impl_newtype!(SubjectKeyIdentifier, OctetString);
5151
impl_extension!(SubjectKeyIdentifier, critical = false);
52+
impl_key_identifier!(
53+
SubjectKeyIdentifier,
54+
(|result: &[u8]| Ok(Self(OctetString::new(result)?)))
55+
);
5256

5357
/// SubjectAltName as defined in [RFC 5280 Section 4.2.1.6].
5458
///

x509-cert/src/ext/pkix/authkeyid.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use der::Sequence;
1919
/// ```
2020
///
2121
/// [RFC 5280 Section 4.2.1.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1
22-
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
22+
#[derive(Clone, Debug, Eq, PartialEq, Sequence, Default)]
2323
#[allow(missing_docs)]
2424
pub struct AuthorityKeyIdentifier {
2525
#[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")]
@@ -37,3 +37,10 @@ impl AssociatedOid for AuthorityKeyIdentifier {
3737
}
3838

3939
impl_extension!(AuthorityKeyIdentifier, critical = false);
40+
impl_key_identifier!(
41+
AuthorityKeyIdentifier,
42+
(|result: &[u8]| Ok(Self {
43+
key_identifier: Some(OctetString::new(result)?),
44+
..Default::default()
45+
}))
46+
);

x509-cert/src/macros.rs

+40
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,43 @@ macro_rules! impl_extension {
9292
}
9393
};
9494
}
95+
96+
/// Implements conversions between [`spki::SubjectPublicKeyInfo`] and [`SubjectKeyIdentifier`] or [`AuthorityKeyIdentifier`]
97+
macro_rules! impl_key_identifier {
98+
($newtype:ty, $out:expr) => {
99+
#[cfg(feature = "builder")]
100+
mod builder_key_identifier {
101+
use super::*;
102+
use der::asn1::OctetString;
103+
use sha1::{Digest, Sha1};
104+
use spki::SubjectPublicKeyInfoRef;
105+
106+
impl<'a> TryFrom<SubjectPublicKeyInfoRef<'a>> for $newtype {
107+
type Error = der::Error;
108+
109+
fn try_from(issuer: SubjectPublicKeyInfoRef<'a>) -> Result<Self, Self::Error> {
110+
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2
111+
//
112+
// For CA certificates, subject key identifiers SHOULD be derived from
113+
// the public key or a method that generates unique values. Two common
114+
// methods for generating key identifiers from the public key are:
115+
116+
// (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
117+
// value of the BIT STRING subjectPublicKey (excluding the tag,
118+
// length, and number of unused bits).
119+
120+
// (2) The keyIdentifier is composed of a four-bit type field with
121+
// the value 0100 followed by the least significant 60 bits of
122+
// the SHA-1 hash of the value of the BIT STRING
123+
// subjectPublicKey (excluding the tag, length, and number of
124+
// unused bits).
125+
126+
// Here we're using the first method
127+
128+
let result = Sha1::digest(issuer.subject_public_key.raw_bytes());
129+
$out(result.as_slice())
130+
}
131+
}
132+
}
133+
};
134+
}

0 commit comments

Comments
 (0)