今天售后来咨询客户买了证书出现https请求400错误,让帮忙弄下。看到客户的代码好像没什么问题,自己就试了下,还真的出现异常了。记录下避免以后被坑。
场景:双向验证的SSL,请求需要带客户端证书。
有可能大家碰到的问题不一样,但是出现这个问题是一个客户反馈过来的。用的是apache的httpclient,注册证书请求服务端,然后报400错误,错误信息是unable to find valid certification path to requested target。
客户的代码大概是这样的:
static public void client() throws Exception {
// Trust own CA and all self-signed certs
File cert = new File("D:/test/cert.pfx");
KeyStore keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(new FileInputStream(cert), "cert_password".toCharArray());
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "cert_password".toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpGet httpget = new HttpGet("https://domains.com/");
System.out.println("Executing request " + httpget.getRequestLine());
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(entity);
}
其实跟httpclient给出的配置是差不多的,但是出现这个是本地没法信任服务端的原因。那就是说要在本地添加一个信任服务端的证书的列表。奇怪的是操作系统和jdk都会有一个信任列表,而客户正式环境的证书是肯定受信任的。为什么还会出现这样的情况呢?后来分析了证书,怀疑一个原因就是证书的层次超过了三层(暂时无法验证,只有一张证书而已)。
解决的方案是:
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "cert_password".toCharArray())
.loadTrustMaterial(new File("trust_ca.jks"),"trust_password".toCharArray(), new TrustSelfSignedStrategy()).build();
添加了一个trustmaterial,自己手动合成一个信任列表,带进去请求。
jks=密钥文件+颁发机构的证书,在一些ca机构官网都会有在线合成。
有可能每个人的错误原因不一样,这个只是其中之一。