如何在Spring Feign Client中使用P12客户端证书

时间:2019-09-23 09:41:30

标签: spring-boot client-certificates spring-cloud-feign

我有一个调用远程服务的Spring Boot应用程序。

此远程Web服务为我提供了一个p12文件,该文件应该对我的应用程序进行身份验证。

如何配置伪装客户端以使用p12证书?


我尝试设置以下属性:

-Djavax.net.ssl.keyStore=path_to_cert.p12 -Djavax.net.ssl.keyStorePassword=xxx -Djavax.net.ssl.keyStoreType=PKCS12

但是它并没有改变任何东西,我仍然收到此错误:

sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

2 个答案:

答案 0 :(得分:1)

如果您希望在不使用keytool的情况下以编程方式实现上述效果,则可以执行以下操作:

class CustomFeignConfiguration {

    private val log = Logger.getLogger(this.javaClass.name)

    @Value("\${client_p12_base64_encoded_string}")
    private val clientP12: String = ""

    @Value("\${client_p12_password}")
    private val clientP12Pass: String = ""

    @Bean
    fun feignClient(): Client {
        val sslSocketFactory= getSSLSocketFactory()
        log.info("CUSTOM FEIGN CLIENT CALLED")
        return Client.Default(sslSocketFactory, DefaultHostnameVerifier())
    }

    private fun getSSLSocketFactory(): SSLSocketFactory {
        val decoder = java.util.Base64.getDecoder()
        val p12 = decoder.decode(clientP12)
        val p12File = File("clientCer.p12")
        p12File.writeBytes(p12)

        try {
            val sslContext = SSLContexts
                .custom()
                .loadKeyMaterial(p12File, clientP12Pass.toCharArray(), clientP12Pass.toCharArray())
                .build()
            return sslContext.socketFactory
        } catch (exception: Exception) {
            throw RuntimeException(exception)
        }

    }
}

使用配置的FeignClient接口必须专门加载

@FeignClient(name = "client", configuration = [CustomFeignConfiguration::class], url = "\${url}")
interface Client {
  ....
  ....
}

SSLContexts库只能使用p12证书,我们必须将PEM格式的证书和密钥转换为P12格式。

使用以下SSL命令从PEM证书和密钥创建p12证书:

openssl pkcs12 -export -inkey domain.key -in domain.crt -out domain.p12

请记录您在运行此命令后输入的密码。

使用以下命令将此p12证书转换为base64字符串

base64 domain.p12 > domain.p12.base64

使用以下命令将此多行字符串转换为单行字符串:

tr -d "\n\r" < domain.p12.base64 > domain.p12.base64.singleline

使用此命令中的单行字符串以及您先前在 application.properties 中记录的密码。

答案 1 :(得分:0)

我终于可以通过大量的反复试验来做到这一点。

问题是,默认情况下,伪装生成器会使用空SSLSocketFactory来构建伪装客户端:

org.springframework.cloud.openfeign.FeignClientsConfiguration#feignBuilder

SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 0, 0x02);

feign.Feign.Builder

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
    return Feign.builder().retryer(retryer);
}

因此,我必须在@Configuration中定义此bean:

  public static class Builder {
    // ...
    private Client client = new Client.Default(null, null);

使用此方法:(不记得源代码)

@Bean
@Profile({"prod", "docker"})
public Feign.Builder feignBuilder() {
    return Feign.builder()
        .retryer(Retryer.NEVER_RETRY)
        .client(new Client.Default(getSSLSocketFactory(), null));

现在,它对我有用,我通过伪装客户端调用进行了调试,并且sslSocketFactory已正确传递到基础连接。