1 use crate::{oids, AlgorithmIdentifier, Extension, Extensions, Name, SubjectPublicKeyInfo}; 2 use picky_asn1::tag::Tag; 3 use picky_asn1::wrapper::{Asn1SetOf, BitStringAsn1, ObjectIdentifierAsn1}; 4 use serde::{de, ser, Deserialize, Serialize}; 5 6 /// [RFC 2986 #4](https://tools.ietf.org/html/rfc2986#section-4) 7 /// 8 /// ```not_rust 9 /// CertificationRequestInfo ::= SEQUENCE { 10 /// version INTEGER { v1(0) } (v1,...), 11 /// subject Name, 12 /// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, 13 /// attributes [0] Attributes{{ CRIAttributes }} 14 /// } 15 /// ``` 16 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 17 pub struct CertificationRequestInfo { 18 pub version: u8, 19 pub subject: Name, 20 pub subject_public_key_info: SubjectPublicKeyInfo, 21 pub attributes: Attributes, 22 } 23 24 impl CertificationRequestInfo { new(subject: Name, subject_public_key_info: SubjectPublicKeyInfo) -> Self25 pub fn new(subject: Name, subject_public_key_info: SubjectPublicKeyInfo) -> Self { 26 // It shall be 0 for this version of the standard. 27 Self { 28 version: 0, 29 subject, 30 subject_public_key_info, 31 attributes: Attributes(Vec::new()), 32 } 33 } 34 with_attribute(mut self, attribute: Attribute) -> Self35 pub fn with_attribute(mut self, attribute: Attribute) -> Self { 36 self.attributes.0.push(attribute); 37 self 38 } 39 add_attribute(&mut self, attribute: Attribute)40 pub fn add_attribute(&mut self, attribute: Attribute) { 41 self.attributes.0.push(attribute); 42 } 43 } 44 45 // FIXME: this type is a hack to workaround [this issue](https://github.com/Devolutions/picky-rs/pull/78#issuecomment-789904165). 46 // Further refactorings are required to clean this up (proper support for IMPLICIT / EXPLICIT tags, etc) 47 #[derive(Clone, Debug, PartialEq)] 48 pub struct Attributes(pub Vec<Attribute>); 49 50 impl ser::Serialize for Attributes { serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error> where S: ser::Serializer,51 fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error> 52 where 53 S: ser::Serializer, 54 { 55 let mut raw_der = picky_asn1_der::to_vec(&self.0).unwrap_or_default(); 56 raw_der[0] = Tag::APP_0.number(); 57 picky_asn1_der::Asn1RawDer(raw_der).serialize(serializer) 58 } 59 } 60 61 impl<'de> de::Deserialize<'de> for Attributes { deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error> where D: de::Deserializer<'de>,62 fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error> 63 where 64 D: de::Deserializer<'de>, 65 { 66 let mut raw_der = picky_asn1_der::Asn1RawDer::deserialize(deserializer)?.0; 67 raw_der[0] = Tag::SEQUENCE.number(); 68 let vec = picky_asn1_der::from_bytes(&raw_der).unwrap_or_default(); 69 Ok(Attributes(vec)) 70 } 71 } 72 73 /// [RFC 2985 page 15 and 16](https://tools.ietf.org/html/rfc2985#page-15) 74 /// 75 /// Accepted attribute types are `challengePassword` and `extensionRequest` 76 /// 77 #[derive(Clone, Debug, PartialEq)] 78 pub enum AttributeValue { 79 /// `extensionRequest` 80 Extensions(Asn1SetOf<Extensions>), // the set will always have 1 element in this variant 81 // TODO: support for challenge password 82 // ChallengePassword(Asn1SetOf<ChallengePassword>)) 83 Custom(picky_asn1_der::Asn1RawDer), // fallback 84 } 85 86 #[derive(Clone, Debug, PartialEq)] 87 pub struct Attribute { 88 pub ty: ObjectIdentifierAsn1, 89 pub value: AttributeValue, 90 } 91 92 impl Attribute { new_extension_request(extensions: Vec<Extension>) -> Self93 pub fn new_extension_request(extensions: Vec<Extension>) -> Self { 94 Self { 95 ty: oids::extension_request().into(), 96 value: AttributeValue::Extensions(Asn1SetOf(vec![Extensions(extensions)])), 97 } 98 } 99 } 100 101 impl ser::Serialize for Attribute { serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error> where S: ser::Serializer,102 fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error> 103 where 104 S: ser::Serializer, 105 { 106 use ser::SerializeSeq; 107 let mut seq = serializer.serialize_seq(Some(2))?; 108 seq.serialize_element(&self.ty)?; 109 match &self.value { 110 AttributeValue::Extensions(extensions) => seq.serialize_element(extensions)?, 111 AttributeValue::Custom(der) => seq.serialize_element(der)?, 112 } 113 seq.end() 114 } 115 } 116 117 impl<'de> de::Deserialize<'de> for Attribute { deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error> where D: de::Deserializer<'de>,118 fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error> 119 where 120 D: de::Deserializer<'de>, 121 { 122 use std::fmt; 123 124 struct Visitor; 125 126 impl<'de> de::Visitor<'de> for Visitor { 127 type Value = Attribute; 128 129 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 130 formatter.write_str("a valid DER-encoded attribute") 131 } 132 133 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> 134 where 135 A: de::SeqAccess<'de>, 136 { 137 let ty: ObjectIdentifierAsn1 = seq_next_element!(seq, Attribute, "type oid"); 138 139 let value = match Into::<String>::into(&ty.0).as_str() { 140 oids::EXTENSION_REQ => { 141 AttributeValue::Extensions(seq_next_element!(seq, Attribute, "at extension request")) 142 } 143 _ => AttributeValue::Custom(seq_next_element!(seq, Attribute, "at custom value")), 144 }; 145 146 Ok(Attribute { ty, value }) 147 } 148 } 149 150 deserializer.deserialize_seq(Visitor) 151 } 152 } 153 154 /// [RFC 2986 #4](https://tools.ietf.org/html/rfc2986#section-4) 155 /// 156 /// ```not_rust 157 /// CertificationRequest ::= SEQUENCE { 158 /// certificationRequestInfo CertificationRequestInfo, 159 /// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, 160 /// signature BIT STRING 161 /// } 162 /// ``` 163 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 164 pub struct CertificationRequest { 165 pub certification_request_info: CertificationRequestInfo, 166 pub signature_algorithm: AlgorithmIdentifier, 167 pub signature: BitStringAsn1, 168 } 169 170 #[cfg(test)] 171 mod tests { 172 use super::*; 173 use crate::name::*; 174 use crate::{DirectoryName, GeneralName}; 175 use picky_asn1::bit_string::BitString; 176 use picky_asn1::restricted_string::{IA5String, PrintableString, Utf8String}; 177 use picky_asn1::wrapper::IntegerAsn1; 178 use std::str::FromStr; 179 180 #[test] basic_csr()181 fn basic_csr() { 182 let encoded = base64::decode( 183 "MIICYjCCAUoCAQAwHTEbMBkGA1UEAxMSdGVzdC5jb250b3NvLmxvY2FsMIIBIjAN\ 184 BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAym0At2TvEqP0mYVLJzGVpNXjugu/\ 185 kBpuKvXt/Vax4Bxnj3YzHTCpwkyZPytUC6zJ+q+uGh0e7gYQsYHJKjgoKEsS6gQ4\ 186 ZM3D/AQy0zqPUT0ruSKDWKK4f2d/2ijDs5R2LHj7DtNZBanCXU16Qp1O28su0QZK\ 187 OYbXzsJSpHp80dhqD6JUxXlSZzlVBp28CC9ryrE6w+kOQ38TZ1/mBJPsfmDeKBpm\ 188 3FRrfHtWt43eok/T6FhCLIzsqyCZ0UCQqkcLr+TfoftJe2nOHQ1sfk4keJ9iwA/f\ 189 hYv5rqUB3RUztSIhExwtYDwd+YovenhsL4sW/kjR29RTLUFPPXAelG9XPwIDAQAB\ 190 oAAwDQYJKoZIhvcNAQELBQADggEBAKrCf4sFDBFZQ6CPYdaxe3InMp7KFaueMIB8\ 191 /YK73rJ+JGB6fQfltCCkToTE1y0Q3UqTlqHmaqdoh0KMWue6jCFvBat4/TUqUG7W\ 192 tRLDP67eMulolcIzLqwTjR38DVJvnwrd2pey43q3UHBjlStxT/gI4ysQHn4qrzHB\ 193 6OK9O6ypqTtwXxnm3TJF9dctLwvbh7NZSaamSlxI0/ajKZOP9k1KZEOPtaiiMPe2\ 194 yr+QvwY2ov66MRG5PPRZELQWBaPZOuFwmCsFOLXJMpvhoAgklBCFZmiQMgApGIC1\ 195 FIDgjm2ZhQQIRMnTsAV6f7BclRTaUkc0sPl17YB9GfNfOm1oL7o=", 196 ) 197 .expect("invalid base64"); 198 199 let certification_request_info = CertificationRequestInfo::new( 200 DirectoryName::new_common_name(PrintableString::from_str("test.contoso.local").unwrap()).into(), 201 SubjectPublicKeyInfo::new_rsa_key( 202 IntegerAsn1::from(encoded[74..331].to_vec()), 203 IntegerAsn1::from(encoded[333..336].to_vec()), 204 ), 205 ); 206 207 check_serde!(certification_request_info: CertificationRequestInfo in encoded[4..338]); 208 209 let csr = CertificationRequest { 210 certification_request_info, 211 signature_algorithm: AlgorithmIdentifier::new_sha256_with_rsa_encryption(), 212 signature: BitString::with_bytes(&encoded[358..614]).into(), 213 }; 214 215 check_serde!(csr: CertificationRequest in encoded); 216 } 217 218 #[test] csr_with_extensions_attribute()219 fn csr_with_extensions_attribute() { 220 let encoded = base64::decode( 221 "MIICjDCCAXQCAQAwIDELMAkGA1UEBhMCWFgxETAPBgNVBAMMCHNvbWV0ZXN0MIIB\ 222 IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNELh212N4optYS7pqbtvjyv\ 223 +t4fQjX/pwB88BUCEjBgh+DJ49EBPQg9oObADTcBi3EeXu4M5y6f/dzIhovayJ/y\ 224 9j7Cj0Bw+VY+eRXywkVG/DqaiKG2mIQW+fho7/jhazhpeIxCzObPTwiQK7i96Vjq\ 225 9S+o4QQejE2SYLOhQ4/cgUaT7JBm4yab7cvhFjKYjVmoP6ioIcHb9Cmv25Lttuvk\ 226 n64bDiPKz6BkutRpbMipQjSA8xKEgjgFG/nxBynA8PXnZIunhTNyhXrqRoAe6SXn\ 227 ZLZLmwOkeU5WTewVVTXlmqaZTPwtb/9EjjoRnO3+Ulb5zT5wPULc79xuY16kzwID\ 228 AQABoCcwJQYJKoZIhvcNAQkOMRgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJ\ 229 KoZIhvcNAQELBQADggEBAIm9lOhZG3XY4CNJ5b18Qu/OfFi+T0tgxt4bTqINQ1Iz\ 230 SQFrsnheBrzmasfFliz10N96cOmNka1UpWqK7N5/TfkJHX3zKYRpc2jEkrFun48B\ 231 3+bOJJPH48zmTGxBgU7iiorpaVt3CpgXNswhU3fpcT5gLy8Ys7DXC39Nn1lW0Lko\ 232 cd6xK4oIJyoeiXyVBdn68gtPY6xjFxta67nyj39sSGhATxrDgxtLHEH2+HStywr0\ 233 4/osg9vP/OH5iFYOiEimK6ErYNg8rM1A/OTe5p8emA6y3o5dHG8lKYwevyUXMSLv\ 234 38CNeh0MS2KmyHz2085HlIIAXIu2xAUyWLsQik+eV6M=", 235 ) 236 .expect("invalid base64"); 237 238 let extensions = vec![Extension::new_subject_alt_name(vec![GeneralName::DNSName( 239 IA5String::from_string("localhost".into()).unwrap().into(), 240 )]) 241 .into_non_critical()]; 242 243 let mut dn = DirectoryName::new(); 244 dn.add_attr(NameAttr::CountryName, PrintableString::from_str("XX").unwrap()); 245 dn.add_attr(NameAttr::CommonName, Utf8String::from_str("sometest").unwrap()); 246 247 let certification_request_info = CertificationRequestInfo::new( 248 dn.into(), 249 SubjectPublicKeyInfo::new_rsa_key( 250 IntegerAsn1::from(encoded[77..334].to_vec()), 251 IntegerAsn1::from(encoded[336..339].to_vec()), 252 ), 253 ) 254 .with_attribute(Attribute::new_extension_request(extensions)); 255 256 check_serde!(certification_request_info: CertificationRequestInfo in encoded[4..380]); 257 258 let csr = CertificationRequest { 259 certification_request_info, 260 signature_algorithm: AlgorithmIdentifier::new_sha256_with_rsa_encryption(), 261 signature: BitString::with_bytes(&encoded[400..656]).into(), 262 }; 263 264 check_serde!(csr: CertificationRequest in encoded); 265 } 266 } 267