certificate: parseable

master
occheung 2020-10-23 17:06:31 +08:00
parent ed2b73389a
commit d241e93d8e
2 changed files with 445 additions and 42 deletions

View File

@ -3,23 +3,25 @@ use num_enum::TryFromPrimitive;
use alloc::vec::Vec; use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct Certificate<'a> { pub struct Certificate<'a> {
tbs_certificate: TBSCertificate<'a>, pub tbs_certificate: TBSCertificate<'a>,
signature_algorithm: AlgorithmIdentifier<'a>, pub signature_algorithm: AlgorithmIdentifier<'a>,
signature_value: &'a [u8] pub signature_value: &'a [u8]
} }
#[derive(Debug, Clone)]
pub struct TBSCertificate<'a> { pub struct TBSCertificate<'a> {
version: Version, pub version: Version,
serial_number: &'a [u8], pub serial_number: &'a [u8],
signature: AlgorithmIdentifier<'a>, pub signature: AlgorithmIdentifier<'a>,
issuer: &'a [u8], pub issuer: &'a [u8],
validity: Validity<'a>, pub validity: Validity<'a>,
subject: &'a [u8], pub subject: &'a [u8],
subject_public_key_info: SubjectPublicKeyInfo<'a>, pub subject_public_key_info: SubjectPublicKeyInfo<'a>,
issuer_unique_id: Option<&'a [u8]>, pub issuer_unique_id: Option<&'a [u8]>,
subject_unique_id: Option<&'a [u8]>, pub subject_unique_id: Option<&'a [u8]>,
extensions: Extensions<'a>, pub extensions: Extensions<'a>,
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
@ -31,44 +33,98 @@ pub enum Version {
v3 = 2, v3 = 2,
} }
#[derive(Debug, Clone)]
pub struct Validity<'a> { pub struct Validity<'a> {
pub not_before: Time<'a>, pub not_before: Time<'a>,
pub not_after: Time<'a>, pub not_after: Time<'a>,
} }
#[derive(Debug, Clone)]
pub enum Time<'a> { pub enum Time<'a> {
UTCTime(&'a [u8]), UTCTime(&'a [u8]),
GeneralizedTime(&'a [u8]), GeneralizedTime(&'a [u8]),
} }
#[derive(Debug, Clone)]
pub struct SubjectPublicKeyInfo<'a> { pub struct SubjectPublicKeyInfo<'a> {
pub algorithm: AlgorithmIdentifier<'a>, pub algorithm: AlgorithmIdentifier<'a>,
pub subject_public_key: &'a [u8], pub subject_public_key: &'a [u8],
} }
#[derive(Debug, Clone)]
pub struct Extensions<'a> { pub struct Extensions<'a> {
extensions: Vec<Extension<'a>> // TODO: Give a limit to the number of policies, migrate to heapless vec
// An arbitrary upper limit does not violate RFC5280
pub extensions: Vec<Extension<'a>>
} }
pub enum Extension<'a> { #[derive(Debug, Clone)]
pub struct Extension<'a> {
pub extension_id: &'a [u8],
pub critical: bool,
pub extension_value: ExtensionValue<'a>,
}
#[derive(Debug, Clone)]
pub enum ExtensionValue<'a> {
KeyUsage { KeyUsage {
// Acceptable usage of this certificate // Acceptable usage of this certificate
// Cross verify with ExtendedKeyUsage // Cross verify with ExtendedKeyUsage
usage: u8 // MSb is bit 0
usage: u16
}, },
CertificatePolicies { CertificatePolicies {
// Policies listed in an extension // Policies listed in an extension
// Need to verify its validity // Need to verify its validity
policies: Vec<&'a [u8]> // TODO: Give a limit to the number of policies, migrate to heapless vec
// An arbitrary upper limit does not violate RFC5280
info: Vec<PolicyInformation<'a>>
}, },
SubjectAlternativeName,
// Permitted subtrees and excluded subtrees are not implemented
// SubjectAlternativeName,
BasicConstraints { BasicConstraints {
is_ca: bool, is_ca: bool,
path_len_constraint: Option<u8>, path_len_constraint: Option<u8>,
}, },
// Permitted subtrees and excluded subtrees are not implemented
// NameConstraints,
// Policy mapping will not be supported
// PolicyConstraints,
ExtendedKeyUsage {
// A list of all possible extended key usage in OID
// Cross check validity with regular KeyUsage
any_extended_key_usage: bool,
id_kp_server_auth: bool,
id_kp_client_auth: bool,
id_kp_code_signing: bool,
id_kp_email_protection: bool,
id_kp_time_stamping: bool,
id_kp_oscp_signing: bool,
},
InhibitAnyPolicy {
// Number of certificates in the path that may still allow AnyPolicy
// Certificate chain size should be limited to a small number
skip_certs: u8
},
// Extension data from an unsupported extension type
Unrecognized,
} }
#[derive(Debug, Clone)]
pub struct PolicyInformation<'a> {
pub id: &'a [u8],
pub qualifier: &'a [u8],
}
#[derive(Debug, Clone)]
pub struct AlgorithmIdentifier<'a> { pub struct AlgorithmIdentifier<'a> {
pub algorithm: &'a [u8], pub algorithm: &'a [u8],
pub parameters: &'a [u8], pub parameters: &'a [u8],

View File

@ -3,6 +3,7 @@ use nom::bytes::complete::take;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::bytes::complete::take_till; use nom::bytes::complete::take_till;
use nom::combinator::complete; use nom::combinator::complete;
use nom::combinator::opt;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use nom::error::make_error; use nom::error::make_error;
@ -13,13 +14,20 @@ use smoltcp::Result;
use byteorder::{ByteOrder, NetworkEndian, BigEndian}; use byteorder::{ByteOrder, NetworkEndian, BigEndian};
use crate::tls_packet::*; use crate::tls_packet::*;
use crate::certificate::Certificate as Asn1DerCertificate;
use crate::certificate::Version as Asn1DerVersion; use crate::certificate::{
use crate::certificate::AlgorithmIdentifier as Asn1DerAlgId; Certificate as Asn1DerCertificate,
use crate::certificate::Time as Asn1DerTime; Version as Asn1DerVersion,
use crate::certificate::Validity as Asn1DerValidity; AlgorithmIdentifier as Asn1DerAlgId,
use crate::certificate::SubjectPublicKeyInfo as Asn1DerSubjectPublicKeyInfo; Time as Asn1DerTime,
use crate::certificate::Extensions as Asn1DerExtensions; Validity as Asn1DerValidity,
SubjectPublicKeyInfo as Asn1DerSubjectPublicKeyInfo,
Extensions as Asn1DerExtensions,
Extension as Asn1DerExtension,
ExtensionValue as Asn1DerExtensionValue,
PolicyInformation as Asn1DerPolicyInformation,
TBSCertificate as Asn1DerTBSCertificate,
};
use core::convert::TryFrom; use core::convert::TryFrom;
use core::convert::TryInto; use core::convert::TryInto;
@ -368,22 +376,73 @@ pub fn parse_asn1_der_header(bytes: &[u8]) -> IResult<&[u8], (u8, usize)> {
// TODO: Not return length // TODO: Not return length
// It is quite useless when the value slice of the exact same length is returned // It is quite useless when the value slice of the exact same length is returned
// i.e. `length` can be replaced by `value.len()`
pub fn parse_asn1_der_object(bytes: &[u8]) -> IResult<&[u8], (u8, usize, &[u8])> { pub fn parse_asn1_der_object(bytes: &[u8]) -> IResult<&[u8], (u8, usize, &[u8])> {
let (rest, (tag, length)) = parse_asn1_der_header(bytes)?; let (rest, (tag, length)) = parse_asn1_der_header(bytes)?;
let (rest, value) = take(length)(rest)?; let (rest, value) = take(length)(rest)?;
Ok((rest, (tag, length, value))) Ok((rest, (tag, length, value)))
} }
pub fn parse_asn1_der_certificate(bytes: &[u8]) -> IResult<&[u8], (&[u8], &[u8], &[u8])> { pub fn parse_asn1_der_certificate(bytes: &[u8]) -> IResult<&[u8], Asn1DerCertificate> {
let (_, (_, _, rest)) = parse_asn1_der_object(bytes)?; let (excluded, (_, _, rest)) = parse_asn1_der_object(bytes)?;
let (rest, (_, _, tbscertificate_slice)) = parse_asn1_der_object(rest)?; let (_, (tbs_certificate, sig_alg, sig_value)) = complete(
let (rest, (_, _, signature_alg)) = parse_asn1_der_object(rest)?; tuple((
let (rest, (_, _, sig_val)) = parse_asn1_der_object(rest)?; parse_asn1_der_tbs_certificate,
Ok((rest, (tbscertificate_slice, signature_alg, sig_val))) parse_asn1_der_algorithm_identifier,
parse_asn1_der_bit_string
))
)(rest)?;
Ok((
excluded,
Asn1DerCertificate {
tbs_certificate,
signature_algorithm: sig_alg,
signature_value: sig_value,
}
))
} }
pub fn parse_asn1_der_tbs_certificate(bytes: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { // Parser for TBSCertificate (Sequence: 0x30)
todo!() pub fn parse_asn1_der_tbs_certificate(bytes: &[u8]) -> IResult<&[u8], Asn1DerTBSCertificate> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify the tag is indeed 0x30
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
let (_, (
version, serial_number, signature, issuer, validity, subject,
subject_public_key_info, issuer_unique_id, subject_unique_id, extensions
)) = complete(
tuple((
parse_asn1_der_version,
parse_asn1_der_serial_number,
parse_asn1_der_algorithm_identifier,
parse_asn1_der_sequence,
parse_asn1_der_validity,
parse_asn1_der_sequence,
parse_asn1_der_subject_key_public_info,
opt(parse_asn1_der_bit_string),
opt(parse_asn1_der_bit_string),
parse_asn1_der_extensions
))
)(value)?;
Ok((
rest,
Asn1DerTBSCertificate {
version,
serial_number,
signature,
issuer,
validity,
subject,
subject_public_key_info,
issuer_unique_id,
subject_unique_id,
extensions,
}
))
} }
// version: [0] EXPLICIT Version DEFAULT V1 // version: [0] EXPLICIT Version DEFAULT V1
@ -398,9 +457,7 @@ pub fn parse_asn1_der_version(bytes: &[u8]) -> IResult<&[u8], Asn1DerVersion> {
// Parse the encapsulated INTEGER, force completeness // Parse the encapsulated INTEGER, force completeness
let (_, integer) = complete(parse_asn1_der_integer)(value)?; let (_, integer) = complete(parse_asn1_der_integer)(value)?;
// Either 0, 1, or 2, take the last byte and assert all former bytes to be 0 // Either 0, 1, or 2, take the last byte and assert all former bytes to be 0
let (zeroes, version_byte) = take(integer.len()-1)(integer)?; Ok((rest, Asn1DerVersion::try_from(integer[0]).unwrap()))
complete(take_till(|byte| byte != 0))(zeroes)?;
Ok((rest, Asn1DerVersion::try_from(version_byte[0]).unwrap()))
} }
// INTEGER: tag: 0x02 // INTEGER: tag: 0x02
@ -410,8 +467,6 @@ pub fn parse_asn1_der_integer(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
if tag_val != 0x02 { if tag_val != 0x02 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify))); return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
} }
// Consume the leading 0x00 byte
let (value, _) = tag(&[0x00])(value)?;
Ok((rest, value)) Ok((rest, value))
} }
@ -422,12 +477,35 @@ pub fn parse_asn1_der_bit_string(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?; let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify the tag is indeed 0x03 // Verify the tag is indeed 0x03
if tag_val != 0x03 { if tag_val != 0x03 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify))); return Err(nom::Err::Error((bytes, ErrorKind::Verify)));
} }
// Dump `unused_bit` field // Dump `unused_bit` field
let (value, unused_bit_byte) = take(1_usize)(value)?; let (value, unused_bit_byte) = take(1_usize)(value)?;
// Assert no unused bits, otherwise it is a malformatted key // Assert no unused bits, otherwise it is a malformatted key
if value[0] != 0 { if unused_bit_byte[0] != 0 {
return Err(nom::Err::Error((bytes, ErrorKind::Verify)));
}
Ok((rest, value))
}
// BOOLEAN: tag: 0x01
// Length should be 1
// 0x00 -> false; 0xFF -> true
pub fn parse_asn1_der_boolean(bytes: &[u8]) -> IResult<&[u8], bool> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify the tag is indeed 0x01 and the length is 1
// The value should be 0x00 or 0xFF
if tag_val != 0x01 || length != 1 || (value[0] != 0x00 && value[0] != 0xFF) {
return Err(nom::Err::Error((bytes, ErrorKind::Verify)));
}
Ok((rest, value[0] == 0xFF))
}
// SEQUENCE: tag: 0x30
pub fn parse_asn1_der_sequence(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify the tag is indeed 0x03
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify))); return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
} }
Ok((rest, value)) Ok((rest, value))
@ -537,12 +615,281 @@ pub fn parse_asn1_der_subject_key_public_info(bytes: &[u8]) -> IResult<&[u8], As
)) ))
} }
// Parser for extensions (Sequence: 0xA3) // Parser for extensions (Context-specific Sequence: 0xA3, then universal Sequence: 0x30)
pub fn parse_asn1_der_extensions(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensions> { pub fn parse_asn1_der_extensions(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensions> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?; let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify the tag_val is indeed 0xA3 // Verify the tag_val is indeed 0xA3
if tag_val != 0xA3 { if tag_val != 0xA3 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify))); return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
} }
todo!()
let (_, (tag_val, length, mut value)) = complete(
parse_asn1_der_object
)(value)?;
// Verify the tag_val is indeed 0x30
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
let mut extensions = Vec::new();
while value.len() != 0 {
let (rem, extension) = parse_asn1_der_extension(value)?;
value = rem;
extensions.push(extension);
}
Ok((
rest,
Asn1DerExtensions { extensions }
))
}
// Parser for an extension (Sequence: 0x30)
pub fn parse_asn1_der_extension(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtension> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify the tag_val is indeed 0x30
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
// Parse an appropriate extension according to OID and critical-ness
let (_, (oid, critical, rem_ext_data)) = complete(
tuple((
parse_asn1_der_oid,
opt(parse_asn1_der_boolean),
parse_asn1_der_octet_string
))
)(value)?;
let extension_value = match oid {
oid::CERT_KEY_USAGE => {
let (_, extension_value) = complete(
parse_asn1_der_key_usage
)(rem_ext_data)?;
extension_value
},
oid::CERT_POLICIES => {
let (_, extension_value) = complete(
parse_asn1_der_certificate_policies
)(rem_ext_data)?;
extension_value
},
oid::CERT_BASIC_CONSTRAINTS => {
let (_, extension_value) = complete(
parse_asn1_der_basic_constraints
)(rem_ext_data)?;
extension_value
},
oid::CERT_EXT_KEY_USAGE => {
let (_, extension_value) = complete(
parse_asn1_der_extended_key_usage
)(rem_ext_data)?;
extension_value
},
oid::CERT_INHIBIT_ANY_POLICY => {
let (_, extension_value) = complete(
parse_inhibit_any_policy
)(rem_ext_data)?;
extension_value
},
// TODO: Parse extension value for recognized extensions
_ => Asn1DerExtensionValue::Unrecognized
};
Ok((
rest,
Asn1DerExtension {
extension_id: oid,
critical: critical.map_or(false, |b| b),
extension_value
}
))
}
// Parser for KeyUsage Extension, may have bit padding
// Do not use parse_asn1_der_bit_string, that assumes no bit padding
pub fn parse_asn1_der_key_usage(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify the tag_val represents a bitstring, and it must have length 2
// i.e. bit-padding | bit-string
if tag_val != 0x03 || (length != 2 && length != 3) {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
// Erase the padded bits
let padding = value[0];
let usage_array: [u8; 2] = if length == 2 {
[value[1], 0]
} else {
[value[1], value[2]]
};
let usage = (NetworkEndian::read_u16(&usage_array) >> padding) << padding;
Ok((
rest,
Asn1DerExtensionValue::KeyUsage {
usage
}
))
}
// Parser for CertificatePolicies Extension (sequence: 0x30)
pub fn parse_asn1_der_certificate_policies(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
let (rest, (tag_val, length, mut value)) = parse_asn1_der_object(bytes)?;
// Verify tag value
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
let mut vec: Vec<Asn1DerPolicyInformation> = Vec::new();
while value.len() != 0 {
let (rem, info) = parse_asn1_der_policy_information(value)?;
value = rem;
vec.push(info);
}
Ok((
rest,
Asn1DerExtensionValue::CertificatePolicies {
info: vec,
}
))
}
// Parser for PolicyInformation (Sequence: 0x30)
pub fn parse_asn1_der_policy_information(bytes: &[u8]) -> IResult<&[u8], Asn1DerPolicyInformation> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify tag value
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
let (_, (oid, (_, _, qualifier))) = complete(
tuple((
parse_asn1_der_oid,
parse_asn1_der_object
))
)(value)?;
Ok((
rest,
Asn1DerPolicyInformation {
id: oid,
qualifier
}
))
}
// Parser for BasicConstraints (Sequence: 0x30)
pub fn parse_asn1_der_basic_constraints(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify tag value
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
let (_, (is_ca, path_len_constraint)) = complete(
tuple((
opt(parse_asn1_der_boolean),
opt(parse_asn1_der_integer)
))
)(value)?;
let is_ca = is_ca.map_or(false, |b| b);
let path_len_constraint = path_len_constraint.map(
|slice| {
if slice.len() != 1 {
255
} else {
slice[0]
}
}
);
Ok((
rest,
Asn1DerExtensionValue::BasicConstraints {
is_ca,
path_len_constraint
}
))
}
// Parser for Extended Key Usage Extension (Sequence: 0x30)
pub fn parse_asn1_der_extended_key_usage(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
let (rest, (tag_val, length, mut value)) = parse_asn1_der_object(bytes)?;
// Verify tag value
if tag_val != 0x30 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
let mut flags: [bool; 7] = [false; 7];
while value.len() != 0 {
let (rem, oid_val) = parse_asn1_der_oid(value)?;
value = rem;
match oid_val {
oid::ANY_EXTENDED_KEY_USAGE => flags[0] = true,
oid::ID_KP_SERVER_AUTH => flags[1] = true,
oid::ID_KP_CLIENT_AUTH => flags[2] = true,
oid::ID_KP_CODE_SIGNING => flags[3] = true,
oid::ID_KP_EMAIL_PROTECTION => flags[4] = true,
oid::ID_KP_TIME_STAMPING => flags[5] = true,
oid::ID_KP_OCSP_SIGNING => flags[6] = true,
_ => {},
}
}
Ok((
rest,
Asn1DerExtensionValue::ExtendedKeyUsage {
any_extended_key_usage: flags[0],
id_kp_server_auth: flags[1],
id_kp_client_auth: flags[2],
id_kp_code_signing: flags[3],
id_kp_email_protection: flags[4],
id_kp_time_stamping: flags[5],
id_kp_oscp_signing: flags[6],
}
))
}
// Parser for inhibit anyPolicy extension (integer)
pub fn parse_inhibit_any_policy(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
let (rest, integer_slice) = parse_asn1_der_integer(bytes)?;
Ok((
rest,
Asn1DerExtensionValue::InhibitAnyPolicy {
skip_certs: {
if integer_slice.len() == 1 {
integer_slice[0]
} else {
255
}
}
}
))
}
// Parser for octet string (tag: 0x04)
pub fn parse_asn1_der_octet_string(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
// Verify tag value
if tag_val != 0x04 {
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
Ok((rest, value))
}
mod oid {
// Extensions
pub const CERT_KEY_USAGE: &'static [u8] = &[85, 29, 15]; // 2.5.29.15
pub const CERT_POLICIES: &'static [u8] = &[85, 29, 32]; // 2.5.29.32
pub const CERT_BASIC_CONSTRAINTS: &'static [u8] = &[85, 29, 19]; // 2.5.29.19
pub const CERT_EXT_KEY_USAGE: &'static [u8] = &[85, 29, 37]; // 2.5.29.37
pub const CERT_INHIBIT_ANY_POLICY: &'static [u8] = &[85, 29, 54]; // 2.5.29.54
// Extended Key Extensions
pub const ANY_EXTENDED_KEY_USAGE: &'static [u8] = &[85, 29, 37, 0]; // 2.5.29.37.0
pub const ID_KP_SERVER_AUTH: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 1]; // 1.3.6.1.5.5.7.3.1
pub const ID_KP_CLIENT_AUTH: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 2]; // 1.3.6.1.5.5.7.3.2
pub const ID_KP_CODE_SIGNING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 3]; // 1.3.6.1.5.5.7.3.3
pub const ID_KP_EMAIL_PROTECTION: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 4]; // 1.3.6.1.5.5.7.3.4
pub const ID_KP_TIME_STAMPING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 8]; // 1.3.6.1.5.5.7.3.8
pub const ID_KP_OCSP_SIGNING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 9]; // 1.3.6.1.5.5.7.3.9
} }