问题

项目上部署多个系统,相互间请求接口时经常遇到如下报错:

response = requests.post(url, data, headers, verify=True, timeout=300)

HTTPSConnectionPool(host='xx.net', port=443): Max retries exceeded with url: /xx/api/rest (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')])")))

分析

1. HTTPS建立连接

https建立连接时,在http三次握手建立连接后,还有TLS握手过程,如下图所示(参考链接
在这里插入图片描述

2. HTTPS单向认证和双向认证

​​1. 认证方向与核心目标​​
​​1.1单向认证(One-way Authentication)​​
​​方向​​:仅客户端验证服务器身份,服务器不验证客户端身份。
​​目标​​:确保客户端访问的是合法的服务器,防止中间人攻击(如钓鱼网站)。
​​典型场景​​:普通网站(如电商、社交媒体)、API接口等。
​​1.2双向认证(Two-way/Mutual TLS Authentication)​​
​​方向​​:客户端和服务器互相验证身份,双方均需提供有效证书。
​​目标​​:在单向认证基础上,进一步确保客户端是授权的合法用户,防止未授权访问。
​​典型场景​​:企业内网、金融系统、物联网设备等高安全需求场景。

​​2. 流程差异​​
​​2.1单向认证流程​​
​​客户端发起请求​​:客户端向服务器发送HTTPS连接请求。
​​服务器返回证书​​:服务器发送其SSL证书(含公钥)给客户端。
​​客户端验证证书​​:客户端通过内置的CA证书库验证服务器证书的合法性(如域名匹配、有效期、CA签名等)。
​​密钥协商​​:客户端生成对称密钥,用服务器公钥加密后发送给服务器,完成加密通信。
2.2双向认证流程​​
在单向认证基础上增加以下步骤:
​​服务器要求客户端证书​​:服务器在验证自身证书后,向客户端发送CertificateRequest消息,要求客户端提供证书。
​​客户端发送证书​​:客户端提交自己的SSL证书(含公钥)。
​​服务器验证客户端证书​​:服务器验证客户端证书的合法性(如CA签名、有效期等)。
​​双向密钥协商​​:双方基于彼此的公钥生成共享密钥,完成加密通信。

3. Nginx配置文件

# 路径
/etc/nginx/xx.conf

### HTTPS服务
server {
        listen 443 ssl;
        server_name 内网域名/公网域名;

        ## HTTPS配置
        # 如果在公网环境部署,需使用CA授权的证书
        ssl_certificate             _.xx.net.crt;
        ssl_certificate_key         _.xx.net.key;
        # 如果在内网环境部署,需使用系统自行授权的证书(自定义根证书为root.crt)
        ssl_certificate             default.server.crt;
        ssl_certificate_key         default.server.nopassword.key;
        # 如果在内网环境部署,并开启HTTPS双向认证,需开启如下配置
        ssl_client_certificate      root.crt;
        ssl_verify_depth            1;
        ssl_verify_client           on;
        
        ssl_session_cache           shared:SSL:1m;
        ssl_session_timeout         5m;
        ssl_ciphers                 HIGH:!aNULL:!MD5;
        ssl_protocols               TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers   on;
		...
}

4. 请求代码

# 如果系统部署在内网,需要发送根证书供nginx校验
response = requests.post(url, data, headers, verify='root.crt file path', cert='xx', timeout=300)
# 如果系统部署在外网,无需发送根证书,由CA校验
response = requests.post(url, data, headers, timeout=300)

示意图
在这里插入图片描述

5. SSLError bad handshake原因

公网和内网两种部署情况下,python代码与nginx配置的ssl_certificate、ssl_certificate_key不对应。
其他原因,如网站证书过期等,此处不作分析。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