11// Copyright 2018-2026 the Deno authors. MIT license.
22
3+ use std:: collections:: HashMap ;
34use std:: net:: IpAddr ;
45use std:: net:: Ipv4Addr ;
56use std:: net:: Ipv6Addr ;
@@ -41,6 +42,8 @@ struct SubjectOrIssuer {
4142 ou : Option < String > ,
4243 #[ serde( skip_serializing_if = "Option::is_none" ) ]
4344 cn : Option < String > ,
45+ #[ serde( rename = "emailAddress" , skip_serializing_if = "Option::is_none" ) ]
46+ email_address : Option < String > ,
4447}
4548
4649#[ derive( serde:: Serialize ) ]
@@ -57,6 +60,8 @@ pub struct CertificateObject {
5760 fingerprint256 : String ,
5861 fingerprint512 : String ,
5962 subjectaltname : String ,
63+ #[ serde( rename = "infoAccess" , skip_serializing_if = "Option::is_none" ) ]
64+ info_access : Option < HashMap < String , Vec < String > > > ,
6065 // RSA key fields
6166 #[ serde( skip_serializing_if = "Option::is_none" ) ]
6267 bits : Option < u32 > ,
@@ -143,8 +148,16 @@ impl Certificate {
143148 CertificateSources :: Der ( der) => der. to_vec ( ) ,
144149 } ;
145150
146- let valid_from = cert. validity ( ) . not_before . to_string ( ) ;
147- let valid_to = cert. validity ( ) . not_after . to_string ( ) ;
151+ let valid_from = cert
152+ . validity ( )
153+ . not_before
154+ . to_string ( )
155+ . replace ( "+00:00" , "GMT" ) ;
156+ let valid_to = cert
157+ . validity ( )
158+ . not_after
159+ . to_string ( )
160+ . replace ( "+00:00" , "GMT" ) ;
148161
149162 let mut serial_number = cert. serial . to_str_radix ( 16 ) ;
150163 serial_number. make_ascii_uppercase ( ) ;
@@ -154,6 +167,7 @@ impl Certificate {
154167 let fingerprint512 = self . fingerprint :: < sha2:: Sha512 > ( ) . unwrap_or_default ( ) ;
155168
156169 let subjectaltname = get_subject_alt_name ( cert) . unwrap_or_default ( ) ;
170+ let info_access = get_info_access_object ( cert) ;
157171
158172 let subject = extract_subject_or_issuer ( cert. subject ( ) ) ;
159173 let issuer = extract_subject_or_issuer ( cert. issuer ( ) ) ;
@@ -179,6 +193,7 @@ impl Certificate {
179193 fingerprint256,
180194 fingerprint512,
181195 subjectaltname,
196+ info_access,
182197 bits,
183198 exponent,
184199 modulus,
@@ -383,6 +398,9 @@ fn extract_subject_or_issuer(name: &X509Name) -> SubjectOrIssuer {
383398 oid if oid == & x509_parser:: oid_registry:: OID_X509_COMMON_NAME => {
384399 result. cn = Some ( value_str) ;
385400 }
401+ oid if oid == & x509_parser:: oid_registry:: OID_PKCS9_EMAIL_ADDRESS => {
402+ result. email_address = Some ( value_str) ;
403+ }
386404 _ => { }
387405 }
388406 }
@@ -435,9 +453,15 @@ fn x509name_to_string(
435453 let val_str =
436454 attribute_value_to_string ( attr. attr_value ( ) , attr. attr_type ( ) ) ?;
437455 // look ABBREV, and if not found, use shortname
438- let abbrev = match oid2abbrev ( attr. attr_type ( ) , oid_registry) {
439- Ok ( s) => String :: from ( s) ,
440- _ => format ! ( "{:?}" , attr. attr_type( ) ) ,
456+ let abbrev = if * attr. attr_type ( )
457+ == x509_parser:: oid_registry:: OID_PKCS9_EMAIL_ADDRESS
458+ {
459+ String :: from ( "emailAddress" )
460+ } else {
461+ match oid2abbrev ( attr. attr_type ( ) , oid_registry) {
462+ Ok ( s) => String :: from ( s) ,
463+ _ => format ! ( "{:?}" , attr. attr_type( ) ) ,
464+ }
441465 } ;
442466 let rdn = format ! ( "{}={}" , abbrev, val_str) ;
443467 match acc2. len ( ) {
@@ -456,14 +480,22 @@ fn x509name_to_string(
456480#[ string]
457481pub fn op_node_x509_get_valid_from ( #[ cppgc] cert : & Certificate ) -> String {
458482 let cert = cert. inner . get ( ) . deref ( ) ;
459- cert. validity ( ) . not_before . to_string ( )
483+ cert
484+ . validity ( )
485+ . not_before
486+ . to_string ( )
487+ . replace ( "+00:00" , "GMT" )
460488}
461489
462490#[ op2]
463491#[ string]
464492pub fn op_node_x509_get_valid_to ( #[ cppgc] cert : & Certificate ) -> String {
465493 let cert = cert. inner . get ( ) . deref ( ) ;
466- cert. validity ( ) . not_after . to_string ( )
494+ cert
495+ . validity ( )
496+ . not_after
497+ . to_string ( )
498+ . replace ( "+00:00" , "GMT" )
467499}
468500
469501#[ op2]
@@ -509,9 +541,25 @@ fn extract_key_info(spki: &x509_parser::x509::SubjectPublicKeyInfo) -> KeyInfo {
509541 let modulus_bytes = key. modulus ;
510542 let exponent_bytes = key. exponent ;
511543
512- let bits = Some ( ( modulus_bytes. len ( ) * 8 ) as u32 ) ;
513- let modulus = Some ( data_encoding:: HEXUPPER . encode ( modulus_bytes) ) ;
514- let exponent = Some ( data_encoding:: HEXUPPER . encode ( exponent_bytes) ) ;
544+ // Strip leading zero byte used for ASN.1 positive integer encoding
545+ let modulus_trimmed = if modulus_bytes. first ( ) == Some ( & 0 ) {
546+ & modulus_bytes[ 1 ..]
547+ } else {
548+ modulus_bytes
549+ } ;
550+ let bits = Some ( ( modulus_trimmed. len ( ) * 8 ) as u32 ) ;
551+ let modulus = Some ( data_encoding:: HEXUPPER . encode ( modulus_trimmed) ) ;
552+ // Format exponent as "0x" + hex without leading zeros (e.g., "0x10001")
553+ let exp_hex = data_encoding:: HEXLOWER . encode ( exponent_bytes) ;
554+ let exp_trimmed = exp_hex. trim_start_matches ( '0' ) ;
555+ let exponent = Some ( format ! (
556+ "0x{}" ,
557+ if exp_trimmed. is_empty( ) {
558+ "0"
559+ } else {
560+ exp_trimmed
561+ }
562+ ) ) ;
515563 let pubkey = Some ( spki. raw . to_vec ( ) ) ;
516564
517565 KeyInfo {
@@ -643,6 +691,60 @@ fn get_subject_alt_name(cert: &X509Certificate) -> Option<String> {
643691 }
644692}
645693
694+ fn get_info_access_object (
695+ cert : & X509Certificate ,
696+ ) -> Option < HashMap < String , Vec < String > > > {
697+ let oid_aia = Oid :: from ( & [ 1 , 3 , 6 , 1 , 5 , 5 , 7 , 1 , 1 ] ) . ok ( ) ?;
698+ let oid_ocsp = Oid :: from ( & [ 1 , 3 , 6 , 1 , 5 , 5 , 7 , 48 , 1 ] ) . ok ( ) ?;
699+ let oid_ca_issuers = Oid :: from ( & [ 1 , 3 , 6 , 1 , 5 , 5 , 7 , 48 , 2 ] ) . ok ( ) ?;
700+
701+ let ext = cert. extensions ( ) . iter ( ) . find ( |e| e. oid == oid_aia) ?;
702+
703+ let data = ext. value ;
704+ let ( _, seq) =
705+ x509_parser:: der_parser:: asn1_rs:: Sequence :: from_der ( data) . ok ( ) ?;
706+
707+ let mut result: HashMap < String , Vec < String > > = HashMap :: new ( ) ;
708+ let mut remaining = seq. content . as_ref ( ) ;
709+
710+ while !remaining. is_empty ( ) {
711+ let ( rest, access_desc) =
712+ x509_parser:: der_parser:: asn1_rs:: Sequence :: from_der ( remaining) . ok ( ) ?;
713+ remaining = rest;
714+
715+ let ( general_name_data, method_oid) =
716+ Oid :: from_der ( access_desc. content . as_ref ( ) ) . ok ( ) ?;
717+
718+ let method_name = if method_oid == oid_ocsp {
719+ "OCSP - URI"
720+ } else if method_oid == oid_ca_issuers {
721+ "CA Issuers - URI"
722+ } else {
723+ continue ;
724+ } ;
725+
726+ if !general_name_data. is_empty ( ) {
727+ let ( _, any) =
728+ x509_parser:: der_parser:: asn1_rs:: Any :: from_der ( general_name_data)
729+ . ok ( ) ?;
730+ if any. tag ( ) . 0 == 6
731+ && let Ok ( uri) = std:: str:: from_utf8 ( any. data )
732+ {
733+ result
734+ . entry ( method_name. to_string ( ) )
735+ . or_default ( )
736+ . push ( uri. to_string ( ) ) ;
737+ }
738+ }
739+ }
740+
741+ if result. is_empty ( ) {
742+ None
743+ } else {
744+ Some ( result)
745+ }
746+ }
747+
646748#[ op2]
647749#[ string]
648750pub fn op_node_x509_to_string ( #[ cppgc] cert : & Certificate ) -> String {
@@ -1087,6 +1189,44 @@ pub fn op_node_x509_verify(
10871189 }
10881190}
10891191
1192+ /// Map well-known signature algorithm OIDs to their OpenSSL names.
1193+ fn sig_alg_oid_to_name ( oid : & str ) -> Option < & ' static str > {
1194+ match oid {
1195+ "1.2.840.113549.1.1.4" => Some ( "md5WithRSAEncryption" ) ,
1196+ "1.2.840.113549.1.1.5" => Some ( "sha1WithRSAEncryption" ) ,
1197+ "1.2.840.113549.1.1.11" => Some ( "sha256WithRSAEncryption" ) ,
1198+ "1.2.840.113549.1.1.12" => Some ( "sha384WithRSAEncryption" ) ,
1199+ "1.2.840.113549.1.1.13" => Some ( "sha512WithRSAEncryption" ) ,
1200+ "1.2.840.113549.1.1.10" => Some ( "rsassaPss" ) ,
1201+ "1.2.840.10045.4.1" => Some ( "ecdsa-with-SHA1" ) ,
1202+ "1.2.840.10045.4.3.2" => Some ( "ecdsa-with-SHA256" ) ,
1203+ "1.2.840.10045.4.3.3" => Some ( "ecdsa-with-SHA384" ) ,
1204+ "1.2.840.10045.4.3.4" => Some ( "ecdsa-with-SHA512" ) ,
1205+ "1.3.101.112" => Some ( "ED25519" ) ,
1206+ "1.3.101.113" => Some ( "ED448" ) ,
1207+ _ => None ,
1208+ }
1209+ }
1210+
1211+ #[ op2]
1212+ #[ string]
1213+ pub fn op_node_x509_get_signature_algorithm_name (
1214+ #[ cppgc] cert : & Certificate ,
1215+ ) -> Option < String > {
1216+ let cert = cert. inner . get ( ) . deref ( ) ;
1217+ let oid = cert. signature_algorithm . algorithm . to_id_string ( ) ;
1218+ sig_alg_oid_to_name ( & oid) . map ( |s| s. to_string ( ) )
1219+ }
1220+
1221+ #[ op2]
1222+ #[ string]
1223+ pub fn op_node_x509_get_signature_algorithm_oid (
1224+ #[ cppgc] cert : & Certificate ,
1225+ ) -> String {
1226+ let cert = cert. inner . get ( ) . deref ( ) ;
1227+ cert. signature_algorithm . algorithm . to_id_string ( )
1228+ }
1229+
10901230#[ op2]
10911231#[ string]
10921232pub fn op_node_x509_get_info_access (
0 commit comments