///|
/// An attribute from a certification request (RFC 2986, 4.1).
///
/// The values are kept as decoded DER because their shape depends on the
/// attribute type.
pub struct Attribute {
oid : @asn1.ObjectIdentifier
values : Array[@asn1.Der]
}
///|
/// The subject, public key and attributes being requested (RFC 2986, 4.1).
pub struct CertificationRequestInfo {
version : Int
subject : Name
subject_public_key_info : SubjectPublicKeyInfo
attributes : Array[Attribute]
}
///|
/// A PKCS#10 certification request (RFC 2986, 4.2).
pub struct CertificationRequest {
/// The original DER encoding, so a request can be round tripped.
raw : Bytes
info : CertificationRequestInfo
signature_algorithm : AlgorithmIdentifier
signature : Bytes
}
///|
/// Finds an attribute by its dotted object identifier.
pub fn CertificationRequest::attribute(
self : CertificationRequest,
oid : String,
) -> Attribute? {
for attribute in self.info.attributes {
if attribute.oid.to_string() == oid {
return Some(attribute)
}
}
None
}
///|
/// The extensions requested through the `extensionRequest` attribute, or an
/// empty array when that attribute is absent or malformed.
pub fn CertificationRequest::requested_extensions(
self : CertificationRequest,
) -> Array[Extension] {
let attribute = match self.attribute("1.2.840.113549.1.9.14") {
Some(attribute) => attribute
None => return []
}
let values = attribute.values
if values.length() == 0 {
return []
}
parse_extension_list(values[0]) catch {
_ => []
}
}
///|
/// The subject alternative names requested with the certificate.
pub fn CertificationRequest::subject_alt_names(
self : CertificationRequest,
) -> Array[GeneralName] {
for extension in self.requested_extensions() {
if extension.oid.to_string() == "2.5.29.17" {
let decoded = extension.decode() catch { _ => Other }
match decoded {
SubjectAltName(names) => return names
_ => ()
}
}
}
[]
}
///|
/// Renders a summary of the request.
pub fn CertificationRequest::to_string(self : CertificationRequest) -> String {
let buffer = StringBuilder()
buffer.write_string("Certification request\n")
buffer.write_string(" Version: ")
buffer.write_string(self.info.version.to_string())
buffer.write_string("\n")
buffer.write_string(" Subject: ")
buffer.write_string(self.info.subject.to_string())
buffer.write_string("\n")
buffer.write_string(" Public key: ")
buffer.write_string(key_description(self.info.subject_public_key_info))
buffer.write_string("\n")
buffer.write_string(" Signature algorithm: ")
buffer.write_string(self.signature_algorithm.name())
buffer.write_string(" (")
buffer.write_string(self.signature_algorithm.algorithm.to_string())
buffer.write_string(")\n")
buffer.write_string(" Signature: ")
buffer.write_string(self.signature.length().to_string())
buffer.write_string(" bytes\n")
buffer.write_string(" Attributes (")
buffer.write_string(self.info.attributes.length().to_string())
buffer.write_string(")\n")
for attribute in self.info.attributes {
buffer.write_string(" ")
buffer.write_string(request_attribute_display_name(attribute.oid))
buffer.write_string(" (")
buffer.write_string(attribute.oid.to_string())
buffer.write_string(")\n")
}
let extensions = self.requested_extensions()
if extensions.length() > 0 {
buffer.write_string(" Requested extensions (")
buffer.write_string(extensions.length().to_string())
buffer.write_string(")\n")
for extension in extensions {
buffer.write_string(" ")
buffer.write_string(extension.name())
buffer.write_string(" (")
buffer.write_string(extension.oid.to_string())
buffer.write_string(") critical=")
buffer.write_string(if extension.critical { "true" } else { "false" })
buffer.write_string("\n")
}
}
let alternative_names = self.subject_alt_names()
if alternative_names.length() > 0 {
buffer.write_string(" Subject alternative names (")
buffer.write_string(alternative_names.length().to_string())
buffer.write_string(")\n")
for name in alternative_names {
buffer.write_string(" ")
buffer.write_string(name.kind())
buffer.write_string(": ")
buffer.write_string(name.to_string())
buffer.write_string("\n")
}
}
buffer.to_string()
}
///|
/// Parses one `Attribute` of a certification request.
fn parse_request_attribute(value : @asn1.Der) -> Attribute raise X509Error {
let children = value.as_sequence() catch {
err => raise InvalidDer(err.to_string())
}
if children.length() != 2 {
raise Malformed("Attribute must have exactly two elements")
}
let oid = children[0].as_object_identifier() catch {
err => raise InvalidDer(err.to_string())
}
let values = children[1].as_sequence() catch {
err => raise InvalidDer(err.to_string())
}
{ oid, values, }
}
///|
/// Parses the CertificationRequestInfo.
fn parse_certification_request_info(
value : @asn1.Der,
) -> CertificationRequestInfo raise X509Error {
let children = value.as_sequence() catch {
err => raise InvalidDer(err.to_string())
}
if children.length() != 4 {
raise Malformed("CertificationRequestInfo must have four elements")
}
let version = children[0].as_integer() catch {
err => raise InvalidDer(err.to_string())
}
if version.to_int() != 0 {
raise Malformed("only PKCS#10 version 1 requests are supported")
}
let subject = parse_name(children[1])
let subject_public_key_info = parse_subject_public_key_info(children[2])
let attributes : Array[Attribute] = []
match children[3] {
@asn1.Constructed(tag, items) => {
if tag.cls != @asn1.ContextSpecific || tag.number != 0 {
raise Malformed("the attribute list must use the [0] tag")
}
for item in items {
attributes.push(parse_request_attribute(item))
}
}
@asn1.Primitive(_, _) =>
raise Malformed("the attribute list must be a constructed value")
}
{ version: 0, subject, subject_public_key_info, attributes, }
}
///|
/// Parses a certification request from its DER encoding.
pub fn parse_certification_request(
bytes : BytesView,
) -> CertificationRequest raise X509Error {
let raw = @buffer.from_bytes(bytes).to_bytes()
let value = @asn1.DerReader::new(bytes).read_single() catch {
err => raise InvalidDer(err.to_string())
}
let children = value.as_sequence() catch {
err => raise InvalidDer(err.to_string())
}
if children.length() != 3 {
raise Malformed("CertificationRequest must have three elements")
}
let info = parse_certification_request_info(children[0])
let signature_algorithm = parse_algorithm_identifier(children[1])
let (unused_bits, signature) = children[2].as_bit_string() catch {
err => raise InvalidDer(err.to_string())
}
if unused_bits != 0 {
raise Malformed("signature BIT STRING must have no unused bits")
}
{ raw, info, signature_algorithm, signature, }
}
///|
/// Parses a certification request from PEM text.
pub fn parse_pem_certification_request(
text : StringView,
) -> CertificationRequest raise X509Error {
let block = @pem.decode_pem_single(text) catch {
err => raise InvalidPem(err.to_string())
}
parse_certification_request(block.data[:])
}