httpclient ssl support
手头上一些工作,需要经常访问公司内网的数据,为了减少重复的劳动力,就考虑程序帮忙爬取信息数据。apache下httpclient是一个很好的工具,不过公司内网是使用HTTPS协议的,于是乎,就需要考虑如何让httpclient支持https。
支持ssl的关键,在于如何创建一个SSLSocket,幸好,httpclient提供了支持。
请看:
org.apache.commons.httpclient.protocol.ProtocolSocketFactory
javadocs:A factory for creating Sockets.
实现一个简单的EasySSLProtocolSocketFactory,详见代码:
public
class
EasySSLProtocolSocketFactory
implements
ProtocolSocketFactory {
private SSLContext sslcontext = null ;
private String ksfile;
private String tksfile;
private String kspwd;
private String tkspwd;
public EasySSLProtocolSocketFactory(String ks, String kspwd, String tks, String tkspwd){
this .ksfile = ks;
this .kspwd = kspwd;
this .tksfile = tks;
this .tkspwd = tkspwd;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
public Socket createSocket( final String host, final int port, final InetAddress localAddress, final int localPort,
final HttpConnectionParams params) throws IOException, UnknownHostException,
ConnectTimeoutException {
if (params == null ) {
throw new IllegalArgumentException( " Parameters may not be null " );
}
int timeout = params.getConnectionTimeout();
SocketFactory socketfactory = getSSLContext().getSocketFactory();
if (timeout == 0 ) {
return socketfactory.createSocket(host, port, localAddress, localPort);
} else {
Socket socket = socketfactory.createSocket();
SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
SocketAddress remoteaddr = new InetSocketAddress(host, port);
socket.bind(localaddr);
socket.connect(remoteaddr, timeout);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,
UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
private SSLContext createEasySSLContext() {
try {
SSLContext context = SSLContext.getInstance( " SSL " );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( " SunX509 " );
TrustManagerFactory tmf = TrustManagerFactory.getInstance( " SunX509 " );
KeyStore ks = KeyStore.getInstance( " JKS " );
KeyStore tks = KeyStore.getInstance( " JKS " );
ks.load( new FileInputStream(ksfile), kspwd.toCharArray());
tks.load( new FileInputStream(tksfile), tkspwd.toCharArray());
kmf.init(ks, kspwd.toCharArray());
tmf.init(tks);
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null );
return context;
} catch (Exception e) {
throw new HttpClientError(e.toString());
}
}
private SSLContext getSSLContext() {
if ( this .sslcontext == null ) {
this .sslcontext = createEasySSLContext();
}
return this .sslcontext;
}
}
private SSLContext sslcontext = null ;
private String ksfile;
private String tksfile;
private String kspwd;
private String tkspwd;
public EasySSLProtocolSocketFactory(String ks, String kspwd, String tks, String tkspwd){
this .ksfile = ks;
this .kspwd = kspwd;
this .tksfile = tks;
this .tkspwd = tkspwd;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
public Socket createSocket( final String host, final int port, final InetAddress localAddress, final int localPort,
final HttpConnectionParams params) throws IOException, UnknownHostException,
ConnectTimeoutException {
if (params == null ) {
throw new IllegalArgumentException( " Parameters may not be null " );
}
int timeout = params.getConnectionTimeout();
SocketFactory socketfactory = getSSLContext().getSocketFactory();
if (timeout == 0 ) {
return socketfactory.createSocket(host, port, localAddress, localPort);
} else {
Socket socket = socketfactory.createSocket();
SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
SocketAddress remoteaddr = new InetSocketAddress(host, port);
socket.bind(localaddr);
socket.connect(remoteaddr, timeout);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,
UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
private SSLContext createEasySSLContext() {
try {
SSLContext context = SSLContext.getInstance( " SSL " );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( " SunX509 " );
TrustManagerFactory tmf = TrustManagerFactory.getInstance( " SunX509 " );
KeyStore ks = KeyStore.getInstance( " JKS " );
KeyStore tks = KeyStore.getInstance( " JKS " );
ks.load( new FileInputStream(ksfile), kspwd.toCharArray());
tks.load( new FileInputStream(tksfile), tkspwd.toCharArray());
kmf.init(ks, kspwd.toCharArray());
tmf.init(tks);
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null );
return context;
} catch (Exception e) {
throw new HttpClientError(e.toString());
}
}
private SSLContext getSSLContext() {
if ( this .sslcontext == null ) {
this .sslcontext = createEasySSLContext();
}
return this .sslcontext;
}
}
备注:
至于ssl相关的知识,和如何创建java key store,可见:
SSL双向认证java实现
如何使用,也简单:
Protocol.registerProtocol("https", new Protocol("https", new EasySSLProtocolSocketFactory(KS_FILE, KS_PWD, TKS_FILE, TKS_PWD), 443))
官方资料:
http://hc.apache.org/httpclient-3.x/sslguide.html
演示代码:
httpclient-ssl.zip