【问题标题】:Apache HttpClient 4.3 and x509 client certificate to authenticateApache HttpClient 4.3 和 x509 客户端证书进行身份验证
【发布时间】:2014-05-01 15:26:01
【问题描述】:

现在我正在寻找有关如何通过 HttpComponentsMessageSender(不相关)重写客户端 x509 证书身份验证的已弃用解决方案的任务的解决方案。

例如,不推荐使用的解决方案是:

    SSLSocketFactory lSchemeSocketFactory = new SSLSocketFactory(this.keyStore, this.keyStorePassword);
    Scheme sch = new Scheme("https", 443, lSchemeSocketFactory);

    DefaultHttpClient httpClient = (DefaultHttpClient)getHttpClient();
    httpClient.getConnectionManager().getSchemeRegistry().register(sch);

作为我正在使用的 CloseableHttpClient 的新解决方案:

    SSLContextBuilder sslContextBuilder = SSLContexts.custom()
            // this key store must contain the key/cert of the client
            .loadKeyMaterial(keyStore, keyStorePassword.toCharArray());

    if (trustStore != null) {
        // this key store must contain the certs needed and trusted to verify the servers cert
        sslContextBuilder.loadTrustMaterial(trustStore);
    }

    SSLContext sslContext = sslContextBuilder.build();

    LayeredConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);

    // Create a registry of custom connection socket factories for supported
    // protocol schemes / https
    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("https", sslsf)
            .register("http", new PlainConnectionSocketFactory())
            .build();

    PoolingHttpClientConnectionManager connPoolControl =
            new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    setConnPoolControl(connPoolControl);
    getClientBuilder().setSSLSocketFactory(sslsf);

我仍然收到来自服务器的 403 禁止。但是,当我使用“已弃用”版本的解决方案时,效果很好。 SSL 证书已签署 Thawte。

有什么想法吗? 谢谢

