如何在spring feign客户端中使用p12客户端证书

vfh0ocws  于 2023-04-20  发布在  Spring
关注(0)|答案(3)|浏览(281)

我有一个调用远程服务的Sping 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
ie3xauqp

ie3xauqp1#

我终于可以通过大量的盲目试验和错误来做到这一点。
问题是,默认情况下,feign构建器使用null SSLSocketFactory构建feign客户端:

org.springframework.cloud.openfeign.FeignClientsConfiguration#feignBuilder

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

feign.Feign.Builder

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

所以,我必须在@Configuration中定义这个bean:

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

用这种方法:(不记得来源)

SSLSocketFactory getSSLSocketFactory() {
    char[] allPassword = keyStorePassword.toCharArray();
    SSLContext sslContext = null;
    try {
        sslContext = SSLContextBuilder
            .create()
            .setKeyStoreType(keyStoreType)
            .loadKeyMaterial(ResourceUtils.getFile(keyStore), allPassword, allPassword)
            .build();
    } catch (Exception e) { /* *** */ }
    return sslContext.getSocketFactory();
}

现在,它为我工作了,我调试了假装的客户端调用,sslSocketFactory被正确地传递给了底层连接。

vlf7wbxs

vlf7wbxs2#

如果您希望在不使用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中记录的密码。

8i9zcol2

8i9zcol23#

与@BiAiB的回答相反,如果有人在2023年面临同样的问题。我在Feign Configurations中将客户端配置为Bean,并在那里设置SSL Socket Factory详细信息。
Gradle导入:

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.6'
implementation 'io.github.openfeign:feign-httpclient:12.3'

虚拟配置中的客户端Bean:

@Bean
public Client feignClient() throws Exception {
    log.info("Configuring SSL Context for Feign Client");
    return new Client.Default(createSSLContext(), SSLConnectionSocketFactory.getDefaultHostnameVerifier());
}

并从资源文件中为创建了SSL Socket Factory,如下所示:

private SSLSocketFactory createSSLContext() throws Exception {
    String trustStorePath = "classpath:cacerts"
    String keyStorePath = "classpath:client-key.pfx"

    log.info("Trust Store for Feign Client: " + trustStorePath);
    log.info("Key Store for Feign Client: " + keyStorePath);

    KeyStore keyStore = KeyStore.getInstance("PKCS12"); // PKCS12 for PFX files. Change this to 'JKS' if you are using java keystore
    keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePassword.toCharArray());

    SSLContext context = SSLContextBuilder.create()
            .loadTrustMaterial(ResourceUtils.getFile(trustStorePath), trustStorePassword.toCharArray())
            .loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
            .build();
    return context.getSocketFactory();
}

相关问题