You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生成根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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 12:15:31