微服务集群HTTPS单向/双向认证

8 阅读37分钟

一、方案背景与目标

1.1 现状

现有订单中心、支付中心、商品中心3个微服务应用,同属一个系统生态,目前各应用间通过HTTP协议相互调用,每个应用前置均部署独立Nginx代理,用于请求转发与基础防护。

1.2 痛点

HTTP协议为明文传输,存在数据窃听、篡改、伪造风险,尤其订单、支付等核心业务数据,传输过程中无加密保护,不符合内部安全规范。

1.3 目标

将微服务间调用统一切换为HTTPS加密协议,基于OpenSSL自签证书实现,补充单向认证、双向认证两种模式的完整配置;明确3种证书生成方案的细节与适用场景;提供Apache HttpClient客户端(忽略证书、信任证书)的完整改造方案;完成Nginx证书配置,确保改造无业务侵入、部署便捷。

二、证书生成方案

结合需求,明确3种证书生成方式的细节、优缺点及适用场景,重点提供企业级推荐方案的分步生成命令。

2.1 三种证书生成方案对比

生成方案核心说明优点缺点适用场景
方案1:所有应用共用1套证书生成1套server.crt(服务端证书)、server.key(服务端私钥),订单、支付、商品中心共用该套证书配置最简单、部署最快、无需管理多套证书,学习成本低安全性差,1套证书泄露则所有微服务均受影响,无隔离性内部测试环境、小规模微服务、对安全要求极低的场景
方案2:每个应用独立证书(无CA)订单、支付、商品中心分别生成独立的证书和私钥,互不关联,均为自签证书隔离性好,单个应用证书泄露不影响其他应用,配置灵活证书管理繁琐,客户端需分别信任每个应用的证书,运维成本高;例如订单中心需同时调用商品、支付中心时,需分别加载商品、支付中心的独立证书,配置和维护极为繁琐,易出现证书遗漏、配置错误问题安全要求较高、服务数量较少(3-5个)的生产环境
方案3:CA根证书+签发各应用证书(推荐)先生成全局唯一的自签CA根证书,再用CA根证书分别签发订单、支付、商品中心的服务端证书,所有证书共用一个信任源最安全、符合企业级规范;统一信任源,客户端只需信任CA根证书即可;便于证书吊销、统一管理生成步骤稍多,需先创建CA根证书,再签发各应用证书生产环境、核心微服务(订单、支付)、对安全要求高的场景(推荐首选)

2.2 分步独立生成命令

全程分步执行,每一步均有明确说明,可单独复制执行,便于理解和排查问题;所有证书有效期默认10年(3650天),可根据需求调整。

第一步:环境准备(创建证书存放目录)

创建独立目录存放CA根证书和各应用证书,避免混淆,执行命令:

# 创建根证书目录(存放CA相关文件)
mkdir -p /usr/local/ssl/ssl_ca
# 创建各应用证书目录
mkdir -p /usr/local/ssl/ssl_order  # 订单中心
mkdir -p /usr/local/ssl/ssl_pay    # 支付中心
mkdir -p /usr/local/ssl/ssl_goods  # 商品中心
# 进入CA根证书目录,开始生成CA
cd /usr/local/ssl/ssl_ca

第二步:生成CA根证书(全局唯一,信任锚点)

CA根证书用于签发所有微服务的服务端证书,客户端只需信任该CA,即可信任所有由其签发的证书,执行命令:

# 1. 生成CA根证书私钥(ca.key),2048位加密,无密码(便于管理,内网安全)
openssl genrsa -out ca.key 2048# 2. 生成CA根证书公钥(ca.crt),自签名,有效期10年
# 执行后会提示输入国家、组织、域名等信息,可全部回车默认(内网使用无需严格填写)
openssl req -new -x509 -key ca.key -out ca.crt -days 3650

生成文件:ca.key(CA私钥,需妥善保管,不可泄露)、ca.crt(CA公钥,用于客户端信任和服务端签发)。

生成 CA 根证书时,无需填写任何微服务的独立域名(如order.micro-service.comgoods.micro-service.com),仅需填写 CA 自身的基础信息(可简单填写或默认);微服务的独立域名,只需要填在「生成各服务证书请求文件(.csr)的 Common Name 字段」中,对应你方案中订单、商品、支付中心的独立域名。

关键注意事项:

  1. 每个微服务的 Common Name,必须和它 Nginx 配置中的 server_name 完全一致(比如订单中心 Nginx 配置 server_name order.micro-service.com,csr 中就必须填这个域名),否则客户端会报 “证书域名不匹配” 错误。
  2. CA 根证书的 Common Name,无需和任何微服务域名一致,只要是自定义的 CA 名称即可(比如 MicroService-CA)。
  3. 除了 Common Name,其他字段(国家、组织等)可与 CA 根证书保持一致,也可留空,不影响证书的签发和校验(内网场景无需严格填写)。

第三步:用CA签发订单中心证书

进入订单中心证书目录,生成私钥、证书请求文件,再用CA根证书签发,执行命令:

# 进入订单中心证书目录
cd /usr/local/ssl/ssl_order
​
# 1. 生成订单中心服务端私钥(order.key)
openssl genrsa -out order.key 2048
​
# 2. 生成证书请求文件(order.csr),用于向CA申请签发证书
# 提示信息可回车默认,与CA根证书信息一致即可
openssl req -new -key order.key -out order.csr
​
# 3. 用CA根证书签发订单中心服务端证书(order.crt),有效期10年
openssl x509 -req -in order.csr -CA /usr/local/ssl/ssl_ca/ca.crt -CAkey /usr/local/ssl/ssl_ca/ca.key -CAcreateserial -out order.crt -days 3650

