如何使用rust-openssl获取X509证书SAN的othername值?
解决rust-openssl获取X509证书SAN字段中othername值的问题
问题场景
你需要解析X509证书SAN字段里的othername: UPN::myuser@somedomain.com内容,但rust-openssl的GeneralName类型未提供直接获取othername的方法,调用ipaddress()/email()/dnsname()均返回None。
解决方案
rust-openssl的GeneralName封装了OpenSSL底层结构体,我们可以通过底层指针或ASN.1解析来提取othername的UPN值,以下是两种可行实现:
方式一:调用OpenSSL底层API(推荐)
直接通过as_ptr()获取底层GENERAL_NAME指针,利用OpenSSL原生逻辑提取内容:
use std::ptr; use openssl::x509::{GeneralName, X509}; use openssl::asn1::Asn1OctetString; use openssl::nid::Nid; // 从GeneralName中提取UPN格式的othername值 fn get_upn_from_general_name(name: &GeneralName) -> Option<String> { let gn_ptr = name.as_ptr(); unsafe { // 检查是否为OTHERNAME类型 if (*gn_ptr).type_ != openssl::sys::GENERAL_NAME_GEN_OTHERNAME { return None; } let othername = (*gn_ptr).d.otherName; if othername.is_null() { return None; } // 验证类型ID是否为UPN的OID let nid = openssl::sys::OBJ_obj2nid((*othername).type_id); if nid != Nid::USER_PRINCIPAL_NAME.as_raw() { return None; } // 提取并转换为UTF-8字符串 let octet_str = Asn1OctetString::from_ptr((*othername).value); String::from_utf8(octet_str.as_slice().to_vec()).ok() } } fn main() { static SOME_PEM: &str = "spikes/x509_parser/cert.pem"; let data = std::fs::read(SOME_PEM).expect("Could not read file"); let cert = X509::from_pem(data.as_slice()).expect("Could not load cert"); let sans = cert.subject_alt_names().unwrap(); println!("SAN count: {}", sans.len()); for entry in &sans { if let Some(upn) = get_upn_from_general_name(entry) { println!("Found UPN: {}", upn); } } }
方式二:手动解析ASN.1结构
通过将GeneralName转为DER字节流,手动解析ASN.1格式提取内容(需依赖asn1-rs库):
# Cargo.toml中添加依赖 [dependencies] openssl = { version = "0.10", features = ["v110"] } asn1-rs = "0.5"
use openssl::x509::{GeneralName, X509}; fn get_upn_from_general_name(name: &GeneralName) -> Option<String> { // 将GeneralName转为ASN.1 DER字节 let der = name.to_der().ok()?; let mut cursor = std::io::Cursor::new(der); // 验证是否为otherName类型的ASN.1标签 let (tag, _) = asn1_rs::read_tag(&mut cursor).ok()?; if tag != asn1_rs::Tag::ContextSpecific(0) { return None; } // 解析OtherName的SEQUENCE结构 let (_, seq_content) = asn1_rs::read_sequence(&mut cursor).ok()?; let mut seq_cursor = std::io::Cursor::new(seq_content); // 验证OID是否为UPN的标识(1.3.6.1.4.1.311.20.2.3) let oid = asn1_rs::read_oid(&mut seq_cursor).ok()?; if oid != "1.3.6.1.4.1.311.20.2.3" { return None; } // 提取并转换为UTF-8字符串 let (_, upn_bytes) = asn1_rs::read_explicit(&mut seq_cursor, asn1_rs::Tag::ContextSpecific(0)).ok()?; String::from_utf8(upn_bytes.to_vec()).ok() } // main函数同方式一
关键说明
GeneralName的底层GENERAL_NAME结构体中,type_字段为GENERAL_NAME_GEN_OTHERNAME时对应othername类型。- UPN的标准OID为
1.3.6.1.4.1.311.20.2.3,需验证该OID确保提取的是目标值。 - 两种方式均可提取出
myuser@somedomain.com这类UPN内容。
内容的提问来源于stack exchange,提问作者sethcall
相关产品推荐
相关产品推荐

