前言:

1、 某一次调上游接口过程中,测试环境用http,但生产环境需要用https,故将http改造成https,记录在此,以便之后参考;

2、https比http更安全,http明文传输,https密文传输;

3、https三次服务器握手,先验证服务器的可信性,然后进行数据加密传输;

4、该实例继承httpclient实现https通讯;

5、TLS是SSL 3.0的升级版;

https相关文件说明:

1、xxx.keystore // 服务器证书库,配置在web服务端,tomcat的server.xml中;

2、xxxTrust.keystore // 客户端信任证书库,由服务端证书生产;

3、*.cer // 客户端证书,由客户端证书库导出;

4、*.cer // 服务端证书,由服务端证书库导出;

5、客户端证书库、服务器证书库都设置,表示双向验证,如果只传其中一个,表示单向数据传输验证;

6、https与http的代码不同之处,在于sslContext的获取;

相关jar包:

<dependency>
     <groupId>commons-httpclient</groupId>
     <artifactId>commons-httpclient</artifactId>
     <version>3.1</version>
 </dependency>

代码说明:

本实例只实现了https双向验证中的客户端验证,具体服务器端的验证和配置(主要是web服务器配置)这里不涉及

代码如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class HttpsUtil {
	/**
	 * 默认字符集
	 */
	private static final String defaultCharset = "UTF-8";

	/**
	 * 连接超时时间
	 */
	private final static int connectionTimeout = 6000;

	/**
	 * socket超时时间
	 */
	private final static int soTimeout = 10000;

	// 主秘钥绝对路径
	private static final String keystore = "xxx.keystore";
	// 主密钥密码
	private static final String keystorePassword = "xxxxxx";
	// 信任密钥绝对路径
	private static final String truststore = "xxxTrust.keystore";
	// 信任密钥密码
	private static final String truststorePassword = "xxxxxx";

	private static SSLContext sslContext;

	/**
	 * 从给定的路径中加载此 KeyStore
	 * 
	 * @param filePath
	 *            keystore 文件绝对路径
	 * @param password
	 *            keystore 访问密钥
	 * @return keystore 对象
	 */
	private static KeyStore createKeyStore(final String filePath,
			final String password) throws KeyStoreException,
			NoSuchAlgorithmException, CertificateException, IOException {
		if (filePath == null) {
			throw new IllegalArgumentException("证书路径为空!");
		}
		KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
		InputStream is = null;
		try {
			// is = new FileInputStream(new File(filePath));
			is = HttpsUtil.class.getResourceAsStream(filePath);
			keystore.load(is, password != null ? password.toCharArray() : null);
		} finally {
			if (is != null) {
				is.close();
				is = null;
			}
		}
		return keystore;
	}

	/**
	 * Post方式提交,URL中不包含提交参数, 格式:http://www.g.cn
	 * 
	 * @param url
	 *            提交地址
	 * @param content
	 *            提交内容
	 * @return 响应消息
	 */
	public static String post(String url, String content) {
		return post(url, content, null);
	}

	/**
	 * Post方式提交
	 * 
	 * @param url
	 *            请求地址
	 * @param content
	 *            请求内容,可为空
	 * @param charset
	 *            字符集
	 * @return
	 */
	public static String post(String url, String content, String charset) {
		if (StringUtils.isBlank(url)) {
			return null;
		}
		if (charset == null) {
			charset = defaultCharset;
		}

		CloseableHttpClient httpclient = getHttpClient();

		ByteArrayEntity entity = null;
		HttpPost httpPost = null;
		String result = null;
		try {
			httpPost = new HttpPost(url);
			if (!StringUtils.isBlank(content)) {
				entity = new ByteArrayEntity(content.getBytes(charset));
				httpPost.setEntity(entity);
			}
			CloseableHttpResponse httpResponse = httpclient.execute(httpPost);
			StatusLine status = httpResponse.getStatusLine();
			HttpEntity httpEntity = httpResponse.getEntity();
			result = EntityUtils.toString(httpEntity);
			if (status.getStatusCode() == HttpStatus.SC_OK) {
				System.out.println("服务器返回【正常】:" + result);
			} else {
				System.out.println("服务器返回【异常】:" + result);
				return null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (httpclient != null) {
				try {
					httpclient.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	/**
	 * Post方式提交,URL中不包含提交参数, 格式:http://www.g.cn
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            提交参数集, 键/值对
	 * @return 响应消息
	 */
	public static String post(String url, Map<String, String> params) {
		return post(url, params, null);
	}

	/**
	 * Post方式提交,URL中不包含提交参数, 格式:http://www.g.cn
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            提交参数集, 键/值对
	 * @param charset
	 *            参数提交编码集
	 * @return 响应消息
	 */
	public static String post(String url, Map<String, String> params,
			String charset) {
		if (StringUtils.isBlank(url)) {
			return null;
		}
		if (charset == null) {
			charset = defaultCharset;
		}

		CloseableHttpClient httpclient = getHttpClient();

		String result = null;

		UrlEncodedFormEntity formEntity = null;

		try {
			if (StringUtils.isBlank(charset)) {
				formEntity = new UrlEncodedFormEntity(getParamsList(params));
			} else {
				formEntity = new UrlEncodedFormEntity(getParamsList(params),
						charset);
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}

		HttpPost httpPost = new HttpPost(url);
		httpPost.setEntity(formEntity);

		try {
			CloseableHttpResponse httpResponse = httpclient.execute(httpPost);
			StatusLine status = httpResponse.getStatusLine();
			HttpEntity httpEntity = httpResponse.getEntity();
			result = EntityUtils.toString(httpEntity);
			if (status.getStatusCode() == HttpStatus.SC_OK) {
				System.out.println("服务器返回【正常】:" + result);
			} else {
				System.out.println("服务器返回【异常】:" + result);
				return null;
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (httpclient != null) {
				try {
					httpclient.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	/**
	 * 将传入的键/值对参数转换为NameValuePair参数集
	 * 
	 * @param paramsMap
	 *            参数集, 键/值对
	 * @return NameValuePair参数集
	 */
	private static List<NameValuePair> getParamsList(
			Map<String, String> paramsMap) {
		if (paramsMap == null || paramsMap.size() == 0) {
			return null;
		}
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		for (Map.Entry<String, String> map : paramsMap.entrySet()) {
			params.add(new BasicNameValuePair(map.getKey(), map.getValue()));
		}
		return params;
	}

	/**
	 * form请求,上传图片
	 * 
	 * @param datas
	 *            普通数据map
	 * @param files
	 *            图片map
	 * @param uploadUrl
	 *            上传图片的地址
	 */
	public static String postForm(Map<String, String> datas,
			Map<String, String> files, String uploadUrl) {

		CloseableHttpClient httpclient = getHttpClient();

		HttpPost httpPost = new HttpPost(uploadUrl);
		String result = "";

		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(soTimeout)
				.setConnectTimeout(connectionTimeout).build();
		httpPost.setConfig(requestConfig);

		MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder
				.create();
		multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
		multipartEntityBuilder.setCharset(Charset.forName(defaultCharset));

		Set<Map.Entry<String, String>> setData = datas.entrySet();
		Iterator<Map.Entry<String, String>> iterator = setData.iterator();

		// 文本
		while (iterator.hasNext()) {
			Map.Entry<String, String> entry = (Map.Entry<String, String>) iterator
					.next();
			multipartEntityBuilder.addTextBody(
					entry.getKey(),
					entry.getValue(),
					ContentType.create("text/plain",
							Charset.forName(defaultCharset)));
		}
		// 发送的文件
		if (files != null) {
			iterator = files.entrySet().iterator();
			while (iterator.hasNext()) {
				Map.Entry<String, String> entry = (Map.Entry<String, String>) iterator
						.next();
				String path = entry.getValue();
				if ("".equals(path) || path == null)
					continue;
				File file = new File(entry.getValue());
				// addBinaryBody还支持字节流、文件流
				multipartEntityBuilder.addBinaryBody(entry.getKey(), file);
			}
		}

		CloseableHttpResponse response = null;
		HttpEntity httpEntity = multipartEntityBuilder.build();
		httpPost.setEntity(httpEntity);
		try {
			response = httpclient.execute(httpPost);
			StatusLine status = response.getStatusLine();
			HttpEntity entity = response.getEntity();
			result = EntityUtils.toString(entity);
			if (status.getStatusCode() == HttpStatus.SC_OK) {
				System.out.println("服务器返回【正常】:" + result);
			} else {
				System.out.println("服务器返回【异常】:" + result);
				return null;
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (httpclient != null) {
				try {
					httpclient.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	/**
	 * 获取httpclient
	 * 
	 * @return
	 */
	private static CloseableHttpClient getHttpClient() {

		if (sslContext == null) {
			try {
				// 客户端秘钥管理器
				KeyManagerFactory kmf = KeyManagerFactory
						.getInstance("SunX509");
				kmf.init(createKeyStore(keystore, keystorePassword),
						keystorePassword.toCharArray()); // init()验证数据
				KeyManager[] keyManagers = kmf.getKeyManagers();

				// 服务端秘钥管理器
				TrustManagerFactory trustManagerFactory = TrustManagerFactory
						.getInstance("SunX509");
				trustManagerFactory.init(createKeyStore(truststore,
						truststorePassword));
				TrustManager[] trustManagers = trustManagerFactory
						.getTrustManagers();

				sslContext = SSLContext.getInstance("TLS");
				sslContext.init(keyManagers, trustManagers, new SecureRandom());
			} catch (Exception e) {
				System.out.println("获取ssl证书异常!");
				e.printStackTrace();
				return null;
			}
		}

		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				sslContext,
				SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

		return HttpClients.custom().setSSLSocketFactory(sslsf).build();
	}

}