生成根CA、服务器及客户端证书的正确方式(gRPC双向TLS问题)
问题描述
使用rcgen crate生成的证书,在基于Tonic的gRPC双向TLS(Mutual TLS)实现中无法正常工作,推测配置存在问题。以下是生成根CA、服务器和客户端证书的代码:
根CA生成代码
let mut params = CertificateParams::default(); params.alg = &PKCS_ECDSA_P384_SHA384; params.key_pair = Some(KeyPair::generate(&PKCS_ECDSA_P384_SHA384)?); params.key_identifier_method = KeyIdMethod::Sha384; let mut dn = DistinguishedName::new(); dn.push( DnType::CommonName, DnValue::PrintableString("Test Root CA ECC".to_string()), ); params.distinguished_name = dn; params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); params.not_before = OffsetDateTime::now_utc(); params.not_after = OffsetDateTime::now_utc() + Duration::days(365 * 10); params.key_usages = vec![ KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign ]; let cert = Certificate::from_params(params)?; let cert_pem = cert.serialize_pem(); let mut cert_file = fs::File::create("cert_root.pem")?; cert_file.write_all(cert_pem.unwrap().as_bytes())?; let private_key_pem = cert.serialize_private_key_pem(); let mut private_key_file = fs::File::create("cert_root.key")?; private_key_file.write_all(private_key_pem.as_bytes())?;
服务器证书生成代码
let mut params = CertificateParams::default(); params.alg = &PKCS_ECDSA_P384_SHA384; params.key_pair = Some(KeyPair::generate(&PKCS_ECDSA_P384_SHA384).unwrap()); params.key_identifier_method = KeyIdMethod::Sha384; let mut dn = DistinguishedName::new(); dn.push( DnType::CommonName, DnValue::PrintableString("r.u.local".to_string()), ); params.distinguished_name = dn; params.subject_alt_names = vec![ SanType::DnsName("localhost".to_string()), SanType::IpAddress( std::net::IpAddr::V4( std::net::Ipv4Addr::new(127, 0, 0, 1) ) ), SanType::IpAddress( std::net::IpAddr::V6( std::net::Ipv6Addr::new( 0, 0, 0, 0, 0, 0, 0, 1, ) ) ), ]; params.not_before = OffsetDateTime::now_utc(); params.not_after = OffsetDateTime::now_utc() + Duration::days(365 * 1); let cert = Certificate::from_params(params)?; let cert_signed = cert.serialize_pem_with_signer(&root_cert)?;
客户端证书生成代码
let mut params = CertificateParams::default(); params.alg = &PKCS_ECDSA_P384_SHA384; params.key_pair = Some(KeyPair::generate(&PKCS_ECDSA_P384_SHA384).unwrap()); params.key_identifier_method = KeyIdMethod::Sha384; let mut dn = DistinguishedName::new(); params.distinguished_name = dn; params.not_before = OffsetDateTime::now_utc(); params.not_after = OffsetDateTime::now_utc() + Duration::days(365 * 1); let cert = Certificate::from_params(params)?; let cert_signed = cert.serialize_pem_with_signer(&root_cert)?;
请问是否需要设置ExtendedKeyUsagePurpose的ClientAuth和ServerAuth?还存在其他遗漏的配置吗?
解决方案
1. 必须设置ExtendedKeyUsage
是的,双向TLS场景下必须为证书指定对应的扩展密钥用途:
- 服务器证书:需添加
ExtendedKeyUsagePurpose::ServerAuth,表明该证书用于服务器身份验证 - 客户端证书:需添加
ExtendedKeyUsagePurpose::ClientAuth,表明该证书用于客户端身份验证
修改后的服务器证书参数示例:
params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth]; // 补充基础密钥用途,满足TLS握手要求 params.key_usages = vec![ KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::KeyEncipherment, ];
修改后的客户端证书参数示例:
params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ClientAuth]; // 补充基础密钥用途 params.key_usages = vec![KeyUsagePurpose::DigitalSignature];
2. 其他遗漏配置检查
- 客户端证书缺少标识信息:当前客户端证书的DistinguishedName为空,建议至少添加CommonName字段,避免部分TLS栈因证书主体为空拒绝验证
- 服务器证书密钥用途缺失:原代码未设置
key_usages,需补充DigitalSignature和KeyEncipherment(适配ECDSA算法),确保证书具备TLS握手所需的密钥操作权限 - 证书链完整性:确保Tonic服务端和客户端都正确加载完整的信任链(根CA证书+对应端的签名证书),避免因信任链不完整导致验证失败
- 私钥导出遗漏:生成服务器和客户端证书时,别忘了导出对应的私钥文件(类似根CA的
serialize_private_key_pem操作),Tonic需要私钥完成TLS握手
内容的提问来源于stack exchange,提问作者Nkk
相关产品推荐
相关产品推荐

