Apache HTTPClient 在相互身份验证期间不发送客户端证书 [英] Apache HTTPClient sends no client cert during mutual authentication

查看:38
本文介绍了Apache HTTPClient 在相互身份验证期间不发送客户端证书的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在对需要相互身份验证的 API 进行 RESTful 后期调用时,我收到了握手失败异常.我已经与另一边的工程师进行了验证,他正在查看堆栈跟踪,服务器端证书已被我的客户端交换和接受,没有问题,但是当服务器请求客户端证书时,我的身边没有提供,并且握手结束.

I'm getting a handshake_failed exception when making a RESTful post call to an API that requires mutual authentication. I have verified with an engineer on the other side who is watching a stack trace that the server-side certs are exchanged and accepted by my client without issue, but when the server requests the client-side cert my side doesn't provide that and the handshake terminates.

问题是我不知道如何验证 SSLSocketFactory 是无法识别正确的证书,还是一开始就没有寻找它.

The thing is I have no idea how to verify whether SSLSocketFactory is unable to identify the right certificate or just isn't looking for it in the first place.

这是我的代码,它是利用 Apache Httpclient 库的 js.

Here is my code, which is js leveraging the Apache Httpclient library.

importPackage(Packages.org.apache.http.client);
importPackage(Packages.org.apache.http.client.methods);
importPackage(Packages.org.apache.http.impl.client);
importPackage(Packages.org.apache.http.message);
importPackage(Packages.org.apache.http.client.entity);
importPackage(Packages.org.apache.http.util);
importPackage(Packages.org.apache.commons.httpclient);
importPackage(Packages.org.apache.http.params);
importPackage(Packages.org.apache.http.ssl);
importPackage(Packages.org.apache.http.ssl.SSLSocketFactory);
importPackage(Packages.java.security.Keystore);
importPackage(Packages.org.apache.http.conn.ssl);
importPackage(Packages.javax.net.ssl);
importPackage(Packages.java.io);
importPackage(Packages.org.apache.http.impl.conn);
importPackage(Packages.org.apache.http.conn.scheme);

//Benchmark 1
var healthMessageStatus = "0";
var xtn_startTime = DateUtil.getCurrentDate('yyyyMMddHHmmss');
channelMap.put('xtn_startTime',xtn_startTime);

var url = 'https://smr.prodposturl.net/AuthenticatingXmlServer.aspx'

//HTTP Connection Template
var httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 3600000);
HttpConnectionParams.setSoTimeout(httpParams, 3600000);

//APPROACH 3
var KEY_STORE_PATH = "C:\\PHIA\\Certs\\mykeystore";
var KEY_STORE_PASSWORD = "changeit";

var TRUST_STORE_PATH = "C:\\PHIA\\Certs\\cacerts";
var TRUST_STORE_PASSWORD = "changeit";

var keystore = new KeyStore.getInstance(KeyStore.getDefaultType());
channelMap.put('keystore',keystore);

var keystoreInput = new FileInputStream(KEY_STORE_PATH);

keystore.load(keystoreInput, KEY_STORE_PASSWORD.split(''));
var keystoreline = "Keystore has " + keystore.size() + " keys";

// load the truststore
var truststore = new KeyStore.getInstance(KeyStore.getDefaultType());
var truststoreInput = new FileInputStream(TRUST_STORE_PATH);
truststore.load(truststoreInput, TRUST_STORE_PASSWORD.split(''));
var truststoreline = "Truststore has " + truststore.size() + " keys";

channelMap.put('keystoreline', keystoreline);
channelMap.put('truststoreline', truststoreline);


var schemeRegistry = new SchemeRegistry();
var lSchemeSocketFactory = new org.apache.http.conn.ssl.SSLSocketFactory("TLSv1", keystore, KEY_STORE_PASSWORD, truststore, null, org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);//, keystore);

var lSchemeSocketFactoryString = String(lSchemeSocketFactory);
channelMap.put('lSchemeSocketFactoryString',lSchemeSocketFactoryString);

schemeRegistry.register(new Scheme("https", lSchemeSocketFactory, 443));

var httpclient = new DefaultHttpClient(new org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager(httpParams, schemeRegistry), httpParams);

httpclient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(10, false));

//Benchmark 2
var xtn_postTime = DateUtil.getCurrentDate('yyyyMMddHHmmss');
channelMap.put('xtn_postTime',xtn_startTime);

var httpPost = new HttpPost(url);

httpPost.addHeader("Authorization", "Basic RiF2ZSR0YXI2UzpQUk4icV9APE0tdigj")
httpPost.addHeader("Content-Type", "text/xml")

//passes the results to a string builder/entity
var se = new org.apache.http.entity.StringEntity(connectorMessage.getEncodedData().toString());
channelMap.put('se', se);

