Apache HTTPClient在相互身份验证期间不发送客户端证书

时间:2016-06-10 21:41:49

标签: javascript java ssl ssl-certificate apache-httpclient-4.x

在对需要相互身份验证的API进行RESTful post调用时,我收到了handshake_failed异常。我已经与另一方的工程师进行了验证,他正在观察堆栈跟踪服务器端证书是否被我的客户端交换和接受而没有问题,但是当服务器请求客户端证书时,我方并不提供那和握手终止了。

问题是我不知道如何验证SSLSocketFactory是否无法识别正确的证书或者只是首先找不到它。

这是我的代码,它利用了Apache Httpclient库。

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)

1 个答案:

答案 0 :(得分:0)

决定在使用服务器进行身份验证时使用的私钥是由JRE(确切地说是JSSE提供程序)而不是HttpClient进行的。但是,可以使用自定义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();