You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Eureka双向SSL模式下无法自注册,报bad certificate异常

问题

我尝试通过双向SSL保障微服务间通信,服务A可正常注册到Eureka Server,但Eureka实例自注册或注册到其他Eureka实例时,始终出现bad certificate异常。

Eureka Server的application.yml配置

server:
  port: ${SERVER_SSL_PORT:${PORT:10100}}
  ssl:
    enabled: true
    client-auth: need
    key-alias: ${JKS_ALIAS}
    key-password: ${JKS_KEY_PASSWORD}
    key-store-type: ${JKS_TYPE}
    key-store-provider: ${JKS_PROVIDER:SUN}
    key-store: ${JKS_PATH}
    key-store-password: ${JKS_KEY_PASSWORD}
    trust-store: ${JKS_PATH}
    trust-store-password: ${JKS_KEY_PASSWORD}
    trust-store-provider: ${JKS_PROVIDER:SUN}
    trust-store-type: ${JKS_TYPE}

## EUREKA CONFIGURATION
eureka:
  client:
    enabled: true
    register-with-eureka: true
    fetch-registry: false
    service-url:
      defaultZone: ${SVC_REGISTRY:https://localhost:14102/eureka}
    prefer-same-zone-eureka: true
    healthcheck:
      enabled: true
    tls:
      enabled: true
      key-store: ${JKS_PATH}
      key-password: ${JKS_KEY_PASSWORD}
      key-store-type: ${JKS_TYPE}
      key-store-password: ${JKS_KEYSTORE_PASSWORD}
      trust-store: ${JKS_PATH}
      trust-store-password: ${JKS_KEY_PASSWORD}
      trust-store-type: ${JKS_TYPE}
  instance:
    instance-id: ${SERVER_NAME:${spring.application.name}:${server.port}}@${eureka.instance.hostname}
    hostname: ${SERVER_HOST:localhost}
    home-page-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}
    status-page-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}${management.endpoints.web.base-path:}/info
    health-check-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}${management.endpoints.web.base-path:}/health
    secure-health-check-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}${management.endpoints.web.base-path:}/health
    lease-renewal-interval-in-seconds: 10
    prefer-ip-address: true
    metadata-map:
      instanceId: ${eureka.instance.instance-id}
      zone: ${SERVER_ZONE:local}
    non-secure-port-enabled: false
    secure-port-enabled: true
    secure-port: ${server.port}

自定义JerseyClient配置

@Bean
@SneakyThrows
public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs2() {
    DiscoveryClient.DiscoveryClientOptionalArgs args = new DiscoveryClient.DiscoveryClientOptionalArgs();

    EurekaJerseyClientImpl.EurekaJerseyClientBuilder clientBuilder = new EurekaJerseyClientImpl.EurekaJerseyClientBuilder()
            .withClientName("DiscoveryClient-HTTPClient-Custom")
            .withUserAgent("Java-EurekaClient")
            .withConnectionTimeout(config.getEurekaServerConnectTimeoutSeconds() * 1000)
            .withReadTimeout(config.getEurekaServerReadTimeoutSeconds() * 1000)
            .withMaxConnectionsPerHost(config.getEurekaServerTotalConnectionsPerHost())
            .withMaxTotalConnections(config.getEurekaServerTotalConnections())
            .withConnectionIdleTimeout(config.getEurekaConnectionIdleTimeoutSeconds() * 1000)
            .withEncoderWrapper(CodecWrappers.getEncoder(config.getEncoderName()))
            .withDecoderWrapper(CodecWrappers.resolveDecoder(config.getDecoderName(), config.getClientDataAccept()))
            .withCustomSSL(sslContext());
           
    EurekaJerseyClient jerseyClient = clientBuilder.build();
    args.setEurekaJerseyClient(jerseyClient);//Provide custom EurekaJerseyClient to override default one
    return args;
}

@Bean
public SSLContext sslContext() throws Exception {
    log.info("initialize ssl context bean with keystore {} ", env.getProperty("JKS_PATH"));
    char[] password = env.getProperty("JKS_KEY_PASSWORD").toCharArray();
    return new SSLContextBuilder()
            .loadTrustMaterial(ResourceUtils.getFile(env.getProperty("JKS_PATH")), password).build();
}

错误日志

2022-09-01 18:44:01.522 INFO 26829 --- [tbeatExecutor-0]
c.n.d.s.t.d.RedirectingEurekaHttpClient : Request execution error.
endpoint=DefaultEndpoint{
serviceUrl='https://XXXXXXXXXXXXXXXXXXXX:DDDD/eureka/},
exception=javax.net.ssl.SSLHandshakeException: Received fatal alert:
bad_certificate
stacktrace=com.sun.jersey.api.client.ClientHandlerException:
javax.net.ssl.SSLHandshakeException: Received fatal alert:
bad_certificate
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187)
at com.sun.jersey.api.client.Client.handle(Client.java:652)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.put(WebResource.java:529)
at

该证书在服务A注册到注册中心时可正常使用,且在搭载有效证书的真实服务器上测试,仍出现此问题。


排查解决线索

  • 校验Eureka TLS配置的密钥库密码一致性:eureka.client.tls.key-store-password使用的是${JKS_KEYSTORE_PASSWORD},而server.ssl等其他配置用的是${JKS_KEY_PASSWORD},若两个变量值不一致,会导致Eureka客户端无法正确加载密钥库。建议统一密码变量,或确认JKS_KEYSTORE_PASSWORD的值是否匹配密钥库实际密码。
  • 补充SSLContext的密钥库加载逻辑:当前自定义的sslContext()仅加载了信任库,双向SSL要求客户端同时提供自身证书供服务端验证,需补充加载密钥库的代码:
    @Bean
    public SSLContext sslContext() throws Exception {
        log.info("initialize ssl context bean with keystore {} ", env.getProperty("JKS_PATH"));
        char[] password = env.getProperty("JKS_KEY_PASSWORD").toCharArray();
        KeyStore keyStore = KeyStore.getInstance(env.getProperty("JKS_TYPE"));
        keyStore.load(new FileInputStream(ResourceUtils.getFile(env.getProperty("JKS_PATH"))), password);
        
        return new SSLContextBuilder()
                .loadKeyMaterial(keyStore, password)
                .loadTrustMaterial(ResourceUtils.getFile(env.getProperty("JKS_PATH")), password)
                .build();
    }
    
  • 确认目标Eureka Server信任当前实例证书:检查目标Eureka Server的trust-store是否包含当前Eureka实例的证书公钥,或确认双方使用的JKS文件是否互相包含对方证书。
  • 检查证书的有效期与SAN扩展:确认证书是否在有效期内,且证书的Subject Alternative Name(SAN)扩展包含当前Eureka实例的hostname或IP,地址不匹配会导致服务端拒绝证书。
  • 验证自定义JerseyClient是否生效:通过日志或调试模式确认discoveryClientOptionalArgs2 Bean是否被正确加载,Eureka客户端实际使用的SSLContext是否为自定义实例。
  • 确认Eureka Server的client-auth配置逻辑:当前server.ssl.client-auth设为need,要求所有客户端必须提供有效证书,需确保Eureka实例作为客户端时,正确发送了自身证书,而非仅信任服务端证书。

内容的提问来源于stack exchange,提问作者Angelo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:39:23