Actix-Web搭建HTTPS服务器:ZeroSSL证书引发SSL错误排查
问题描述
我基于Actix-Web搭建了一个简单的HTTPS服务器,使用openssl生成的自签名TLS密钥在本地运行完全正常,但换成ZeroSSL提供的真实密钥后,浏览器无法访问服务:Chrome系浏览器提示ERR_SSL_VERSION_OR_CIPHER_MISMATCH,火狐浏览器提示SSL_ERROR_NO_CYPHER_OVERLAP。
代码实现
async fn main() -> std::io::Result<()> { let cert_key_file: PathBuf = PathBuf::from("zerossl/private.key"); let cert_file: PathBuf = PathBuf::from("zerossl/certificate.crt"); let cert_ca_file: PathBuf = PathBuf::from("zerossl/ca_bundle.crt"); // 为了简洁省略了path_to_certificate、path_to_ca、path_to_key的创建逻辑 let mut local_address = format!("0.0.0.0:{}", SERVICE_PORT); let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder .set_private_key_file(path_to_key, SslFiletype::PEM) .unwrap(); builder.set_certificate_chain_file(path_to_ca).unwrap(); builder.set_certificate_file(path_to_certificate, SslFiletype::PEM).unwrap(); HttpServer::new(|| { App::new() //.route("/{filename:.*}", web::get().to(index)) .route("/hello", web::get().to(|| async { "Hello World!" })) .service(greet) }) .bind_openssl(local_address, builder)? .run() .await }
补充调试信息(curl输出)
➜ release git:(master) curl -v https://zavijavasoft.com:8080 * Trying 37.140.197.152:8080... * TCP_NODELAY set * Connected to zavijavasoft.com (37.140.197.152) port 8080 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS alert, handshake failure (552): * error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure * Closing connection 0 curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
请问我哪里操作出错了?
解决方案
问题出在SSL证书链的加载顺序和方式上,调整以下两点即可解决:
修正证书加载顺序:必须先加载主证书(
certificate.crt),再加载CA证书链(ca_bundle.crt)。你当前的代码先调用了set_certificate_chain_file,后调用set_certificate_file,这会导致SSL握手时无法正确构建完整的信任链,进而引发加密套件不匹配的错误。正确的代码修改:
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); // 第一步:加载私钥 builder.set_private_key_file(path_to_key, SslFiletype::PEM).unwrap(); // 第二步:加载主证书 builder.set_certificate_file(path_to_certificate, SslFiletype::PEM).unwrap(); // 第三步:加载CA证书链 builder.set_certificate_chain_file(path_to_ca).unwrap();
另外,还有一种更稳妥的替代方案:将certificate.crt的内容和ca_bundle.crt的内容合并到同一个PEM文件中(主证书内容在前,CA bundle内容在后),然后只调用set_certificate_chain_file加载这个合并后的文件,这样可以彻底避免顺序问题。
修改完成后重启服务器,浏览器和curl就能正常建立SSL连接了。
内容的提问来源于stack exchange,提问作者Oleg Budin
相关产品推荐
相关产品推荐

