Заставьте клиент Feign брать хранилище доверенных сертификатов из пользовательского свойства

Клиент Feign в нашем приложении общается с самозаверяющим сервером. Мы можем заставить клиент Feign использовать собственное хранилище доверенных сертификатов, используя системное свойство javax.net.ssl.trustStore. Но поскольку мое приложение также взаимодействует со стандартными сайтами, сертифицированными CA, хранилище доверенных сертификатов по умолчанию не должно быть переопределено.

Как я могу использовать собственное хранилище доверенных сертификатов без использования системного свойства javax.net.ssl.trustStore? Или как мне сделать так, чтобы мой клиент Feign использовал хранилище доверенных сертификатов из свойства, отличного от стандартного системного свойства javax.net.ssl.trustStore?


person Kannan Ramamoorthy    schedule 13.04.2018    source источник


Ответы (3)


В итоге я создал свой собственный экземпляр SSLSocketFactory, который я передаю своему клиенту Feign, используя приведенный ниже код:

/**
 * Gets the {@link SSLSocketFactory} instance for the client communication
 * using the given truststore file and password.
 * 
 * Since the instance is used as client, this is instantiated with empty
 * keystore and the truststore represented by the given truststore file.
 * 
 * 
 * @param theTrustStoreFile
 *            The complete file path of the truststore.
 * @return {@link SSLSocketFactory} instance that internally uses the given
 *         truststore.
 * @throws Exception
 *             When there is an error in the creating the
 *             {@link SSLSocketFactory} instance.
 */
public static SSLSocketFactory getClientSSLSocketFactory(File theTrustStoreFile)
        throws Exception
{
    // This supports TLSv1.2
    SSLContext sslContext = SSLContext.getInstance("TLS");

    KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());

    FileInputStream file = getFileInputStream(theTrustStoreFile);
    kStore.load(file, null);

    TrustManagerFactory tmf = TrustManagerFactory
            .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(kStore);

    sslContext.init(new KeyManager[] {}, tmf.getTrustManagers(), null);

    return sslContext.getSocketFactory();
}

/**
 * Reads the file into {@link FileInputStream} instance.
 * 
 * @param file
 *            The file to be read.
 * @return {@link FileInputStream} that represents the file content/
 * @throws Exception
 *             When there is any error in reading the file.
 */
private static FileInputStream getFileInputStream(final File file) throws Exception
{
    return AccessController.doPrivileged(new PrivilegedExceptionAction<FileInputStream>()
    {
        @Override
        public FileInputStream run() throws Exception
        {
            try
            {
                if (file.exists())
                {
                    return new FileInputStream(file);
                } else
                {
                    return null;
                }
            } catch (FileNotFoundException e)
            {
                // couldn't find it, oh well.
                return null;
            }
        }
    });
}

И когда я создаю экземпляр своего клиента, я делаю это так:

Feign.builder().client(getClientSSLSocketFactory(trustFile),null)...

Этот содержит пример кода и его использование.

person Kannan Ramamoorthy    schedule 16.04.2018

Вот как я использовал FeignClient с keystore и truststore

Конфигурация FeignClient

@Configuration
public class TestClientConfig {

    @Bean
    public Client feignClient() {
        Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
        return trustSSLSockets;
    }

    private SSLSocketFactory getSSLSocketFactory() {
        try {
            TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    //Do your validations
                    return true;
                }
            };
            String allPassword = "123456";
            SSLContext sslContext = SSLContextBuilder
                    .create()
                    // .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.p12"), allPassword.toCharArray(), allPassword.toCharArray())
                    .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword.toCharArray(), allPassword.toCharArray())
                    .loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword.toCharArray())
                    .build();
            return sslContext.getSocketFactory();
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        }
    }
}

Интерфейс

@FeignClient(name = "Test", url = "https://localhost:8443",configuration=TestClientConfig.class)
public interface TestClient {

    @RequestMapping(method = RequestMethod.GET,value = "/hello")
    String getHello();
}
person Niraj Sonawane    schedule 05.12.2019
comment
Вы ничего не делаете с созданным вами acceptingTrustStrategy. Вы намеревались передать его в sslContext? - person Shannon; 21.04.2021

Благодаря комбинации ответов Каннана и Нираджа мне удалось настроить мой фиктивный клиент для использования customSSLSocketFactory.

Мой вариант использования: у меня есть API, для которого требуется добавить SSL-сертификат на мою локальную машину на JAVA cacerts, чтобы получить его. Однако я не могу добавить этот сертификат после развертывания на облачном сервере. Следовательно, мне нужно было настроить мой клиент Feign для получения пользовательского хранилища доверия из самого моего приложения.

Вот моя версия этого

  1. Создайте свое собственное хранилище доверенных сертификатов с добавленным сертификатом keytool -import -file <filepath-of-certificate> -alias <certificate-alias-name> -keystore <truststore-alias-name> система запросит пароль, установите свой собственный пароль и запомните его Доверительное хранилище не требует никаких расширений для этого случая

  2. Скопируйте этот файл хранилища доверенных сертификатов в папку ресурсов вашего весеннего загрузочного приложения.

  3. В файле application.properties или application.yml укажите путь к классам и пароль для вашего хранилища доверенных сертификатов. Я использую application.yml, поэтому пример выглядит следующим образом

e.g.

example-service:
  server: https://example.api.com
  trustStore: classpath:<Your truststore name>
  trustStorePassword: yourpassword

* Вы также можете пропустить шаг 3 и напрямую перейти в хранилище доверенных сертификатов и пароль ниже *

  1. В классе CustomFeignConfig вы можете напрямую передать хранилище доверенных сертификатов в качестве ресурса и напрямую передать входной поток хранилища доверенных сертификатов в sslsocketfactory. Обратите внимание, что нотация Bean необходима для переопределения конфигурации имитации по умолчанию. Мне требуется хранилище доверенных сертификатов только для моего случая, поэтому я передаю пустой новый KeyManager[]{} в sslContext.init()

` открытый класс CustomFeignConfig {

@Bean
public Client feignClient(@Value("${example-service.trustStore}") Resource trustStoreResource,
                          @Value("${example-service.trustStorePassword}") String trustStorePassword) throws Exception {
    try {
        return new Client.Default(
                sslSocketFactory(trustStoreResource.getInputStream(), trustStorePassword),
                null);
    } catch (Exception e) {
        throw new Exception("Error in initializing feign client", e);
    }
}

private static SSLSocketFactory sslSocketFactory(InputStream trustStoreStream, String trustStorePassword)
        throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, KeyManagementException {
    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    TrustManagerFactory tmf = createTrustManager(trustStoreStream, trustStorePassword);
    sslContext.init(new KeyManager[]{}, tmf.getTrustManagers(), null);
    return sslContext.getSocketFactory();
}

private static TrustManagerFactory createTrustManager(InputStream trustStoreStream, String trustStorePassword)
        throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    trustStore.load(trustStoreStream, trustStorePassword.toCharArray());
    TrustManagerFactory tmf = TrustManagerFactory
            .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(trustStore);
    return tmf;
}

} `

  1. Примените эту пользовательскую конфигурацию имитации только к тому API, для которого она требуется.

@FeignClient(name = "example-service", url = "${example-service.server}", configuration = CustomFeignConfig.class) public interface MyFeignClient {}

person hairy25    schedule 14.06.2021