生成文件:order.key(订单服务私钥)、order.csr(证书请求文件,可删除)、order.crt(订单服务证书)。

第四步:用CA签发支付中心证书

与订单中心步骤一致,替换对应文件名,执行命令:

# 进入支付中心证书目录
cd /usr/local/ssl/ssl_pay
​
# 1. 生成支付中心服务端私钥(pay.key)
openssl genrsa -out pay.key 2048
​
# 2. 生成证书请求文件(pay.csr)
openssl req -new -key pay.key -out pay.csr
​
# 3. 用CA根证书签发支付中心服务端证书(pay.crt)
openssl x509 -req -in pay.csr -CA /usr/local/ssl/ssl_ca/ca.crt -CAkey /usr/local/ssl/ssl_ca/ca.key -CAcreateserial -out pay.crt -days 3650

第五步:用CA签发商品中心证书

同理,执行命令:

# 进入商品中心证书目录
cd /usr/local/ssl/ssl_goods
​
# 1. 生成商品中心服务端私钥(goods.key)
openssl genrsa -out goods.key 2048
​
# 2. 生成证书请求文件(goods.csr)
openssl req -new -key goods.key -out goods.csr
​
# 3. 用CA根证书签发商品中心服务端证书(goods.crt)
openssl x509 -req -in goods.csr -CA /usr/local/ssl/ssl_ca/ca.crt -CAkey /usr/local/ssl/ssl_ca/ca.key -CAcreateserial -out goods.crt -days 3650

补充:方案1、方案2的简化生成命令

若使用方案1(共用1套证书),只需生成1套证书,复制到所有应用目录即可:

# 生成共用证书(以server为前缀)
mkdir -p /usr/local/ssl/ssl_common
cd /usr/local/ssl/ssl_common
openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt -days 3650

若使用方案2(各应用独立证书,无CA),只需省略“CA签发”步骤,直接生成自签证书:

# 以订单中心为例,其他应用同理
cd /usr/local/ssl/ssl_order
openssl genrsa -out order.key 2048
openssl req -new -x509 -key order.key -out order.crt -days 3650

三、HTTPS单向认证与双向认证配置

基于方案3(CA+各应用证书),补充单向认证、双向认证的完整Nginx配置,适配不同安全需求;单向认证仅客户端验证服务端,双向认证双方互验,核心微服务推荐双向认证。

3.1 单向认证配置

核心逻辑:仅客户端验证服务端证书的合法性,服务端不验证客户端身份,配置简单、性能损耗低,适用于非核心微服务(如商品中心查询接口)。

以订单中心Nginx为例,支付、商品中心同理,替换对应证书路径和服务地址:

server {
    listen 443 ssl;  # 监听HTTPS默认端口443
    server_name order.micro-service.com;  # 订单中心内网域名(需与证书一致)
​
    # 服务端证书配置(方案3生成的订单中心证书)
    ssl_certificate      /usr/local/ssl/ssl_order/order.crt;  # 服务端证书
    ssl_certificate_key  /usr/local/ssl/ssl_order/order.key;  # 服务端私钥
​
    # SSL安全优化(固定配置,提升安全性)
    ssl_protocols        TLSv1.2 TLSv1.3;  # 禁用低版本TLS,提升安全
    ssl_ciphers          ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5;  # 加密套件
    ssl_prefer_server_ciphers  on;  # 优先使用服务端加密套件
​
    # 代理转发到后端订单中心微服务(HTTP不变,Nginx负责HTTPS解密)
    location / {
        proxy_pass http://127.0.0.1:8080;  # 订单中心微服务本地端口
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;  # 传递客户端真实IP
    }
}
​
# 强制HTTP跳转HTTPS(可选,避免HTTP访问)
server {
    listen 80;
    server_name order.micro-service.com;
    return 301 https://$host$request_uri;  # 所有HTTP请求跳转至HTTPS
}

3.2 双向认证配置

核心逻辑:客户端验证服务端证书 + 服务端验证客户端证书,安全性最高,可杜绝非法客户端调用,适用于订单中心、支付中心等核心微服务。

在单向认证基础上,增加“客户端证书校验”配置,以支付中心Nginx为例:

server {
    listen 443 ssl;
    server_name pay.micro-service.com;  # 支付中心内网域名
​
    # 服务端证书配置(方案3生成的支付中心证书)
    ssl_certificate      /usr/local/ssl/ssl_pay/pay.crt;
    ssl_certificate_key  /usr/local/ssl/ssl_pay/pay.key;
​
    # 双向认证核心配置(新增)
    ssl_client_certificate  /usr/local/ssl/ssl_ca/ca.crt;  # 信任的CA根证书(用于验证客户端证书)
    ssl_verify_client       on;  # 开启客户端证书校验(强制校验)
    ssl_verify_depth        2;  # 校验深度,默认2即可
​
    # SSL安全优化(与单向认证一致)
    ssl_protocols        TLSv1.2 TLSv1.3;
    ssl_ciphers          ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;
​
    # 代理转发到后端支付中心微服务
    location / {
        proxy_pass http://127.0.0.1:8081;  # 支付中心微服务本地端口
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
​
# 强制HTTP跳转HTTPS
server {
    listen 80;
    server_name pay.micro-service.com;
    return 301 https://$host$request_uri;
}

补充说明:双向认证需客户端携带CA签发的客户端证书(后续客户端改造会补充),服务端通过CA根证书验证客户端证书的合法性,非法客户端无法建立连接。

四、客户端改造(Apache HttpClient,忽略/信任证书)

微服务作为调用方(客户端),需改造HTTP调用逻辑,切换为HTTPS,并配置证书校验方式(忽略证书/信任证书);服务端无需改造代码,由Nginx负责HTTPS加密/解密。

以下均基于Apache HttpClient(用户指定)实现,提供完整可直接使用的工具类,适配单向认证、双向认证场景。

4.1 依赖准备(Maven)

确保项目引入Apache HttpClient依赖,版本推荐4.5+:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.14</version>
</dependency>

4.2 方案1:忽略证书(不安全,仅测试用)

核心逻辑:关闭SSL证书校验,跳过服务端证书合法性验证,适用于测试环境(避免频繁处理证书信任问题),生产环境严禁使用。

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
​
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
​
/**
 * Apache HttpClient 忽略证书工具类(测试用)
 */
public class HttpsIgnoreCertUtil {
​
    /**
     * 获取忽略证书的HttpClient实例
     */
    public static CloseableHttpClient getIgnoreSslHttpClient() throws Exception {
        // 1. 创建信任管理器:不校验任何证书
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    // 不校验客户端证书
                    @Override
                    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {}
                    // 不校验服务端证书
                    @Override
                    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
                    // 无需返回可信任证书
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                }
        };
​
        // 2. 初始化SSL上下文,关闭证书校验
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
​
        // 3. 创建SSL连接工厂,忽略主机名校验(避免域名与证书不一致报错)
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                sslContext,
                NoopHostnameVerifier.INSTANCE  // 忽略主机名校验
        );
​
        // 4. 构建HttpClient实例,设置SSL连接工厂
        return HttpClients.custom()
                .setSSLSocketFactory(sslSocketFactory)
                .build();
    }