【问题讨论】:

    标签: java ssl httpclient x509certificate spring-ws


    【解决方案1】:

    Tomas,也许为时已晚,但我希望它能帮助其他人...... 有一种方法,我用它来使用 Apache HttpClient 4.3 创建 CloseableHttpClient:

    public static CloseableHttpClient prepareClient() {
        try {           
            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).useTLS().build();
            HttpClientBuilder builder = HttpClientBuilder.create();
            SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            builder.setSSLSocketFactory(sslConnectionFactory);
            Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("https", sslConnectionFactory)
                    .register("http", new PlainConnectionSocketFactory())
                    .build();
            HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
            builder.setConnectionManager(ccm);
            return builder.build();
        } catch (Exception ex) {
    
            return null;
        }
    }
    

    Apache 基金会从 4.4 版本开始将 org.apache.http.conn.ssl.SSLContextBuilder、org.apache.http.conn.ssl.SSLContexts 和 org.apache.http.conn.ssl.SSLSocketFactory 移至弃用,@987654321 @您可以找到 Apache Client 4.5.2 API 弃用列表。因此,可以像这样更改 pervious 方法:

    public static CloseableHttpClient prepareClient() {
        try {
            SSLContext sslContext = SSLContexts.custom()
                    .loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
            HttpClientBuilder builder = HttpClientBuilder.create();
            SSLConnectionSocketFactory sslConnectionFactory = 
                    new SSLConnectionSocketFactory(sslContext.getSocketFactory(), 
                            new NoopHostnameVerifier());
            builder.setSSLSocketFactory(sslConnectionFactory);
            Registry<ConnectionSocketFactory> registry = 
                    RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("https", sslConnectionFactory)
                    .register("http", new PlainConnectionSocketFactory())
                    .build();
            HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
            builder.setConnectionManager(ccm);
            return builder.build();
        } catch (Exception ex) {
            LOG.error("couldn't create httpClient!! {}", ex.getMessage(), ex);
            return null;
        }
    }
    

    NoopHostnameVerifier

    NO_OP HostnameVerifier 本质上是主机名验证 离开。此实现是无操作的,并且从不抛出 SSL 异常。

    如果你需要验证主机名,你可以使用 DefaultHostnameVerifier 或者你可以实现你的自定义主机名验证器。

    【讨论】:

      【解决方案2】:

      您需要创建一个包含受信任 CA 的密钥库,即trust.jks。在此密钥库中,您应该只放置应用程序将要连接的服务器的证书。

      然后,您需要一个用于服务器身份的密钥库,即identity.jks。在此密钥库中,您应该将私钥 + 证书 + CA 链存储在一个别名(名称)下,您的应用程序将使用该别名向服务器进行身份验证。

      然后你可以像这样构建HttpClient

      public static HttpClient getHttpClient() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException {
      
          KeyStore identityKeyStore = KeyStore.getInstance("jks");
          identityKeyStore.load(MyClass.class.getClassLoader().getResourceAsStream("identity.jks"), "identity_password".toCharArray());
      
          KeyStore trustKeyStore = KeyStore.getInstance("jks");
          trustKeyStore.load(MyClass.class.getClassLoader().getResourceAsStream("trust.jks"), "trust_password".toCharArray());
      
          SSLContext sslContext = SSLContexts
                  .custom()
                  // load identity keystore
                  .loadKeyMaterial(identityKeyStore, "identity_password".toCharArray(), new PrivateKeyStrategy() {
                      @Override
                      public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
                          return "identity_alias";
                      }
                  })
                  // load trust keystore
                  .loadTrustMaterial(trustKeyStore, null)
                  .build();
      
          SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
                  new String[]{"TLSv1.2", "TLSv1.1"},
                  null,
                  SSLConnectionSocketFactory.getDefaultHostnameVerifier());
      
          return HttpClients.custom()
                  .setSSLSocketFactory(sslConnectionSocketFactory)
                  .build();
      }
      

      要构建identity.jks,需要CAs链、公钥和私钥:

      $1 = mycustomidentity
      
      # make the keycert bundle for pkcs12 keystore
      cat intermediate/certs/ca-chain.cert.pem \
          intermediate/certs/$1.cert.pem \
          intermediate/private/$1.key.pem \
          > intermediate/keycerts/$1.full-chain.keycert.pem
      
      # generate the pkcs12 keystore with the alias of the server url
      openssl pkcs12 -export \
          -in intermediate/keycerts/$1.full-chain.keycert.pem \
          -out intermediate/pkcs12s/$1.full-chain.p12 \
          -name $1 \
          -noiter -nomaciter
      
      # .p12 to .jks
      keytool -importkeystore -srckeystore $1.full-chain.p12 \
          -srcstoretype pkcs12 -srcalias $1 \
          -destkeystore identity.jks -deststoretype jks \
          -deststorepass identity_password -destalias identity_alias
      

      trust.jks文件只需要服务器的证书(见https://stackoverflow.com/a/36427118/2692914https://stackoverflow.com/a/7886248/2692914),改别名没有问题:

      # .crt, .cer into a .jks
      keytool -import -alias trust_alias -file server_certificate.crt \
          -keystore trust.jks
      

      【讨论】:

      • 这里的 DnieUtils 是什么?
      • 类的名称。在这种情况下获取将加载资源的类加载器
      【解决方案3】:

      以下是 HttpClient 4.4+ 的代码(更新了 @Daniyar 4.4+ 的代码)

      import org.apache.http.config.Registry;
      import org.apache.http.config.RegistryBuilder;
      import org.apache.http.conn.socket.ConnectionSocketFactory;
      import org.apache.http.conn.socket.PlainConnectionSocketFactory;
      import org.apache.http.conn.ssl.DefaultHostnameVerifier;
      import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
      import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
      import org.apache.http.impl.client.CloseableHttpClient;
      import org.apache.http.impl.client.HttpClientBuilder;
      import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
      import org.apache.http.ssl.SSLContexts;
      
      public static CloseableHttpClient createApacheHttp4ClientWithClientCertAuth() {
          try {
              SSLContext sslContext = SSLContexts
                      .custom()
                      .loadTrustMaterial(null, new TrustSelfSignedStrategy())
                      .build();
      
              SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslContext,
                      new DefaultHostnameVerifier());
      
              Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
                      .register("https", sslConnectionFactory)
                      .register("http", new PlainConnectionSocketFactory())
                      .build();
      
              HttpClientBuilder builder = HttpClientBuilder.create();
              builder.setSSLSocketFactory(sslConnectionFactory);
              builder.setConnectionManager(new PoolingHttpClientConnectionManager(registry));
      
              return builder.build();
          } catch (Exception ex) {
      
              return null;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-02-02
        • 2022-06-23
        • 2014-08-23
        • 2023-03-03
        • 1970-01-01
        • 2012-01-10
        • 1970-01-01
        • 1970-01-01
        • 2018-10-20
        相关资源
        最近更新 更多