当我尝试通过 HTTP 代理使用 OkHttp 进行 HTTPS 调用时,KeyManager 不起作用

问题描述 投票:0回答:1

简介:

我正在尝试用 Java 编写对公共 API 的调用(它不是已知的 API,但该域是公共的,可以从任何具有互联网访问权限的计算机进行访问)。 为了进行调用,API 所有者向我发送了 openAPi (v3.0.1) 规范。 由于维护/工业化问题,决定使用 openapi-generator-maven-plugin (v7.5.0) 通过 openAPI 规范自动生成客户端。 该插件默认使用 OkHttp3 来生成客户端类。 我必须调用的 API 有一个特点,它经过编程,因此必须发送客户端证书进行身份验证。 如果我从具有直接访问权限的环境中进行调用,则它可以正常工作。 如果我从需要通过 HTTP 代理的环境进行调用,它将无法正常工作。

配置(因保密问题更换变量):

certConf:
  apiKey: {API_KEY}
  urlService: {URL_SERVICE}
  keyStorePath: ".\\src\\main\\resources\\{CLIENT_CERTIFICATE}.pfx"
  keyStorePassword: {CLIENT_CERTIFICATE_PASSWORD}
  keyStoreType: "PKCS12"
  certificatValidePath: ".\\src\\main\\resources\\{API_CERTIFICATE_CHAIN}.crt"

proxy:
  externe:
    port: 8080
    address: {PROXY_IP}

直接致电:

如果我直接调用它就可以正常工作 ->

package {MY_CONF_PACKAGE};

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import {MY_GENERATED_SOURCES_PACKAGE}.ApiInterface;
import {MY_GENERATED_SOURCES_PACKAGE}.ApiClient;
import {MY_CONF_PACKAGE}.CertConf;
import {MY_CONF_PACKAGE}.ProxyConf;
import okhttp3.OkHttpClient;

@Configuration
public class MyClientConf {
    
    
    @Autowired
    private CertConf certConf;
    
    @Autowired
    private ProxyConf proxyConf;
    
    
    @Bean
    public ApiInterface apiInterfaceController() throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        var apiInterface = new ApiInterface();
        
        apiInterface.setCustomBaseUrl(certConf.getUrlService());
        apiInterface.setApiClient(apiClient());
        
        return apiInterface;
    }

    @Bean
    public ApiClient apiClient() throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        var apiClient = new ApiClient();
        
//      apiClient.setHttpClient(new OkHttpClient.Builder().proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyConf.getExterne().getAddress(), proxyConf.getExterne().getPort())))
//               .build());
        apiClient.setKeyManagers(initKeyManager(certConf.getKeyStorePath(), certConf.getKeyStorePassword(), certConf.getKeyStoreType()));
        apiClient.setSslCaCert(new FileInputStream(certConf.getCertificatValidePath()));
        apiClient.setApiKey(certConf.getApiKey());      
        
        return apiClient;
    }
    
    
    private KeyManager[] initKeyManager(String keyStorePath, String keyStorePassword, String keyStoreType) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {

        try(var keyStoreFile = new FileInputStream(keyStorePath)) {
            
            var keyStore = KeyStore.getInstance(keyStoreType);
        
            keyStore.load(keyStoreFile, keyStorePassword.toCharArray());
            var keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
            return keyManagerFactory.getKeyManagers();
        
        }

    }
    

}

此行正确完成其工作(我设法加密连接,以便它是 HTTPS,表明我调用的 API 的 {API_CERTIFICATE_CHAIN} 是可信的)->

    apiClient.setSslCaCert(new FileInputStream(certConf.getCertificatValidePath()));
    

如果配置不正确,我会收到以下错误 ->

PKIX 路径构建失败

此行正确完成其工作(我设法通过发送 {CLIENT_CERTIFICATE} 来通过服务器身份验证屏障)->

apiClient.setKeyManagers(initKeyManager(certConf.getKeyStorePath(), certConf.getKeyStorePassword(), certConf.getKeyStoreType()));

如果配置不正确,我会收到以下错误 ->

403:禁止:缺少证书

通过 HTTP 代理调用不起作用:

如果我通过代理拨打电话,它会部分工作 ->

package {MY_CONF_PACKAGE};

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import {MY_GENERATED_SOURCES_PACKAGE}.ApiInterface;
import {MY_GENERATED_SOURCES_PACKAGE}.ApiClient;
import {MY_CONF_PACKAGE}.CertConf;
import {MY_CONF_PACKAGE}.ProxyConf;
import okhttp3.OkHttpClient;

@Configuration
public class MyClientConf {
    
    
    @Autowired
    private CertConf certConf;
    
    @Autowired
    private ProxyConf proxyConf;
    
    
    @Bean
    public ApiInterface apiInterfaceController() throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        var apiInterface = new ApiInterface();
        
        apiInterface.setCustomBaseUrl(certConf.getUrlService());
        apiInterface.setApiClient(apiClient());
        
        return apiInterface;
    }

    @Bean
    public ApiClient apiClient() throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        var apiClient = new ApiClient();
        
        apiClient.setHttpClient(new OkHttpClient.Builder().proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyConf.getExterne().getAddress(), proxyConf.getExterne().getPort())))
                 .build());
        apiClient.setKeyManagers(initKeyManager(certConf.getKeyStorePath(), certConf.getKeyStorePassword(), certConf.getKeyStoreType()));
        apiClient.setSslCaCert(new FileInputStream(certConf.getCertificatValidePath()));
        apiClient.setApiKey(certConf.getApiKey());      
        
        return apiClient;
    }
    
    
    private KeyManager[] initKeyManager(String keyStorePath, String keyStorePassword, String keyStoreType) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {

        try(var keyStoreFile = new FileInputStream(keyStorePath)) {
            
            var keyStore = KeyStore.getInstance(keyStoreType);
        
            keyStore.load(keyStoreFile, keyStorePassword.toCharArray());
            var keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
            return keyManagerFactory.getKeyManagers();
        
        }

    }
    

}

此行正确完成其工作(我设法加密连接,以便它是 HTTPS,表明我调用的 API 的 {API_CERTIFICATE_CHAIN} 是可信的)->

    apiClient.setSslCaCert(new FileInputStream(certConf.getCertificatValidePath()));
    

如果配置不正确,我会收到以下错误 ->

PKIX 路径构建失败

此行未正确完成其工作(我无法通过发送 {CLIENT_CERTIFICATE} 来设法通过服务器身份验证屏障)->

apiClient.setKeyManagers(initKeyManager(certConf.getKeyStorePath(), certConf.getKeyStorePassword(), certConf.getKeyStoreType()));

我收到以下错误 ->

403:禁止:缺少证书

这条线完成了部分工作 ->

    apiClient.setHttpClient(new OkHttpClient.Builder().proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyConf.getExterne().getAddress(), proxyConf.getExterne().getPort())))
             .build());
             

我设法通过代理进行呼叫,因为如果配置不正确,我会收到以下错误 ->

连接超时

问题:

我得出的结论是,问题的根源是 HTTP 代理没有正确重定向 {CLIENT_CERTIFICATE}。代理和密钥管理器之间存在我无法理解的交互。

注:

问题不是由流和 HTTP 代理的配置引起的(我已经使用 POSTMAN 进行了调用,无论是否使用 HTTP 代理,它都能正常工作)。 我使用 HTTP 代理调用的环境无法直接访问 API,并且与我直接调用的环境不同。

提前致谢

java okhttp http-proxy client-certificates openapi-generator-maven-plugin
1个回答
0
投票

我已经解决了问题,但部分解决了。如果我使用 JDK 和更现代版本的 java 编译并启动应用程序,它就可以工作。这解决了问题,但我不明白到底是什么解决了它,因为该项目是用 Java 11 制作的,除了这个 HTTP 代理错误之外,它可以正常编译和工作。

JDK11 -> 除了与 HTTP 的奇怪交互之外,所有项目都正常工作

Proxy JDK17 -> 解决HTTP Proxy的错误

这是一个问题,因为迁移应用程序并非易事。

© www.soinside.com 2019 - 2024. All rights reserved.