java 中 https 实现整理

HTTPS协议是安全通信过程中常用的应用层协议,本质上说就是HTTP+SSL/TLS的融合版本。SSL/TLS是基于TCP的传输层协议,它的握手协议主要分为三个阶段:

1.协商算法: 

     客户端提供服务端自身支持的加密算法并发送一个随机数RNC,服务端提供客户端自身支持的加密算法并发送一个随机数RNS,双方商榷得到一个共同支持的加密算法

2.验证证书: 

    单向认证情况下,只需要客户端验证服务端的身份。客户端在接受到服务端的证书时,可以向CA(证书认证中心)发送验证请求,CA会返回证书可用性响应。当然 很多时候,我们在通信过程中使用的是自身签发的证书,客户端可以通过本地的信任库来验证证书。

3.生成秘钥

   客户端再生成一个随机数PMS,并通过服务端下发的公钥加密发送给服务端,服务端通过私钥解密得到PMS。此时客户端与服务端双方有三个随机数RNC,RNS,PMS。通过这三个随机数,得到本次通信的对称秘钥。


PS:

1.电子商务系统或者金融系统中一般会采用双向认证证书,即客户端认证服务端之外,服务端也需要认证客户端。比如网银的U盾,支付宝的数字证书,都是网银或者支付宝认证客户端的手段之一。

2.每次SSL/TLS握手生成的对称秘钥都会不同。


java 中实现SSL/TLS通信的代码如下:

服务端:

package net.flyingfat.Ssl;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class ServerSsl {
	
	public static void main(String[] args) throws Exception {
		sslSocketServer();
	}

	// 启动一个ssl server socket
	// 配置了证书, 所以不会抛出异常
	public static void sslSocketServer() throws Exception {

		// key store相关信息
		String keyName = "cmkey";
		char[] keyStorePwd = "123456".toCharArray();
		char[] keyPwd = "123456".toCharArray();
		KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());

		// 装载当前目录下的key store. 可用jdk中的keytool工具生成keystore
		InputStream in = null;
		keyStore.load(in = ServerSsl.class.getClassLoader().getResourceAsStream(
				keyName), keyPwd);
		in.close();

		// 初始化key manager factory
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
				.getDefaultAlgorithm());
		kmf.init(keyStore, keyPwd);

		// 初始化ssl context
		SSLContext context = SSLContext.getInstance("SSL");
		context.init(kmf.getKeyManagers(),
				new TrustManager[] { new MyX509TrustManager() },
				new SecureRandom());

		// 监听和接收客户端连接
		SSLServerSocketFactory factory = context.getServerSocketFactory();
		SSLServerSocket server = (SSLServerSocket) factory
				.createServerSocket(10002);
		System.out.println("ok");
		Socket client = server.accept();
		System.out.println(client.getRemoteSocketAddress());

		// 向客户端发送接收到的字节序列
		OutputStream output = client.getOutputStream();

		// 当一个普通 socket 连接上来, 这里会抛出异常
		// Exception in thread "main" javax.net.ssl.SSLException: Unrecognized
		// SSL message, plaintext connection?
		InputStream input = client.getInputStream();
		byte[] buf = new byte[1024];
		int len = input.read(buf);
		System.out.println("received: " + new String(buf, 0, len));
		output.write(buf, 0, len);
		output.flush();
		output.close();
		input.close();

		// 关闭socket连接
		client.close();
		server.close();
	}

	public static class MyX509TrustManager implements X509TrustManager {

		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType)
				throws CertificateException {
			System.out.println("click trust");
		}

		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType)
				throws CertificateException {
			System.out.println("server trust");
		}

		@Override
		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}

	}
	
}


客户端:

package net.flyingfat.Ssl;

import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

public class ClientSsl {

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		 sslSocket2();
	}

	// 这种情况是使用空证书??
	public static void sslSocket2() throws Exception {
		SSLContext context = SSLContext.getInstance("SSL");
		context.init(null,
				new TrustManager[] { new ServerSsl.MyX509TrustManager() },
				new SecureRandom());
		SSLSocketFactory factory = context.getSocketFactory();
		SSLSocket s = (SSLSocket) factory.createSocket("localhost", 10002);
		System.out.println("ok");

		OutputStream output = s.getOutputStream();
		InputStream input = s.getInputStream();

		output.write("alert".getBytes());
		System.out.println("sent: alert");
		output.flush();

		byte[] buf = new byte[1024];
		int len = input.read(buf);
		System.out.println("received:" + new String(buf, 0, len));
	}
}

模拟浏览器访问百度页面:

package net.flyingfat.Ssl;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.SecureRandom;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

public class HttpsConnection {

	public static void main(String[] args) throws Exception {
		SSLContext sslContext=SSLContext.getInstance("TLS");
		sslContext.init(null, null, new SecureRandom());
		SSLSocketFactory sslSocketFactory=sslContext.getSocketFactory();
		URL url=new URL("https://www.baidu.com/");
		HttpsURLConnection conn=(HttpsURLConnection) url.openConnection();
		conn.setSSLSocketFactory(sslSocketFactory);
		conn.connect();
		InputStream input=conn.getInputStream();
		BufferedReader reader=new BufferedReader(new InputStreamReader(input));
		String line="";
		while((line=reader.readLine())!=null){
			System.out.println(line);
		}
		reader.close();
		input.close();
		conn.disconnect();
	}

}

SSLContext 中有两个容易混淆的接口: KeyManager(秘钥库)与TrustManager(信任库)

密钥库和信任库都是密钥库。惟一的区别在于它们的使用方式:
密钥库通常包含私钥和公钥证书,这些信息在客户端尝试连接到服务器时提供给客户端。
信任库通常包含受信任的根证书颁发机构 (CA) 证书。这些证书用于在建立出站连接时确定目标服务器是否值得信赖。

关于https的性能

https带来安全性的同时,也带来了性能上的开销,主要体现在两个方面:

1.二次握手带来的网络延时,因为SSL/TLS是基于TCP的,所以首先进行一次TCP的握手,接着再进行SSL/TLS本身的握手

2.握手阶段使用到公钥与私钥的加解密带来的服务器CPU性能消耗

虽然有性能上的开销,但https也是大势所趋,http2.0默认就是基于https的。


参考文章:

http://410063005.iteye.com/blog/1751243

http://network.51cto.com/art/201505/474980.htm
参考书籍:

java 加密与解密技术

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值