​
    /**
     * 测试调用示例(GET请求)
     */
    public static void main(String[] args) throws Exception {
        CloseableHttpClient httpClient = getIgnoreSslHttpClient();
        // 调用地址改为HTTPS(订单中心示例)
        String url = "https://order.micro-service.com/api/order/list";
        // 后续执行GET/POST请求,与HTTP调用逻辑一致
        // ...(省略请求执行代码,与普通HttpClient调用相同)
    }
}

忽略证书具体忽略的信息是什么?

忽略证书本质是关闭客户端对服务端证书的所有合法性校验,具体忽略以下关键信息,无论这些信息是否异常,均会允许建立HTTPS连接:

  1. 证书有效期:忽略证书是否过期(已过有效期)、是否未生效(未到起始有效期),即使证书过期仍可正常连接。
  2. 证书域名匹配:忽略证书中绑定的域名(或IP)与实际访问的服务域名(或IP)是否一致,比如证书绑定order.micro-service.com,访问pay.micro-service.com也可正常连接。
  3. 证书签发机构合法性:忽略证书是否由可信CA机构签发(自签证书、非法机构签发的证书均会被信任),无需验证签发机构的有效性。
  4. 证书完整性与合法性:忽略证书是否被篡改、是否存在伪造、是否符合SSL证书格式规范,即使证书被篡改也会正常建立连接。
  5. 证书吊销状态:忽略证书是否已被签发机构吊销(即使证书已吊销,仍可正常使用)。

4.3 方案2:信任自签CA证书

核心逻辑:客户端仅信任我们自己生成的CA根证书(方案3中的ca.crt),既保证安全,又避免频繁校验,适配单向认证、双向认证场景。

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
​
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
​
/**
 * Apache HttpClient 信任CA证书工具类(生产用)
 */
public class HttpsTrustCaUtil {
​
    // CA根证书路径(根据实际部署路径修改)
    private static final String CA_CERT_PATH = "/usr/local/ssl/ssl_ca/ca.crt";
​
    /**
     * 获取信任CA证书的HttpClient实例(单向认证可用)
     */
    public static CloseableHttpClient getTrustCaHttpClient() throws Exception {
        // 1. 加载CA根证书
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        FileInputStream caInputStream = new FileInputStream(CA_CERT_PATH);
        X509Certificate caCert = (X509Certificate) certificateFactory.generateCertificate(caInputStream);
        caInputStream.close();
​
        // 2. 创建KeyStore,将CA根证书存入(作为信任锚点)
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);  // 初始化空的KeyStore
        keyStore.setCertificateEntry("micro-service-ca", caCert);  // 存入CA证书
​
        // 3. 初始化信任管理器工厂,指定信任的KeyStore
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm()
        );
        trustManagerFactory.init(keyStore);
​
        // 4. 初始化SSL上下文,仅信任指定CA
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new java.security.SecureRandom());
​
        // 5. 创建SSL连接工厂,验证主机名(生产环境建议开启)
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
​
        // 6. 构建HttpClient实例
        return HttpClients.custom()
                .setSSLSocketFactory(sslSocketFactory)
                .build();
    }
​
    /**
     * 获取信任CA+携带客户端证书的HttpClient实例(双向认证可用)
     * 双向认证需额外加载客户端证书(由CA签发)
     */
    public static CloseableHttpClient getTrustCaWithClientCertHttpClient() throws Exception {
        // 1. 加载CA根证书(与单向认证一致)
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        FileInputStream caInputStream = new FileInputStream(CA_CERT_PATH);
        X509Certificate caCert = (X509Certificate) certificateFactory.generateCertificate(caInputStream);
        caInputStream.close();
​
        // 2. 加载客户端证书(客户端私钥+客户端证书,由CA签发)
        String clientKeyPath = "/usr/local/ssl/client/client.key";  // 客户端私钥
        String clientCertPath = "/usr/local/ssl/client/client.crt";// 客户端证书
        KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
        // 加载客户端证书和私钥(若私钥有密码,需传入密码,此处无密码)
        clientKeyStore.load(new FileInputStream(clientCertPath), null);
        clientKeyStore.setKeyEntry("client-key", new FileInputStream(clientKeyPath), null, new X509Certificate[]{caCert});
​
        // 3. 初始化信任管理器(信任CA)和密钥管理器(加载客户端证书)
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm()
        );
        trustManagerFactory.init(KeyStore.getInstance(KeyStore.getDefaultType()));
