使用rumqttc+rustls时遭遇CertNotValidForName证书错误求助
解决rumqttc搭配rustls时的CertNotValidForName证书验证错误
错误原因
CertNotValidForName错误的核心原因是rustls验证服务器证书时,发现证书中声明的域名与你实际连接的test.test.com不匹配。rustls会优先检查证书的Subject Alternative Name(SAN)扩展字段,其次是Common Name(CN)字段,必须确保其中包含目标连接地址。
解决方案
1. 检查服务器证书的域名配置
用openssl命令查看服务器证书的域名信息:
# 查看SAN字段 openssl x509 -in server.crt -text -noout | grep -A 3 "Subject Alternative Name" # 查看CN字段 openssl x509 -in server.crt -text -noout | grep "Subject:"
- 如果输出中没有
test.test.com,说明证书本身的域名配置错误,需要重新生成包含该域名的证书(生成时可通过-subj "/CN=test.test.com"指定CN,或添加SAN扩展声明域名)。
2. 确认连接地址的正确性
检查代码中MqttOptions::new的第二个参数是否与证书中的域名完全一致:
let mut mqttoptions = MqttOptions::new("test", "test.test.com", 1000);
注意避免拼写错误、大小写差异(域名本身不区分大小写,但需与证书中声明的一致),或使用了错误的子域名/顶级域名。
3. 测试环境临时跳过域名验证(生产环境禁止)
如果是本地测试场景,可通过自定义rustls配置关闭域名验证,但生产环境绝对禁止使用,会引入中间人攻击风险:
#[cfg(feature = "use-rustls")] #[tokio::main] pub async fn test_mqtt() -> Result<(), Box<dyn Error>> { use rumqttc::{self, AsyncClient, MqttOptions, TlsConfiguration, Transport}; use rustls::{ClientConfig, RootCertStore, ServerName, Certificate, PrivateKey, client::ServerCertVerifier}; use rustls_pemfile::{certs, pkcs8_private_keys}; use std::io::Cursor; use std::sync::Arc; use std::time::Duration; let mut mqttoptions = MqttOptions::new("test", "test.test.com", 1000); mqttoptions.set_credentials("test", "test"); mqttoptions.set_keep_alive(Duration::from_secs(5)); // 加载CA证书到信任链 let ca = include_bytes!("../certs/ca.crt"); let mut root_store = RootCertStore::empty(); let ca_certs = certs(&mut Cursor::new(ca))?; for cert in ca_certs { root_store.add(&Certificate(cert))?; } // 加载客户端证书与密钥 let client_cert = include_bytes!("../certs/client.crt"); let client_key = include_bytes!("../certs/client.key"); let client_certs = certs(&mut Cursor::new(client_cert))? .into_iter() .map(Certificate) .collect(); let mut keys = pkcs8_private_keys(&mut Cursor::new(client_key))?; let client_key = PrivateKey(keys.remove(0)); // 构建自定义rustls配置,关闭域名验证 let mut config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_client_auth_cert(client_certs, client_key)?; // 替换验证器,跳过域名检查 config.dangerous().set_certificate_verifier(Arc::new(DummyVerifier)); let transport = Transport::Tls(TlsConfiguration::Rustls(config)); mqttoptions.set_transport(transport); let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10); client.subscribe("test/test/test", rumqttc::QoS::AtMostOnce).await.unwrap(); client.subscribe("test/test/test/logs", rumqttc::QoS::AtMostOnce).await.unwrap(); // 用tokio异步任务替代std线程,避免异步方法调用阻塞问题 tokio::spawn(async move { client.publish("test/test/test", rumqttc::QoS::AtLeastOnce, false, "test").await.unwrap(); client.publish("test/test/test/logs", rumqttc::QoS::AtLeastOnce, false, "test").await.unwrap(); tokio::time::sleep(Duration::from_millis(100)).await; }); loop { match eventloop.poll().await { Ok(v) => println!("Event = {:?}", v), Err(e) => { println!("Error = {:?}", e); break; } } } Ok(()) } // 自定义验证器,跳过所有证书域名检查 struct DummyVerifier; impl ServerCertVerifier for DummyVerifier { fn verify_server_cert( &self, _end_entity: &Certificate, _intermediates: &[Certificate], _server_name: &ServerName, _scts: &mut dyn Iterator<Item = &[u8]>, _ocsp_response: &[u8], _now: std::time::SystemTime, ) -> Result<rustls::client::ServerCertVerified, rustls::Error> { Ok(rustls::client::ServerCertVerified::assertion()) } }
4. 排查证书有效期与信任链问题
如果域名匹配仍报错,检查:
- 证书是否过期:
openssl x509 -in server.crt -text -noout | grep -A 2 "Validity" - 加载的CA证书是否是签发服务器证书的根CA或中间CA,确保信任链完整。
内容的提问来源于stack exchange,提问作者GranBoh
相关产品推荐
相关产品推荐