//sets the post request as the resulting string
httpPost.setEntity(se);
var httpContents = String(httpPost);
channelMap.put('httpContents',httpContents);
var keystoreContents = String(keystore);
channelMap.put('keystoreContents',keystoreContents);

var response = httpclient.execute(httpPost);

try {
var statusCode = response.getStatusLine().getStatusCode();
var entity = response.getEntity();
var responseString = EntityUtils.toString(entity, "UTF-8");

channelMap.put('statusCode', statusCode);
channelMap.put('responseString', responseString);

} finally {
response.close();
}

//Benchmark 3
var xtn_finishTime = DateUtil.getCurrentDate('yyyyMMddHHmmss');
channelMap.put('xtn_finishTime',xtn_finishTime);

//Response Calculations
//var xtn_totalProcessing = (xtn_finishTimeMs - xtn_startTimeMs)/1000;
var xtn_totalProcessing = xtn_finishTime - xtn_startTime;
channelMap.put('xtn_totalProcessing',xtn_totalProcessing);

//var xtn_totalPostTime = (xtn_finishTimeMs - xtn_postTimeMs)/1000;
var xtn_totalPostTime = xtn_finishTime - xtn_postTime;
channelMap.put('xtn_totalPostTime', xtn_totalPostTime);

var finalStatusCheck = responseString.indexOf("<Code>010</Code>")
var pendingStatusCheck = responseString.indexOf("<Code>000</Code>")
var followUpMessage = false;
channelMap.put('finalStatusCheck',finalStatusCheck);
if (finalStatusCheck == -1 & pendingStatusCheck == -1) {
healthMessageStatus = "2";
channelMap.put('healthMessageStatus', healthMessageStatus);
throw('Bad HTTP Response Code: ' + statusCode + ' For GUID File ID: ' + $('GUID') + '. Response String: ' + responseString);
}
else if (pendingStatusCheck > -1) {
channelMap.put('healthMessageStatus', healthMessageStatus); 
channelMap.put('followUpMessage', followUpMessage)
followUpMessage = true;
healthMessageStatus = "2";
throw('Bad HTTP Response Code: ' + statusCode + ' For GUID File ID: ' + $('GUID') + '. Response String: ' + responseString);
}
else
{
healthMessageStatus = "1";
channelMap.put('healthMessageStatus', healthMessageStatus); 
channelMap.put('followUpMessage', followUpMessage)
}

这是我得到的例外:

JavaScript Writer error
ERROR MESSAGE: Error evaluating JavaScript Writer
com.mirth.connect.server.MirthJavascriptTransformerException: 
CHANNEL:    0 - Inbound Clinical PDF PROD
CONNECTOR:  SURESCRIPTS PROD API
SCRIPT SOURCE:  JavaScript Writer
SOURCE CODE:    
541:    var httpContents = String(httpPost);
542:    channelMap.put('httpContents',httpContents);
543:    var keystoreContents = String(keystore);
544:    channelMap.put('keystoreContents',keystoreContents);
545: 
546: var response = httpclient.execute(httpPost);
547: 
548: try {
549:     var statusCode = response.getStatusLine().getStatusCode();
550:     var entity = response.getEntity();
LINE NUMBER:    546
DETAILS:    Wrapped javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at 3f792644-5e4b-4bbf-af2d-3ac8ef00b5db:546 (doScript)
    at 3f792644-5e4b-4bbf-af2d-3ac8ef00b5db:598
    at com.mirth.connect.connectors.js.JavaScriptDispatcher$JavaScriptDispatcherTask.call(JavaScriptDispatcher.java:184)
    at com.mirth.connect.connectors.js.JavaScriptDispatcher$JavaScriptDispatcherTask.call(JavaScriptDispatcher.java:122)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.recvAlert(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:533)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:401)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:470)
    at org.apache.http.conn.scheme.SchemeSocketFactoryAdaptor.connectSocket(SchemeSocketFactoryAdaptor.java:65)  
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:144)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:131)
    at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:611)

推荐答案

在与服务器进行身份验证时使用哪个私钥是由 JRE(确切地说是 JSSE 提供程序)而不是 HttpClient 做出的决定.但是,可以通过使用自定义 PrivateKeyStrategy

The decision what private key to use when authenticating with the server is made by the JRE (JSSE provider to be exact), not HttpClient. However one can override the default behavior by employing a custom PrivateKeyStrategy

SSLContext sslContext = SSLContexts.custom()
        .loadKeyMaterial(myKeyStore, "mypassword".toCharArray(), new PrivateKeyStrategy() {
            @Override
            public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
                // Pick a cert alias based on socket endpoint, SSL session or private key details
                return "vip";
            }
        })
        .build();

CloseableHttpClient client = HttpClients.custom()
        .setSslcontext(sslContext)
        .build();

这篇关于Apache HTTPClient 在相互身份验证期间不发送客户端证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