首页 > 解决方案 > 无法使用 Apache HttpClient/OkHttp 在 POST 请求中发送 .p12 文件

问题描述

我正在尝试使用 java 执行相互身份验证。我已经尝试过curl,它正在使用以下命令:

curl --cert-type P12 --cert my-p12-file.p12:password https://server-url --header "Content-Type:application/json" --data '{"key":"value"}'

或者

curl --cert client.crt --key my-key.pem --cacert RootCA.cer https://server-url --header "Content-Type:application/json" --data '{"key":"value"}'

这两个命令都工作正常并给出正确的输出。我在 Postman 中尝试过相同的功能,它也在那里工作。当我以编程方式尝试它时,它在 nodejs 中工作,但不在 Java 中。我关注了许多博客/教程/SO答案,但没有任何效果。这是Java代码:

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;


public class DemoSO {
    private static CloseableHttpClient httpClient;
    public static void main(String[] args) throws Exception {
        System.setProperty("javax.net.debug", "ssl");
        String trustStore = "root-certificate-truststore.jks";
        String keyStore =  "keystore-v0.jks";
        String extraPwd = "password";

        SSLContext sslContext = SSLContexts.custom().
            loadKeyMaterial(new File(keyStore),extraPwd.toCharArray(),extraPwd.toCharArray())
            .loadTrustMaterial(new File(trustStore))
            .build();

        SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());

        httpClient = HttpClients.custom().setSSLSocketFactory(sslConSocFactory)
            .setSSLContext(sslContext)
            .build();

        HttpPost httpPost = new HttpPost("https://server-url");
        String requestBody = "{\"key\":\"value\"}";
        StringEntity entity = new StringEntity(requestBody);
        Header header = new BasicHeader("Content-Type", "application/json");

        entity.setContentType(header);
        httpPost.setHeader(header);
        httpPost.setEntity(entity);

        HttpResponse response = httpClient.execute(httpPost);
        for (Header allHeader : response.getAllHeaders()) {
            System.out.println(allHeader.toString());
        }
        System.out.println(response.getAllHeaders().toString());
        System.out.println(response.getStatusLine());
        System.out.println(EntityUtils.toString(response.getEntity()));
    }
}

上面的代码发送一个 POST 请求到https://server-url. trustStore 是使用以下命令从根证书生成的:

openssl x509 -inform der -in RootCA.cer -outform pem -out root-certificate.pem (转换.cer.pem

keytool -import -file root-certificate.pem -alias root-certificate -keystore root-certificate-truststore.jks

keyStore 是使用 .p12 文件生成的,该文件通过 openssl、postman 和 node 程序运行良好。

keytool -importkeystore -srckeystore my-p12-file.p12 -destkeystore keystore-v0.jks -srcstoretype PKCS12 -deststoretype jks -deststorepass password

任何在 java 中实现的帮助或建议将不胜感激。

谢谢。

[编辑1]

忘记添加回复了。它给出了 401 Unauthorized。这与之前在 cURL 和 postman 中给出的错误相同,但是当我传递 .p12 文件时,它开始正常工作并在响应代码中给出 200。

<ErrorCode>401-Unauthorized</ErrorCode>
<ErrorMessageCode>3103</ErrorMessageCode>
<ErrorMessage>Not enough authentication factor, 1 is required!</ErrorMessage> 

调试模式显示以下错误:

upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
*** ServerHelloDone
[read] MD5 and SHA1 hashes:  len = 4
0000: 0E 00 00 00                                        ....
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
***
update handshake state: certificate[11]
upcoming handshake states: client_key_exc

标签: javasslkeystoreclient-certificatestruststore

解决方案


推荐阅读