​
        // 4. 初始化SSL上下文,同时设置信任管理器和密钥管理器
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(
                null,  // 密钥管理器(若客户端私钥有密码,需传入)
                trustManagerFactory.getTrustManagers(),
                new java.security.SecureRandom()
        );
​
        // 5. 创建SSL连接工厂
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
​
        // 6. 构建HttpClient实例
        return HttpClients.custom()
                .setSSLSocketFactory(sslSocketFactory)
                .build();
    }
​
    /**
     * 生产环境调用示例(POST请求)
     */
    public static void main(String[] args) throws Exception {
        // 单向认证调用(如调用商品中心)
        CloseableHttpClient httpClient = getTrustCaHttpClient();
        // 双向认证调用(如调用支付中心)
        // CloseableHttpClient httpClient = getTrustCaWithClientCertHttpClient();
​
        // 调用地址改为HTTPS(支付中心示例)
        String url = "https://pay.micro-service.com/api/pay/create";
        // 后续执行POST请求,传递参数,与HTTP调用逻辑一致
        // ...(省略请求执行代码)
    }
}

4.4 客户端改造核心步骤

  1. 将微服务中所有调用地址,从 http:// 改为 https://(如订单中心调用支付中心,从pay.micro-service.com改为pay.micro-service.com)。
  2. 注入上述工具类中的HttpClient实例,替换原有HTTP调用的客户端(如RestTemplate底层使用该HttpClient)。
  3. 单向认证:使用 getTrustCaHttpClient() 方法,仅信任CA根证书即可。
  4. 双向认证:使用 getTrustCaWithClientCertHttpClient() 方法,需额外准备客户端证书(由CA签发,生成命令可补充)。
  5. 无需修改业务逻辑,仅改造HTTP调用的客户端配置,实现无侵入改造。

4.5 PC客户端(浏览器)改造说明

PC端客户端核心以浏览器(Chrome、Edge、Firefox等)为主,其证书加载逻辑与Java微服务客户端不同,核心取决于认证模式(单向/双向),且支持“无需加载证书”的场景,具体如下:

4.5.1 单向认证场景(PC浏览器)

单向认证仅要求浏览器验证服务端证书合法性,可无需手动加载证书,因使用自签CA证书,浏览器会提示“证书不受信任”,两种便捷处理方式:

  1. 临时访问(测试环境首选):访问HTTPS地址时,弹出“连接非私密”提示,点击“高级”→“继续访问”,即可正常访问,无需加载任何证书。
  2. 永久信任(生产环境首选):将CA根证书(ca.crt)导入浏览器“受信任的根证书颁发机构”,导入后浏览器默认信任所有该CA签发的服务端证书,无警告提示(主流浏览器操作逻辑一致,核心是导入CA根证书)。

关键说明:单向认证场景下,PC浏览器无需强制加载证书,临时信任可满足测试需求,生产环境导入CA根证书更规范。

4.5.2 双向认证场景(PC浏览器)

双向认证要求浏览器(客户端)同时验证服务端证书、服务端验证浏览器(客户端)证书,因此必须加载客户端证书,步骤如下:

  1. 先将CA根证书(ca.crt)导入浏览器(步骤同单向认证永久信任),确保浏览器信任服务端证书;
  2. 将客户端证书(client.crt)导入浏览器(以Chrome为例):进入“管理证书”→“个人”选项卡→“导入”,选择client.crt文件,完成导入;
  3. 访问双向认证的HTTPS地址时,浏览器会自动弹出“选择证书”提示,选择导入的客户端证书,即可完成身份校验,正常访问。

补充:若客户端证书为PKCS12格式(.p12/.pfx),导入方式一致,导入时需输入证书密码(若设置);未加载客户端证书时,浏览器会提示“400 Bad Request”或“SSL握手失败”。

4.6 APP客户端改造说明

APP客户端(Android、iOS)改造核心是处理自签CA证书的信任问题,适配单向/双向认证,无需用户手动加载证书(由开发人员在APP内集成),具体如下:

4.6.1 单向认证场景(APP)

