邮件客户端提示SSL连接失败,先别急着重签证书。465和587都能用于安全提交邮件,但连接开场不同:一个先握手TLS,一个先说SMTP再升级。拿HTTPS的直连方式去测587,可能还没进入证书校验就失败。
下面把入口、证书验证和SMTP响应分开检查。示例域名必须替换为授权目标;本文实测只连接临时回环服务,不登录邮箱、不发送邮件、不改生产。

图:配置为邮件提交服务时的入口对照,不是完整协议流程或成功回执;587必须成功升级TLS再继续敏感操作,链、名称、有效期及业务另行验收。
一、先确认测的是邮件提交入口
RFC8314讨论邮件用户代理与提交、访问服务之间的TLS,不是把邮件服务器间的25端口中继一并规定成相同策略。本文只讨论SMTP提交,不展开IMAP、POP、DANE或中继投递。
465的隐式TLS在TCP连接后立即协商TLS;587的STARTTLS先收到SMTP欢迎语,EHLO发现能力,再发STARTTLS。端口号是服务约定,不会替服务器自动配置TLS;仍须核对服务商给出的主机名、端口和模式。
二、465用直连TLS,保留严格验证
先准备经授权的CA材料root-ca.pem;私有CA应由可信管理渠道交付,公共CA应使用符合客户端策略的信任库,不能把本次收到的陌生证书直接当根信任。以下是Bash模板,需要OpenSSL和timeout。
set -eu
# root-ca.pem 是经授权的信任材料,不是随便下载的对端证书。
timeout 10s openssl s_client -connect mail.example.test:465 \
-servername mail.example.test -verify_hostname mail.example.test \
-verify_return_error -CAfile root-ca.pem -quiet -crlf <<'EOF'
QUIT
EOF
-servername发送SNI,-verify_hostname检查期望名称,两者不是一回事。-verify_return_error让验证失败终止握手;诊断工具默认继续输出,不能只凭“能看见证书”放行。-crlf把输入换行转换为SMTP使用的CRLF,QUIT只结束会话。
三、587必须先完成STARTTLS协商
对配置为STARTTLS的587入口,加上-starttls smtp。OpenSSL先执行SMTP升级,再做TLS。-name设置的是EHLO身份,不是证书验证名;把它改对不能修复SAN不匹配。
set -eu
timeout 10s openssl s_client -connect mail.example.test:587 \
-starttls smtp -name probe.example.test \
-servername mail.example.test -verify_hostname mail.example.test \
-verify_return_error -CAfile root-ca.pem -quiet -crlf <<'EOF'
QUIT
EOF
先查看完整退出码、stderr和响应。timeout超时只能说明本次探测未完成;wrong version number也不能单独证明证书损坏。若服务未提供STARTTLS,应核对入口、代理和服务配置,不能回退明文继续认证。
四、Python里也不能只改端口
SMTP_SSL用于连接开始即TLS;SMTP加starttls用于升级。显式传入create_default_context生成的上下文,保持链与名称验证。升级后重新EHLO,不沿用升级前的能力缓存。
import smtplib, ssl
ctx = ssl.create_default_context(cafile="root-ca.pem")
def check(port, implicit):
host = "mail.example.test"
if implicit:
client = smtplib.SMTP_SSL(host, port, context=ctx, timeout=10)
else:
client = smtplib.SMTP(host, port, timeout=10,
local_hostname="probe.example.test")
with client:
if not implicit:
client.ehlo()
client.starttls(context=ctx)
client.ehlo()
code, reply = client.noop()
if code != 250:
raise RuntimeError((code, reply))
print(port, "TLS verified; SMTP NOOP", code)
check(465, True)
check(587, False)
代码不调用login或sendmail;NOOP返回250只证明该会话的一条SMTP命令得到成功响应,不证明认证权限、收件策略、投递或收件箱到达。异常直接向外抛出,不以关闭验证或吞异常制造成功。
五、把现象放回正确层次
| 现象 | 优先核对 | 不能推出 |
|---|---|---|
| TCP可连,TLS失败 | 隐式TLS还是STARTTLS、代理、协议 | 证书已损坏 |
| 没有STARTTLS能力 | 实际服务与升级策略 | 可改用明文登录 |
| 证书链或名称错误 | 授权CA、完整链、客户端期望名称 | 发送SNI就验证了名称 |
| NOOP返回250 | 后续授权的认证与投递验收 | 邮件已发送成功 |
六、同一域名的两个端口分别验收
465与587可能由不同进程、负载均衡或节点处理。一个端口换证成功,不能替另一个签字。分别记录主机、端口、模式、证书指纹、有效期、验证时间和工具版本;固定节点探测时仍保留正确SNI和验证名称。
公网不可达、连接超时、缺CA文件要记为未完成或失败,而不是沿用上次正常结果。不要在报告里公开邮箱口令、私钥、会话票据或认证调试日志。
七、隔离实验能证明什么
本次从最终正文提取完整代码,在临时CA、正确及错误名称证书、随机回环端口下重放:仅把模板连接地址、端口和CA文件映射为夹具,域名验证仍保留。对照正确模式、错误CA、错误名称、缺失STARTTLS及NOOP非250,临时材料和监听随后清理。
这是最小SMTP测试服务,不是Postfix或Exchange生产配置验收,也没有公众CA申请、真实邮箱认证或投递。不能把本地通过说成所有邮箱都兼容,更不能把587最初明文协商说成允许明文发送敏感数据。
八、上线前检查清单与资料
- 核对授权入口、端口及TLS模式,465和587独立检查。
- 保持链、名称及有效期验证,STARTTLS失败不降级。
- 验证命令响应后,再按授权独立测试认证与投递。
- 保存真实失败输出,清理临时材料,不泄露认证日志。
335



被折叠的 条评论
为什么被折叠?



