use der_parser::asn1_rs::Tag; use der_parser::num_bigint::BigUint; use url::Url; use time::OffsetDateTime; use x509_parser::x509::AlgorithmIdentifier; use x509_parser::prelude::{Validity, KeyUsage, X509Certificate, FromDer, X509Version, X509Extension, ParsedExtension, CRLDistributionPoints, DistributionPointName, GeneralName}; use crate::data_model::crl::CrlDecodeError; use crate::data_model::resources::ip_resources::IPAddrBlocks; use crate::data_model::resources::as_resources::ASIdentifiers; use crate::data_model::oids; #[derive(Clone, Debug, PartialEq, Eq)] pub struct SubjectPublicKeyInfo { pub algorithm_oid: String, pub subject_public_key: u8, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct AccessDescription { pub access_method_oid: String, pub access_location: Url, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PolicyInformation { pub policy_oid: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RcExtension { pub basic_constraints: bool, pub subject_key_identifier: u8, pub authority_key_identifier: u8, pub key_usage: KeyUsage, pub extended_key_usage_oid: u8, pub crl_distribution_points: Vec, pub authority_info_access: Vec, pub subject_info_access: Vec, pub certificate_policies: Vec, pub ip_resource: IPAddrBlocks, pub as_resource: ASIdentifiers, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct ResourceCert { /// 证书原始DER内容 pub cert_der: Vec, /// 基本证书信息 pub version: u32, pub serial_number: BigUint, pub signature_algorithm_oid: String, pub issuer_dn: String, pub subject_dn: String, pub validity: Validity, pub subject_public_key_info: SubjectPublicKeyInfo, pub extensions: RcExtension, } #[derive(Debug, thiserror::Error)] pub enum ResourceCertError { #[error("X.509 parse resource cert error: {0}")] ParseCert(String), #[error("trailing bytes after CRL DER: {0} bytes")] TrailingBytes(usize), #[error("invalid version {0}")] InvalidVersion(u32), #[error("signatureAlgorithm does not match tbsCertificate.signature")] SignatureAlgorithmMismatch, #[error("unsupported signature algorithm")] UnsupportedSignatureAlgorithm, #[error("invalid Cert signature algorithm parameters")] InvalidSignatureParameters, #[error("invalid Cert validity range")] InvalidValidityRange, #[error("Cert not yet valid")] NotYetValid, #[error("expired")] Expired, #[error("Critical error, {0} should be {1}")] CriticalError(String, String), #[error("Duplicate Extension: {0}")] DuplicateExtension(String), #[error("AKI missing keyIdentifier")] AkiMissingKeyIdentifier, #[error("Unexpected parameter: {0}")] UnexceptedParameter(String), #[error("Missing parameter: {0}")] MissingParameter(String), #[error("CRL DP invalid distributionPointName: {0}")] CrlDpInvalidDistributionPointName(String), #[error("CRL DP unexpected distributionPointType: {0}")] CrlDpUnexpectedDistributionPointType(String), #[error("invalid URI: {0}")] InvalidUri(String), #[error("Unsupported General Name in {0}")] UnsupportedGeneralName(String), #[error("Unsupported CRL Distribution Point")] UnsupportedCrlDistributionPoint, #[error("Invalid Access Location Type")] InvalidAccessLocationType, #[error("Empty AuthorityInfoAccess!")] EmptyAuthorityInfoAccess, } // impl ResourceCert{ // pub fn from_der(cert_der: &[u8]) -> Result { // let (rem, x509_rc) = X509Certificate::from_der(cert_der) // .map_err(|e| ResourceCertError::ParseCert(e.to_string()))?; // // if !rem.is_empty() { // return Err(ResourceCertError::TrailingBytes(rem.len())); // } // // // 校验 // parse_and_validate_cert(x509_rc) // } // // // // } // // fn parse_and_validate_cert(x509_rc: X509Certificate) -> Result { // ///逐个校验RC的内容, 如果有任何一个校验失败, 则返回错误 // // // 1. 版本号必须是V3 // let version = match x509_rc.version() { // X509Version::V3 => X509Version::V3, // v => { // return Err(ResourceCertError::InvalidVersion(v.0)); // } // }; // // // 2.校验签名算法 // // 2.1. 校验外层的签名算法与里层的一致 // let outer = &x509_rc.signature_algorithm; // let inner = &x509_rc.tbs_certificate.signature; // // if outer.algorithm != inner.algorithm || outer.parameters != inner.parameters { // return Err(ResourceCertError::SignatureAlgorithmMismatch); // } // //2.2 RPKI的签名算法必须是rsaWithSHA256 // let signature_algorithm = &x509_rc.signature_algorithm; // if signature_algorithm.algorithm.to_id_string() != oids::OID_SHA256_WITH_RSA_ENCRYPTION { // return Err(ResourceCertError::UnsupportedSignatureAlgorithm); // } // validate_sig_params(signature_algorithm)?; // // // 3. 校验Validity // let validity = x509_rc.validity(); // validate_validity(validity, OffsetDateTime::now_utc())?; // // // 4. SubjectPublicKeyInfo // let subject_public_key_info = x509_rc.tbs_certificate.subject_pki; // // let extensions = parse_and_validate_extensions(x509_rc.extensions())?; // // Ok(ResourceCert { // cert_der: x509_rc.to_der().to_vec(), // version: version.0, // serial_number: x509_rc.serial(), // signature_algorithm_oid: signature_algorithm.algorithm.to_id_string(), // issuer_dn: x509_rc.issuer().to_string(), // subject_dn: x509_rc.subject().to_string(), // validity, // subject_public_key_info: SubjectPublicKeyInfo { // // algorithm_oid: x509_rc.tbs_certificate.subject_pki.algorithm.algorithm.to_id_string(), // // subject_public_key: x509_rc.tbs_certificate.subject_pki.subject_public_key.unused_bits, // }, // extensions, // }) // // // } // // fn validate_sig_params(sig: &AlgorithmIdentifier<'_>) -> Result<(), CrlDecodeError> { // match sig.parameters.as_ref() { // None => Ok(()), // Some(p) if p.tag() == Tag::Null => Ok(()), // Some(_p) => Err(CrlDecodeError::InvalidSignatureAlgorithmParameters), // } // } // // fn validate_validity( // validity: &Validity, // now: OffsetDateTime, // ) -> Result<(), ResourceCertError> { // let not_before = validity.not_before.to_datetime(); // let not_after = validity.not_after.to_datetime(); // // if not_after < not_before { // return Err(ResourceCertError::InvalidValidityRange); // } // // if now < not_before { // return Err(ResourceCertError::NotYetValid); // } // // if now > not_after { // return Err(ResourceCertError::Expired); // } // // Ok(()) // } // // // pub fn parse_and_validate_extensions( // exts: &[X509Extension<'_>], // ) -> Result { // let mut basic_constraints = None; // let mut ip_addr_blocks = None; // let mut as_identifiers = None; // let mut ski = None; // let mut aki = None; // let mut crl_dp = None; // let mut aia = None; // let mut sia = None; // let mut key_usage = None; // let mut extended_key_usage = None; // let mut certificate_policies = None; // // for ext in exts { // let oid = ext.oid.to_id_string(); // let critical = ext.critical; // match oid.as_str() { // oids::OID_BASIC_CONSTRAINTS => { // if basic_constraints.is_some() { // return Err(ResourceCertError::DuplicateExtension("basicConstraints".into())); // } // if !critical { // return Err(ResourceCertError::CriticalError("basicConstraints".into(), "critical".into())); // } // let bc = parse_basic_constraints(ext)?; // basic_constraints = Some(bc); // } // oids::OID_SUBJECT_KEY_IDENTIFIER => { // if ski.is_some() { // return Err(ResourceCertError::DuplicateExtension("subjectKeyIdentifier".into())); // } // if critical { // return Err(ResourceCertError::CriticalError("subjectKeyIdentifier".into(), "non-critical".into())); // } // let s = parse_subject_key_identifier(ext)?; // ski = Some(s); // } // oids::OID_AUTHORITY_KEY_IDENTIFIER => { // if aki.is_some() { // return Err(ResourceCertError::DuplicateExtension("authorityKeyIdentifier".into())); // } // if critical { // return Err(ResourceCertError::CriticalError("authorityKeyIdentifier".into(), "non-critical".into())); // } // let a = parse_authority_key_identifier(ext)?; // aki = Some(a); // } // oids::OID_KEY_USAGE => { // if key_usage.is_some() { // return Err(ResourceCertError::DuplicateExtension("keyUsage".into())); // } // if !critical { // return Err(ResourceCertError::CriticalError("keyUsage".into(), "critical".into())); // } // let ku = parse_key_usage(ext)?; // key_usage = Some(ku); // } // oids::OID_EXTENDED_KEY_USAGE => { // if extended_key_usage.is_some() { // return Err(ResourceCertError::DuplicateExtension("extendedKeyUsage".into())); // } // if critical { // return Err(ResourceCertError::CriticalError("extendedKeyUsage".into(), "non-critical".into())); // } // let eku = oids::OID_EXTENDED_KEY_USAGE; // } // oids::OID_CRL_DISTRIBUTION_POINTS => { // if crl_dp.is_some() { // return Err(ResourceCertError::DuplicateExtension("crlDistributionPoints".into())); // } // if critical { // return Err(ResourceCertError::CriticalError("crlDistributionPoints".into(), "non-critical".into())); // } // let cdp = parse_crl_distribution_points(ext)?; // crl_dp = Some(cdp); // } // oids::OID_AUTHORITY_INFO_ACCESS => { // if aia.is_some() { // return Err(ResourceCertError::DuplicateExtension("authorityInfoAccess".into())); // } // if critical { // return Err(ResourceCertError::CriticalError("authorityInfoAccess".into(), "non-critical".into())); // } // let p_aia = parse_authority_info_access(ext)?; // aia = Some(p_aia); // } // oids::OID_SUBJECT_INFO_ACCESS => { // if sia.is_some() { // return Err(ResourceCertError::DuplicateExtension("subjectInfoAccess".into())); // } // if critical { // return Err(ResourceCertError::CriticalError("subjectInfoAccess".into(), "non-critical".into())); // } // let p_sia = parse_subject_info_access(ext)?; // sia = Some(p_sia); // } // oids::OID_CERTIFICATE_POLICIES => { // if certificate_policies.is_some() { // return Err(ResourceCertError::DuplicateExtension("certificatePolicies".into())); // } // if !critical { // return Err(ResourceCertError::CriticalError("certificatePolicies".into(), "critical".into())); // } // let p_cp = parse_certificate_policies(ext)?; // certificate_policies = Some(p_cp); // } // } // // // } // Ok(RcExtension { // basic_constraints, // ip_addr_blocks, // as_identifiers, // subject_key_id: ski, // authority_key_id: aki, // crl_distribution_points: crl_dp, // authority_info_access: aia, // }) // } // // fn parse_basic_constraints(ext: &X509Extension<'_>) -> Result { // let ParsedExtension::BasicConstraints(bc) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert("basicConstraints parse failed".into())); // }; // Ok(bc.ca) // } // // fn parse_subject_key_identifier(ext: &X509Extension<'_>) -> Result, ResourceCertError> { // let ParsedExtension::SubjectKeyIdentifier(s) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert("subjectKeyIdentifier parse failed".into())); // }; // Ok(s.0.to_vec()) // } // // fn parse_authority_key_identifier(ext: &X509Extension<'_>) -> Result, ResourceCertError> { // let ParsedExtension::AuthorityKeyIdentifier(aki) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert("authorityKeyIdentifier parse failed".into())); // }; // let key_id = aki // .key_identifier // .as_ref() // .ok_or(ResourceCertError::MissingParameter("key_identifier".into()))?; // // if aki.authority_cert_issuer.is_some() { // return Err(ResourceCertError::UnexceptedParameter("authority_cert_issuer".into())); // } // if aki.authority_cert_serial.is_some() { // return Err(ResourceCertError::UnexceptedParameter("authority_cert_serial".into())); // } // // // Ok(key_id.0.to_vec()) // } // // fn parse_key_usage(ext: &X509Extension<'_>) -> Result { // let ParsedExtension::KeyUsage(ku) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert("keyUsage parse failed".into())); // }; // Ok(ku.clone()) // } // // fn parse_crl_distribution_points(ext: &X509Extension<'_>) -> Result, ResourceCertError> { // let ParsedExtension::CRLDistributionPoints(cdp) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert("crlDistributionPoints parse failed".into())); // }; // let mut urls = Vec::new(); // for point in cdp.points.iter() { // if point.reasons.is_some() { // return Err(ResourceCertError::UnexceptedParameter("reasons".into())); // } // if point.crl_issuer.is_some() { // return Err(ResourceCertError::UnexceptedParameter("crl_issuer".into())); // } // // let dp_name = point.distribution_point.as_ref() // .ok_or(ResourceCertError::MissingParameter("distribution_point".into()))?; // match dp_name { // DistributionPointName::FullName(names) => { // for name in names { // match name { // GeneralName::URI(uri) => { // let url = Url::parse(uri) // .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; // urls.push(url); // } // _ => { // return Err(ResourceCertError::UnsupportedGeneralName("distribution_point".into())); // } // } // } // // } // DistributionPointName::NameRelativeToCRLIssuer(_) => { // return Err(ResourceCertError::UnsupportedCrlDistributionPoint); // } // } // } // if urls.is_empty() { // return Err(ResourceCertError::MissingParameter("distribution_point".into())); // } // Ok(urls) // } // // fn parse_authority_info_access( // ext: &X509Extension<'_>, // ) -> Result, ResourceCertError> { // let ParsedExtension::AuthorityInfoAccess(aia) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert( // "authorityInfoAccess parse failed".into(), // )); // }; // // let mut access_descriptions = Vec::new(); // // for access in &aia.accessdescs { // let access_method_oid = access.access_method.to_id_string(); // // let uri = match &access.access_location { // GeneralName::URI(uri) => uri, // _ => { // return Err(ResourceCertError::InvalidAccessLocationType); // } // }; // // let url = Url::parse(uri) // .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; // // access_descriptions.push(AccessDescription { // access_method_oid, // access_location: url, // }); // } // // if access_descriptions.is_empty() { // return Err(ResourceCertError::EmptyAuthorityInfoAccess); // } // // Ok(access_descriptions) // } // // fn parse_subject_info_access(ext: &X509Extension<'_>) -> Result, ResourceCertError> { // let ParsedExtension::SubjectInfoAccess(sia) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert( // "subjectInfoAccess parse failed".into(), // )); // }; // let mut access_descriptions = Vec::new(); // // for access in &sia.accessdescs { // let access_method_oid = access.access_method.to_id_string(); // // // accessLocation: MUST be URI in RPKI // let uri = match &access.access_location { // GeneralName::URI(uri) => uri, // _ => { // return Err(ResourceCertError::InvalidAccessLocationType); // } // }; // // let url = Url::parse(uri) // .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; // // access_descriptions.push(AccessDescription { // access_method_oid, // access_location: url, // }); // } // // if access_descriptions.is_empty() { // return Err(ResourceCertError::EmptyAuthorityInfoAccess); // } // // Ok(access_descriptions) // } // // fn parse_certificate_policies(ext: &X509Extension<'_>) -> Result, ResourceCertError> { // let ParsedExtension::CertificatePolicies(cp) = ext.parsed_extension() else { // return Err(ResourceCertError::ParseCert( // "certificatePolicies parse failed".into(), // )); // }; // let mut policies = Vec::new(); // // }