核心逻辑:APP内集成CA根证书,信任服务端证书,无需用户操作,开发端改造步骤:

  • Android端:将ca.crt证书放入APP项目的assets目录,在网络请求框架(如OkHttp)中配置SSL上下文,加载CA根证书,信任该证书对应的服务端,示例代码(OkHttp):

    // 加载CA根证书
    InputStream caInput = getAssets().open("ca.crt");
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    X509Certificate caCert = (X509Certificate) cf.generateCertificate(caInput);
    caInput.close();
    // 配置信任管理器
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    keyStore.setCertificateEntry("ca", caCert);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(keyStore);
    ​
    // 配置OkHttp信任该CA
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
    OkHttpClient client = new OkHttpClient.Builder()
            .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0])
            .build();
    ​
    ```
    

    iOS端:将ca.crt证书导入项目,在Info.plist中配置证书信任,使用NSURLSession请求时,通过SSL证书校验逻辑,信任该CA签发的服务端证书,避免系统提示“证书不受信任”。

4.6.2 双向认证场景(APP)

核心逻辑:APP内同时集成CA根证书(信任服务端)和客户端证书(用于服务端校验),无需用户手动加载,开发端改造步骤:

  • Android端:在单向认证改造基础上,额外加载客户端证书(client.crt)和客户端私钥(client.key),配置SSL上下文时同时设置信任管理器和密钥管理器,实现双向认证。
  • iOS端:将客户端证书(.p12格式,便于iOS集成)导入项目,在网络请求时,指定客户端证书用于身份校验,与服务端完成双向SSL握手。

关键说明:APP客户端证书均由开发人员集成到安装包中,用户无需手动操作;双向认证场景下,客户端证书需妥善保管,避免打包泄露。

4.7 多客户端改造总结

客户端类型单向认证(是否需加载证书)双向认证(是否需加载证书)核心注意点
Java微服务需加载CA根证书(或忽略证书)需加载CA根证书+客户端证书使用Apache HttpClient配置SSL上下文
PC浏览器可不用加载(临时信任),推荐加载CA根证书必须加载CA根证书+客户端证书导入证书到浏览器对应证书库
APP客户端(Android/iOS)无需用户加载,开发端集成CA根证书无需用户加载,开发端集成CA根证书+客户端证书证书集成到安装包,避免泄露

4.8 关键疑问解答

4.8.1 忽略证书、信任证书(加载证书)与单向认证、双向认证的关系

忽略证书、信任证书(加载证书)是客户端的证书校验方式,单向认证、双向认证是HTTPS的认证模式,二者相互配合,无直接绑定关系,但有固定适配场景,核心关系如下:

  1. 忽略证书(客户端):可适配单向认证、双向认证两种模式,但仅用于测试环境,生产环境严禁使用。
  2. 适配单向认证:客户端忽略服务端证书校验,服务端不校验客户端,可正常建立连接(测试用)。
  3. 适配双向认证:客户端忽略服务端证书校验,同时携带客户端证书供服务端校验,服务端校验客户端证书通过后,可建立连接(仅测试用,失去双向认证的安全意义)。
  4. 信任证书(加载证书,客户端):是生产环境的标准配置,可适配单向认证、双向认证两种模式,核心是客户端仅信任指定证书(CA根证书/客户端证书)。
  5. 适配单向认证:客户端加载并信任服务端证书(或CA根证书),验证服务端合法性;服务端不校验客户端,这是生产环境单向认证的标准配置。
  6. 适配双向认证:客户端同时加载并信任CA根证书(验证服务端)、加载客户端证书(供服务端校验);服务端加载CA根证书(验证客户端),这是生产环境双向认证的标准配置。

核心区别:单/双向认证决定“双方是否互验身份”,忽略/信任证书决定“客户端如何验证服务端身份”,二者独立但需配合使用(生产环境需“信任证书+对应认证模式”)。

4.8.2 是不是只要有证书了,最低都是单向认证?

是的,只要服务端配置了HTTPS证书(无论哪种生成方案),最低都是单向认证,核心原因如下:

  1. HTTPS的基础逻辑:HTTPS本质是“HTTP+SSL/TLS加密”,而SSL/TLS握手的核心第一步就是“客户端验证服务端证书”,这是HTTPS的默认行为,也是单向认证的核心逻辑。
  2. 单向认证的本质:仅客户端验证服务端证书(确认服务端身份合法),服务端不验证客户端,这是HTTPS的最低安全门槛——只要启用HTTPS(配置证书),就会触发客户端对服务端的证书校验,即默认开启单向认证。
  3. 特殊说明:客户端“忽略证书”,并不是关闭单向认证,而是跳过了“客户端对服务端证书的校验”,但服务端仍处于“单向认证模式”(不校验客户端);双向认证是在单向认证的基础上,增加了“服务端对客户端的证书校验”,是更高安全级别的认证模式。

总结:HTTPS(配置证书)= 单向认证(默认),双向认证是单向认证的升级版本,需额外配置客户端证书校验。

五、部署与验证步骤

5.1 部署步骤

  1. 证书部署:将CA根证书、各应用证书(key和crt文件)拷贝到对应Nginx的证书目录(如/usr/local/ssl/对应目录),确保权限为600(仅管理员可读写)。
  2. Nginx配置:替换各应用的Nginx配置文件(按单向/双向认证配置),检查证书路径是否正确。
  3. 重启Nginx:执行 nginx -t检查配置语法,无错误后执行 nginx -s reload 重启。
  4. 客户端改造:微服务集成上述HttpClient工具类,修改调用地址为HTTPS,部署微服务。

5.2 验证命令

# 1. 单向认证验证(调用订单中心,仅信任CA)
curl -v --cacert /usr/local/ssl/ssl_ca/ca.crt https://order.micro-service.com/api/order/list
​
# 2. 双向认证验证(调用支付中心,需携带客户端证书)
curl -v --cacert /usr/local/ssl/ssl_ca/ca.crt --cert /usr/local/ssl/client/client.crt --key /usr/local/ssl/client/client.key https://pay.micro-service.com/api/pay/create
​
# 3. 忽略证书验证(测试用,快速排查服务是否可用)
curl -v -k https://order.micro-service.com/api/order/list

验证成功:返回200状态码,获取正常响应;验证失败:检查证书路径、Nginx配置、客户端证书是否正确。

六、注意事项与方案总结

6.1 注意事项

  • 证书安全:CA私钥(ca.key)需妥善保管,不可泄露;服务端/客户端私钥建议设置密码(生成命令可添加 -aes256 -passout pass:密码),增强安全性。
  • 环境适配:测试环境可使用“忽略证书”或“共用证书”,生产环境必须使用“CA+各应用证书”+双向认证(核心服务)。
  • 版本兼容:Apache HttpClient版本建议4.5+,避免低版本不支持TLS1.2/1.3,导致连接失败。
  • 主机名校验:生产环境不建议关闭主机名校验(NoopHostnameVerifier),确保证书域名与服务域名一致。
  • 证书更新:证书有效期为10年,到期前需重新用CA签发,避免证书过期导致服务不可用。

6.2 方案总结

  1. 证书方案:优先选择“CA根证书+各应用证书”(方案3),兼顾安全性和可管理性;测试环境可简化为“共用证书”(方案1)。
  2. 认证模式:核心微服务(订单、支付)用双向认证,非核心服务(商品)用单向认证,平衡安全与性能。
  3. 改造范围:服务端仅配置Nginx,无需修改微服务代码;客户端仅改造HTTP调用客户端,无业务侵入。
  4. 客户端选择:测试用“忽略证书”,生产用“信任证书”,双向认证需额外加载客户端证书。
  5. 优势:基于OpenSSL自签证书,零成本、内网可控,适配现有微服务架构,安全性满足内部业务需求。

七、补充技术知识点(证书相关)

本章节补充HTTPS证书相关核心技术知识点,明确各类证书文件、关键参数的作用,以及CA根证书签发证书的本质,帮助理解证书生成与校验的底层逻辑。

7.1 Nginx配置crt证书后的客户端交互逻辑

当Nginx配置了ssl_certificate(即.crt文件)后,在SSL/TLS握手阶段,Nginx会自动将该证书发送给客户端。客户端收到证书后,有两种处理策略,其专业术语如下:

  • 交互流程

    1. 证书传递:客户端发起HTTPS连接 -> Nginx响应并发送其配置的.crt证书(包含公钥)。
    2. 客户端校验:客户端根据自身配置,决定如何处理该证书。
  • 两种校验方式的专业术语

    1. 跳过校验(Trust Any/Insecure Skip Verify)

      • 术语证书信任跳过(Certificate Trust Bypass)不安全跳过(Insecure Skip Verify)
      • 逻辑:客户端不验证Nginx发送的证书是否合法、是否过期、域名是否匹配,直接建立加密连接。对应代码中的TrustManager不做任何检查。
      • 场景:仅限测试环境(如前文的HttpsIgnoreCertUtil)。
    2. 合法性校验(Certificate Chain Validation)

      • 术语证书链校验(Certificate Chain Validation)
      • 逻辑:客户端加载可信的CA根证书(如前文的ca.crt),使用该CA证书去验证Nginx发送的证书是否由该CA签发、是否在有效期内、域名是否匹配。
      • 场景:生产环境标准配置(如前文的HttpsTrustCaUtil)。

7.2 核心证书文件(key、csr、crt)与参数详解

在证书生成过程中,keycsrcrt是三个核心文件,配合X.509等标准共同工作。

1. 三种核心文件的作用

文件后缀全称中文含义核心作用与保管要求
.keyPrivate Key私钥文件核心机密。用于加密解密和数字签名。必须严格保管,不可泄露。泄露意味着证书被伪造。服务端配置时需确保文件权限为600。
.csrCertificate Signing Request证书请求文件申请书。包含申请者的公钥和身份信息(如域名、组织)。用于向CA机构(或自建CA)申请签发正式证书。签发完成后,该文件通常不再需要,可删除。
.crtCertificate证书文件身份证。由CA签发,包含公钥、持有者信息、有效期、CA签名等。用于分发给客户端(用于信任)或配置在服务端(用于出示身份)。

2. 关键参数与标准:X.509

  • X.509

    • 定义:这是国际电信联盟(ITU)和国际标准化组织(ISO)制定的数字证书标准
    • 作用:它定义了公钥证书的格式,规定了证书中必须包含的信息(如版本号、序列号、签名算法、颁发者、有效期、主体、公钥信息、扩展信息等)。
    • 现状:目前互联网上绝大多数SSL/TLS证书都遵循X.509标准(通常是v3版本)。我们在OpenSSL命令中使用的-x509参数,就是指生成一个自签的X.509格式证书。

3. CA根证书签发证书的本质与作用

  • 本质: CA根证书签发子证书的本质是 “数字签名”(Digital Signing) 的过程。 CA机构(或我们自建的CA)使用自己的私钥(ca.key) ,对服务端提交的证书请求(server.csr) 中的信息(公钥、域名等)进行加密哈希运算,生成一个签名(Signature) ,并将这个签名和CA的信息打包,生成最终的服务端证书(server.crt) 。最终的 server.crt(对应你方案中的 order.crt、pay.crt 等),既包含服务端本身的公钥及服务端信息,也包含 CA 的公钥及 CA 的信息

  • 作用(为什么需要CA签发?)

    1. 建立信任链(Chain of Trust) : 客户端只需要信任根证书(Root CA),就可以信任所有由该根证书直接或间接签发的下级证书。这解决了“公钥分发”的信任问题。
    2. 防篡改(Integrity) : 如果攻击者篡改了服务端证书中的任何信息(比如把公钥换了),客户端用CA的公钥去验证签名时就会失败,从而发现证书被篡改。
    3. 统一管理(Scalability) : 在微服务架构中,如果有100个服务,客户端不需要维护100个服务的公钥。客户端只需维护1个CA根证书,即可验证这100个服务的身份。这就是前文推荐“方案3(CA+各应用证书)”的核心优势。

4. CA根证书签发证书的本质与作用

因为CRT是CA签发之后才产生的结果,你不能对“结果”进行“签发”,你只能对“申请”进行“签发”。简单来说:CSR是“申请书”,CRT是“身份证”。CA机构的工作是“审批申请”并“颁发身份证”,而不是拿着一张已经做好的身份证去盖章。 CSR里包含了公钥,CA需要把公钥写进证书里。

CA到底对什么进行了“签名”?

  1. 提取信息:CA解析CSR文件,提取出里面的 主体信息(比如CN=www.example.com)和 公钥
  2. 组装结构:CA把这些信息,加上 有效期序列号颁发者信息,组装成一个符合 X.509标准 的数据结构(这叫TBS证书,即“待签名证书”)。
  3. 数字签名:CA使用自己的 CA私钥,对这个TBS数据进行哈希运算并加密,生成 签名值
  4. 生成CRT:CA把 TBS数据 + 签名值 拼在一起,保存为 .crt 文件。

7.3 HTTPS认证加密完整流程

HTTPS核心是「先认证身份,再加密传输」,全程依赖你生成的各类证书,流程如下(以订单中心单向认证、支付中心双向认证为例):

1. 建立连接(触发SSL/TLS握手)

客户端(如商品中心微服务、浏览器)发起HTTPS请求(如order.micro-service.com),首先与服务端Nginx建立TCP连接,随后触发SSL/TLS握手(这是认证和加密的核心环节,对应你文档3.1中的补充说明)。

2. 身份认证(核心步骤,证书发挥关键作用)

这一步分「单向认证」和「双向认证」,对应你方案中的两种配置,证书作用完全不同:

  • 单向认证(如商品中心调用订单中心): ① 服务端(Nginx)自动发送自身证书(order.crt,服务端证书)给客户端; ② 客户端(商品中心微服务)通过加载的CA根证书(ca.crt),校验order.crt的合法性(确认是由可信的CA签发、未被篡改)—— 这就是你文档中说的「证书链校验」; ③ 校验通过,确认服务端是合法的订单中心(不是伪造的);校验失败,拒绝建立连接。
  • 双向认证(如订单中心调用支付中心): ① 先执行单向认证的所有步骤(服务端发送pay.crt,客户端用ca.crt校验); ② 服务端(Nginx)要求客户端出示身份凭证(客户端证书client.crt); ③ 客户端发送client.crt给服务端,服务端通过自身配置的CA根证书(ca.crt),校验client.crt的合法性; ④ 双方身份都验证通过,进入下一步;任意一方校验失败,连接终止。

3. 协商加密算法与会话密钥

身份认证通过后,客户端和服务端会协商一套「对称加密算法」(如AES),并生成一个「临时会话密钥」(用于后续数据加密)。

  • 关键:这个会话密钥的传递的是「加密的」—— 用服务端证书(order.crt/pay.crt)的公钥加密会话密钥,只有服务端的私钥(order.key/pay.key)能解密,避免会话密钥被窃听。

4. 加密传输数据

后续客户端与服务端的所有交互(如查询订单、发起支付),都会用步骤3协商好的「会话密钥」进行对称加密,传输的数据都是密文,即使被截取,也无法破解(除非会话密钥泄露)。

  • 补充:对称加密效率高,适合大量数据传输;而证书的公钥/私钥加密(非对称加密),仅用于「身份认证」和「会话密钥传递」(非对称加密效率低,不适合大量数据传输)。

7.4 各类证书文件在流程中的核心作用

结合你生成的证书(ca.crt、ca.key、order.crt、order.key、client.crt等),逐个说明作用,对应流程中的具体环节:

1. CA根证书相关(ca.crt、ca.key)

  • ca.crt(CA根证书公钥):「信任锚点」,所有校验的核心依据。 ① 客户端用它校验服务端证书(order.crt/pay.crt)的合法性(确认是自己签发的,未被篡改); ② 服务端(双向认证)用它校验客户端证书(client.crt)的合法性; ③ 对应你方案的核心优势:只需信任1个ca.crt,无需逐个信任每个服务的证书,简化配置。
  • ca.key(CA根证书私钥):「签发证书的核心钥匙」,仅用于签发服务端/客户端证书(如order.crt、client.crt),不可泄露(一旦泄露,攻击者可伪造任意证书,整个认证体系失效)。

2. 服务端证书相关(order.crt、order.key、pay.crt、pay.key等)

  • 服务端证书(order.crt/pay.crt):「服务端的身份凭证」,相当于服务端的“身份证”。 ① 单向/双向认证中,传递给客户端,证明自己是合法的服务端; ② 包含服务端的公钥,用于加密「会话密钥」,传递给服务端。
  • 服务端私钥(order.key/pay.key):「服务端的专属钥匙」,仅存于服务端Nginx,不可泄露。 ① 解密客户端用服务端证书公钥加密的「会话密钥」; ② 验证自身证书的合法性(防止证书被篡改)。

3. 客户端证书相关(client.crt、client.key)

  • client.crt(客户端证书):「客户端的身份凭证」,仅用于双向认证。 ① 双向认证中,客户端传递给服务端,证明自己是合法的调用方(如订单中心调用支付中心,需出示client.crt); ② 由CA根证书(ca.crt)签发,确保服务端能通过ca.crt校验其合法性。
  • client.key(客户端私钥):「客户端的专属钥匙」,仅存于客户端(如订单中心微服务),不可泄露。 ① 配合client.crt,完成服务端对客户端的身份校验; ② 部分场景下,用于解密服务端发送的加密数据(按需配置)。

总结:

  1. 认证流程:先握手 → 身份认证(单/双向,证书核心作用) → 协商密钥 → 加密传输;
  2. 证书核心:ca.crt是“信任核心”,服务端证书是“服务端身份证”,客户端证书是“客户端身份证”,私钥(ca.key、order.key等)是“解密/签发的专属钥匙”;
  3. 对应你方案:你生成的3种证书方案,本质是改变“身份认证的凭证来源”,但HTTPS的认证加密流程不变,只是证书的使用方式不同(如方案1共用证书,就是所有服务共用1个“身份证”)。

7.5单证书多域名场景

7.5.1 一个证书能填多个域名吗?

可以,(用SAN字段)。默认情况下,你之前执行的 openssl req -new -key order.key -out order.csr 命令,仅能在 Common Name 字段填写1个域名(如order.micro-service.com),无法直接填多个域名。

但可以通过 添加SAN(Subject Alternative Name,主题备用名称)字段,让一个证书绑定多个独立域名(如同时绑定order.micro-service.com、goods.micro-service.com),适配你多微服务共用1个证书的场景(方案1可优化)。

具体操作(适配你方案的OpenSSL命令):

  1. 新建一个配置文件(如san.cnf),添加SAN字段,内容如下(可直接复制):

    [req]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req
    [req_distinguished_name]
    CN = order.micro-service.com  # 主域名(可任意填,最终以SAN为准)
    [v3_req]
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = order.micro-service.com  # 订单中心域名
    DNS.2 = goods.micro-service.com  # 商品中心域名
    DNS.3 = pay.micro-service.com    # 支付中心域名
    # 可继续添加更多域名,DNS.4、DNS.5...
    
  2. 生成csr文件时,指定该配置文件,命令修改为:

    openssl req -new -key order.key -out order.csr -config san.cnf
    
  3. 后续用CA根证书签发crt证书(命令不变),生成的order.crt即可同时绑定3个微服务域名,Nginx配置时只需用这1个证书,所有绑定域名均可正常通过HTTPS校验。

注意事项:

  • 适配场景:适合方案1(共用1套证书),可减少证书管理成本,无需为每个微服务单独生成证书;
  • 校验要求:证书绑定的所有域名,需与Nginx配置的server_name完全一致(如证书绑定goods.micro-service.com,Nginx的server_name也需填这个);
  • 兼容性:主流浏览器、微服务客户端(Apache HttpClient)均支持SAN字段,无兼容性问题。

7.5.2 域名能用通配符吗?

可以使用通配符域名(如*.micro-service.com),一个证书可匹配所有以micro-service.com为后缀的子域名,完美适配你所有微服务(订单、商品、支付均为子域名)的场景,比SAN字段更简洁。

具体操作(适配你方案的OpenSSL命令):

  1. 无需新建配置文件,直接生成csr时,在Common Name字段填写通配符域名即可:

    • 执行命令 openssl req -new -key server.key -out server.csr
    • 当提示 Common Name []: 时,填写 *.micro-service.com(仅填这一个,无需其他域名);
  2. 用CA根证书签发crt证书(命令不变),生成的server.crt可匹配所有子域名:

    • 适配:order.micro-service.com、goods.micro-service.com、pay.micro-service.com 均能正常使用该证书;
  3. Nginx配置:所有微服务的Nginx,均可使用这套证书,server_name 填写对应子域名即可(如order.micro-service.com)。

注意事项(必看,避免踩坑):

  1. 通配符范围:仅匹配一级子域名,比如 *.micro-service.com 可匹配order.micro-service.com,但无法匹配a.order.micro-service.com(二级子域名);
  2. 场景适配:比SAN字段更适合你当前的微服务架构(所有服务均为同一后缀子域名),方案1(共用证书)用通配符证书最便捷;
  3. 安全性:与普通证书一致,仅需妥善保管私钥,适合测试环境或非核心服务;核心服务(支付中心)建议用独立证书(方案3),更安全。

总结

  1. 多域名需求:用SAN字段(可绑定任意多个不同后缀域名)或通配符(仅绑定同一后缀子域名),均可实现“一个证书多个域名”;

  2. 你的场景推荐:优先用 通配符域名(*.micro-service.com) ,操作更简单,无需配置SAN文件,适配所有微服务子域名;

  3. 与方案适配:

    • 方案1(共用证书):用通配符证书,大幅简化配置和管理;
    • 方案3(CA+各应用证书):不推荐用多域名/通配符,建议每个微服务用独立证书(隔离性好,更安全)。

7.5.2 证书过期了怎么办?

一、核心原则(必遵循)

  1. 过期证书无法续期,只能重新生成/申请,需保持「私钥不变、证书信息(域名、签发机构)与原证书一致」,避免修改Nginx、客户端配置,另注意私钥key文件不存在过期概念。
  2. 优先沿用原证书生成方案,例如原用方案3(CA+各应用证书),则仍用CA根证书重新签发,不切换方案;
  3. 操作顺序:先重新生成证书 → 替换Nginx证书 → 同步更新客户端(仅双向认证需更新客户端证书) → 验证有效性。

二、过期场景

针对方案3(CA根证书+各应用证书,推荐,过期),分两种情况:

  1. 仅各应用服务端证书过期(CA根证书未过期,最常见):

    • 重新用CA根证书签发对应应用证书(沿用原应用私钥、路径、文件名),以订单中心为例:
      cd /usr/local/ssl/ssl_order
      # 私钥(order.key)不变,重新生成证书请求文件(order.csr)
      openssl req -new -key order.key -out order.csr
      # 用CA根证书重新签发新证书(order.crt)
      openssl x509 -req -in order.csr -CA /usr/local/ssl/ssl_ca/ca.crt -CAkey /usr/local/ssl/ssl_ca/ca.key -CAcreateserial -out order.crt -days 3650
      
    • 支付、商品中心同理,重新签发;
    • 替换Nginx证书、平滑重启Nginx,客户端无需额外操作(因信任的是CA根证书,CA未过期,无需重新加载)。
  2. CA根证书过期(所有应用证书均失效):

    • 先重新生成CA根证书(沿用原路径、原文件名,避免客户端改配置):
      cd /usr/local/ssl/ssl_ca
      # 私钥(ca.key)不变,重新生成CA根证书(ca.crt)
      openssl req -new -x509 -key ca.key -out ca.crt -days 3650
      
    • 再用新的CA根证书,重新签发所有应用的服务端证书(步骤同场景3第1点);
    • 替换Nginx证书、重启Nginx;
    • 客户端更新:所有信任CA根证书的客户端(微服务/浏览器/APP),需重新加载新的CA根证书(双向认证需同时更新客户端证书)。