Spring Boot应用复用Let's Encrypt证书及SSL报错排查
嘿,你的问题我碰到过不少次,先直接给结论:完全可以复用前端的Let's Encrypt证书给后端Spring Boot服务,只要满足一个前提——你的后端服务域名在这个证书的覆盖范围内就行。下面给你一步步梳理操作步骤、需要的文件,还有解决你遇到的异常的方法。
一、证书复用的前提
Let's Encrypt证书支持单域名、多域名(SAN证书)和通配符域名,只要你的后端服务域名(比如api.yourdomain.com)是证书里包含的域名之一,就可以直接用。如果证书只覆盖了前端域名(比如www.yourdomain.com),那你需要重新申请包含后端域名的证书,或者直接申请通配符证书*.yourdomain.com,这样所有子域名都能用。
二、需要从托管商获取的证书文件
你需要拿到这几个核心文件,把它们传到后端VPS上(注意权限,别让无关用户能读取私钥):
- 私钥文件:通常叫
privkey.pem或者yourdomain.key——这是服务器的专属密钥,绝对不能泄露! - 完整证书链文件:一般命名为
fullchain.pem——包含你的域名证书+Let's Encrypt的中间证书,浏览器需要这个链来确认证书的合法性。 - 如果托管商只给了单独的
cert.pem(你的域名证书)和chain.pem(Let's Encrypt中间证书),你可以把这两个文件合并成fullchain.pem:直接把chain.pem的内容追加到cert.pem的末尾就行。
三、Spring Boot配置HTTPS的具体操作
Spring Boot 2.2+之后支持直接用PEM格式的证书,不用转格式,非常方便。下面给两种配置方式:
方式1:直接使用PEM文件(推荐)
修改你的application.yml(如果用properties格式就对应调整),添加以下配置:
server: port: 443 # HTTPS默认端口,也可以用8443这类非标准端口 ssl: enabled: true key-store-type: PEM key-store: /path/to/your/privkey.pem # 可以用服务器上的绝对路径,或者放到resources目录下用classpath:前缀 key-store-password: "" # 如果你的私钥没有加密,这里留空就行;如果加密了填对应的密码 certificate: /path/to/your/fullchain.pem # 同样用绝对路径或classpath路径
配置完后,把证书文件放到指定路径,确保运行Spring Boot的用户有读取权限,然后重启服务。
方式2:转换成JKS/PKCS12格式(兼容旧版本Spring Boot)
如果你的Spring Boot版本低于2.2,需要把PEM转换成Java支持的密钥库格式:
# 先把PEM转成PKCS12格式,设置一个密钥库密码(比如changeit) openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out keystore.p12 -name tomcat -CAfile chain.pem -caname root # 可选:把PKCS12转成JKS格式(Spring Boot也直接支持PKCS12,这步可以跳过) keytool -importkeystore -srckeystore keystore.p12 -srcstoretype pkcs12 -destkeystore keystore.jks -deststoretype jks
然后在application.yml里配置:
server: port: 443 ssl: enabled: true key-store: /path/to/keystore.p12 # 或者keystore.jks key-store-password: changeit # 你刚才设置的密码 key-store-type: PKCS12 # 如果用JKS就写JKS key-alias: tomcat # 对应刚才openssl命令里的-name参数
四、解决你遇到的异常问题
你提到的两个错误:
java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
浏览器的net::ERR_SSL_PROTOCOL_ERROR
这几乎可以肯定是HTTP和HTTPS端口搞混了,或者证书配置错误导致SSL握手失败:
- 最常见的情况:后端配置了HTTPS在443端口,但前端还是用
http://api.yourdomain.com请求(把HTTP请求发到了HTTPS端口),或者前端用HTTPS请求,但后端其实没正确启用HTTPS(配置出错,还是在跑HTTP)。 - 另一种可能:证书和私钥不匹配,导致SSL握手失败,浏览器抛出协议错误,同时后端收到了不完整的请求数据,解析成HTTP时就会出现方法名无效的错误。
排查和修复步骤:
- 先在VPS本地测试:用
curl -v https://localhost:443访问后端,看是否能正常握手,有没有证书相关的错误提示。 - 检查前端的请求地址:确保是
https://开头,端口和后端配置的一致(如果用443可以省略端口)。 - 验证证书有效性:用
openssl x509 -in fullchain.pem -text -noout查看证书里的域名是否和后端域名一致;用openssl rsa -in privkey.pem -check验证私钥是否有效。 - 如果你用了Nginx之类的反向代理,更推荐让代理来处理HTTPS,后端跑HTTP:这样配置更简单,也避免后端直接暴露在公网。代理配置示例(Nginx):
server { listen 443 ssl; server_name api.yourdomain.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; location / { proxy_pass http://localhost:8080; # 后端Spring Boot的HTTP端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
这种方式下,后端不需要配置HTTPS,只需要处理HTTP请求,由Nginx负责SSL握手,这样也能避免你遇到的那种端口混淆问题。
内容的提问来源于stack exchange,提问作者Vasko Vasilev

