在上篇文章中写到了如何实现服务端程序,主要是netty实现的。还有如何生成证书和密钥库。
这篇文章主要讲客户端如何实现:
httpclient实现连接池并进行ssl通信
HttpClientUtils2.java
package https;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.*;
/**
* ssl通信的client
*/
public class HttpClientUtils2 {
private static PoolingHttpClientConnectionManager secureConnectionManager;
private static HttpClientBuilder secureHttpBulder = null;
private static RequestConfig requestConfig = null;
private static int MAXCONNECTION = 10;
private static int DEFAULTMAXCONNECTION = 5;
private static String CLIENT_KEY_STORE = "E:\\https\\client.keystore";
private static String CLIENT_TRUST_KEY_STORE = "E:\\https\\client.truststore";
private static String CLIENT_KEY_STORE_PASSWORD = "123456";
private static String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";
private static String CLIENT_KEY_PASS = "123456";
/**
* 进行安全通信的主机和端口
*/
private static String HOST = "127.0.0.1";
private static int PORT = 8888;
static {
//设置http的状态参数
requestConfig = RequestConfig.custom()
.setSocketTimeout(5000)
.setConnectTimeout(5000)
.setConnectionRequestTimeout(5000)
.build();
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream trustStoreInput = new FileInputStream(new File(CLIENT_TRUST_KEY_STORE));
trustStore.load(trustStoreInput, CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream clientKeyStoreInput = new FileInputStream(new File(CLIENT_KEY_STORE));
clientKeyStore.load(clientKeyStoreInput, CLIENT_KEY_STORE_PASSWORD.toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.loadKeyMaterial(clientKeyStore, CLIENT_KEY_PASS.toCharArray())
.setSecureRandom(new SecureRandom())
.useSSL()
.build();
ConnectionSocketFactory plainSocketFactory = new PlainConnectionSocketFactory();
SSLConnectionSocketFactory sslSocketFactoy = new SSLConnectionSocketFactory(
sslContext, new String[]{"SSLv3"}, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainSocketFactory)
.register("https", sslSocketFactoy)
.build();
secureConnectionManager = new PoolingHttpClientConnectionManager(r);
HttpHost target = new HttpHost(HOST, PORT, "https");
secureConnectionManager.setMaxTotal(MAXCONNECTION);
//设置每个Route的连接最大数
secureConnectionManager.setDefaultMaxPerRoute(DEFAULTMAXCONNECTION);
//设置指定域的连接最大数
secureConnectionManager.setMaxPerRoute(new HttpRoute(target), 20);
secureHttpBulder = HttpClients.custom().setConnectionManager(secureConnectionManager);
} catch (Exception e) {
throw new Error("Failed to initialize the server-side SSLContext", e);
}
}
public static CloseableHttpClient getSecureConnection() throws Exception {
return secureHttpBulder.build();
}
public static HttpUriRequest getRequestMethod(Map<String, String> map, String url, String method) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> e : entrySet) {
String name = e.getKey();
String value = e.getValue();
NameValuePair pair = new BasicNameValuePair(name, value);
params.add(pair);
}
HttpUriRequest reqMethod = null;
if ("post".equals(method)) {
reqMethod = RequestBuilder.post().setUri(url)
.addParameters(params.toArray(new BasicNameValuePair[params.size()]))
.setConfig(requestConfig).build();
} else if ("get".equals(method)) {
reqMethod = RequestBuilder.get().setUri(url)
.addParameters(params.toArray(new BasicNameValuePair[params.size()]))
.setConfig(requestConfig).build();
}
return reqMethod;
}
public static void main(String args[]) throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("account", "sdsdsd");
map.put("password", "98765");
HttpClient client = getSecureConnection(); //使用ssl通信
HttpUriRequest post = getRequestMethod(map, "https://127.0.0.1:8888/", "post");
HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
String message = EntityUtils.toString(entity, "utf-8");
System.out.println(message);
} else {
System.out.println("请求失败");
}
}
}
上面的httpclient实现了连接池,并可以进行ssl双向认证的通信过程。其实也可以进行不加密的http通信。
运行结果:
服务器端
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Your session is protected by TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA cipher suite.
VERSION: HTTP/1.1
REQUEST_URI: /
HEADER: Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1
HEADER: Host=127.0.0.1:8888
HEADER: Connection=Keep-Alive
HEADER: User-Agent=Apache-HttpClient/4.3.3 (java 1.5)
HEADER: Accept-Encoding=gzip,deflate
HEADER: Content-Length=29
七月 10, 2014 11:55:07 上午 https.HttpDemoServerHandler exceptionCaught
警告:
java.io.IOException: 远程主机强迫关闭了一个现有的连接。
有异常,这不是主要的,是因为没有关闭连接。
客户端
WELCOME TO THE WILD WILD WEB SERVER
===================================
VERSION: HTTP/1.1
REQUEST_URI: /
HEADER: Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1
HEADER: Host=127.0.0.1:8888
HEADER: Connection=Keep-Alive
HEADER: User-Agent=Apache-HttpClient/4.3.3 (java 1.5)
HEADER: Accept-Encoding=gzip,deflate
HEADER: Content-Length=29
Is Chunked: false
IsMultipart: false
BODY Attribute: Attribute:Mixed: password=98765
BODY Attribute: Attribute:Mixed: account=sdsdsd
END OF POST CONTENT
Process finished with exit code 0
httpclien客户端二
httpclient还有一种方式可以进行ssl通信。下面看这段代码:
ClientCustomSSL.java
package https;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
/**
* This example demonstrates how to create secure connections with a custom SSL
* context.
*/
public class ClientCustomSSL {
private static String CLIENT_KEY_STORE = "E:\\https\\client.keystore";
private static String CLIENT_TRUST_KEY_STORE = "E:\\https\\client.truststore";
private static String CLIENT_KEY_STORE_PASSWORD = "123456";
private static String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";
private static String CLIENT_KEY_PASS = "123456";
public final static void main(String[] args) throws Exception {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File(CLIENT_TRUST_KEY_STORE));
try {
trustStore.load(instream, CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());
} finally {
instream.close();
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream keyStoreInput = new FileInputStream(new File(CLIENT_KEY_STORE));
try {
keyStore.load(keyStoreInput, CLIENT_KEY_STORE_PASSWORD.toCharArray());
} finally {
keyStoreInput.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore, CLIENT_KEY_PASS.toCharArray())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[]{"SSLv3"},
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpPost httpPost = new HttpPost("https://127.0.0.1:8888/");
System.out.println("executing request" + httpPost.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
}
上面这段代码没有用到连接池,比较简单的实现了双向认证的ssl通信过程。
运行结果:
----------------------------------------
HTTP/1.1 200 OK
Response content length: -1
Process finished with exit code 0
==========END==========