Improve self-signed certificates (#207)
Adds support for: - A hierarchical SubjectName field. - Certificate extensions.
This commit is contained in:
Generated
+2
-2
@@ -1047,9 +1047,9 @@ checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x509"
|
name = "x509"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9335b8ff50b6a0de184b3eeb11fdce74224e3af90ca7265012512e73fc999d1a"
|
checksum = "ca3cec94c3999f31341553f358ef55f65fc031291a022cd42ec0ce7219560c76"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"cookie-factory",
|
"cookie-factory",
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ sha-1 = "0.9"
|
|||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
subtle = "2"
|
subtle = "2"
|
||||||
subtle-encoding = "0.5"
|
subtle-encoding = "0.5"
|
||||||
x509 = "0.1.2"
|
x509 = "0.2"
|
||||||
x509-parser = "0.9"
|
x509-parser = "0.9"
|
||||||
zeroize = "1"
|
zeroize = "1"
|
||||||
|
|
||||||
|
|||||||
+20
-6
@@ -49,6 +49,7 @@ use sha2::{Digest, Sha256};
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
use x509::{der::Oid, RelativeDistinguishedName};
|
||||||
use x509_parser::{parse_x509_certificate, x509::SubjectPublicKeyInfo};
|
use x509_parser::{parse_x509_certificate, x509::SubjectPublicKeyInfo};
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
@@ -337,19 +338,21 @@ impl<'a> TryFrom<&'a [u8]> for Certificate {
|
|||||||
impl Certificate {
|
impl Certificate {
|
||||||
/// Creates a new self-signed certificate for the given key. Writes the resulting
|
/// Creates a new self-signed certificate for the given key. Writes the resulting
|
||||||
/// certificate to the slot before returning it.
|
/// certificate to the slot before returning it.
|
||||||
pub fn generate_self_signed(
|
///
|
||||||
|
/// `extensions` is optional; if empty, no extensions will be included. Due to the
|
||||||
|
/// need for an `O: Oid` type parameter, users who do not have any extensions should
|
||||||
|
/// use the workaround `let extensions: &[x509::Extension<'_, &[u64]>] = &[];`.
|
||||||
|
pub fn generate_self_signed<O: Oid>(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
key: SlotId,
|
key: SlotId,
|
||||||
serial: impl Into<Serial>,
|
serial: impl Into<Serial>,
|
||||||
not_after: Option<DateTime<Utc>>,
|
not_after: Option<DateTime<Utc>>,
|
||||||
subject: String,
|
subject: &[RelativeDistinguishedName<'_>],
|
||||||
subject_pki: PublicKeyInfo,
|
subject_pki: PublicKeyInfo,
|
||||||
|
extensions: &[x509::Extension<'_, O>],
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let serial = serial.into();
|
let serial = serial.into();
|
||||||
|
|
||||||
// Issuer and subject are the same in self-signed certificates
|
|
||||||
let issuer = subject.clone();
|
|
||||||
|
|
||||||
let mut tbs_cert = Buffer::new(Vec::with_capacity(CB_OBJ_MAX));
|
let mut tbs_cert = Buffer::new(Vec::with_capacity(CB_OBJ_MAX));
|
||||||
|
|
||||||
let signature_algorithm = match subject_pki.algorithm() {
|
let signature_algorithm = match subject_pki.algorithm() {
|
||||||
@@ -361,11 +364,13 @@ impl Certificate {
|
|||||||
x509::write::tbs_certificate(
|
x509::write::tbs_certificate(
|
||||||
&serial.to_bytes(),
|
&serial.to_bytes(),
|
||||||
&signature_algorithm,
|
&signature_algorithm,
|
||||||
&issuer,
|
// Issuer and subject are the same in self-signed certificates.
|
||||||
|
&subject,
|
||||||
Utc::now(),
|
Utc::now(),
|
||||||
not_after,
|
not_after,
|
||||||
&subject,
|
&subject,
|
||||||
&subject_pki,
|
&subject_pki,
|
||||||
|
&extensions,
|
||||||
),
|
),
|
||||||
tbs_cert.deref_mut(),
|
tbs_cert.deref_mut(),
|
||||||
)
|
)
|
||||||
@@ -425,6 +430,15 @@ impl Certificate {
|
|||||||
)
|
)
|
||||||
.expect("can serialize to Vec");
|
.expect("can serialize to Vec");
|
||||||
|
|
||||||
|
let (issuer, subject) = parse_x509_certificate(&data)
|
||||||
|
.map(|(_, cert)| {
|
||||||
|
(
|
||||||
|
cert.tbs_certificate.issuer.to_string(),
|
||||||
|
cert.tbs_certificate.subject.to_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.expect("We just serialized this correctly");
|
||||||
|
|
||||||
let cert = Certificate {
|
let cert = Certificate {
|
||||||
serial,
|
serial,
|
||||||
issuer,
|
issuer,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use rsa::{hash::Hash::SHA2_256, PaddingScheme, PublicKey};
|
|||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::{env, sync::Mutex};
|
use std::{env, sync::Mutex};
|
||||||
|
use x509::RelativeDistinguishedName;
|
||||||
use yubikey_piv::{
|
use yubikey_piv::{
|
||||||
certificate::{Certificate, PublicKeyInfo},
|
certificate::{Certificate, PublicKeyInfo},
|
||||||
key::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
key::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
||||||
@@ -132,13 +133,15 @@ fn generate_self_signed_cert(algorithm: AlgorithmId) -> Certificate {
|
|||||||
getrandom(&mut serial).unwrap();
|
getrandom(&mut serial).unwrap();
|
||||||
|
|
||||||
// Generate a self-signed certificate for the new key.
|
// Generate a self-signed certificate for the new key.
|
||||||
|
let extensions: &[x509::Extension<'_, &[u64]>] = &[];
|
||||||
let cert_result = Certificate::generate_self_signed(
|
let cert_result = Certificate::generate_self_signed(
|
||||||
&mut yubikey,
|
&mut yubikey,
|
||||||
slot,
|
slot,
|
||||||
serial,
|
serial,
|
||||||
None,
|
None,
|
||||||
"testSubject".to_owned(),
|
&[RelativeDistinguishedName::common_name("testSubject")],
|
||||||
generated,
|
generated,
|
||||||
|
extensions,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(cert_result.is_ok());
|
assert!(cert_result.is_ok());
|
||||||
|
|||||||
Reference in New Issue
Block a user