HTTPS学习(二):原理与实践
div.example { background-color: rgba(229, 236, 243, 1); color: rgba(0, 0, 0, 1); padding: 0.5em; margin: 1em 2em 1em 1em }
div.warning { border: 1px solid rgba(255, 0, 0, 1) }
本文是《大型分布式网站架构设计与实践》 3.5节HTTPS协议的学习笔记。
HTTPS和SSL
HTTPS的全称是Hypertext Transfer Protocol over Secure Socket Layer,即 SSL之上的HTTP。 SSL及其继任者TLS是应用层(HTTP)和传输层(TCP)之间的安全协议层。它可以实现通信双方的认证、通信内容的加密传输。下图是SSL协议的示意图:

上面的握手过程看起来非常复杂,其实这个过程中主要就做了两件事:
1. 证书的认证
2. 通信双方协议产生一个通信秘钥。
关于第二点需要说明的是: 通信双方真正通信时使用的是对称加密。 在通信开始之前,通信双方会协商出一个对称加密的秘钥,而在这个协商过程中会使用非对称加密来传输信息。这样就既能保证通信的效率又解决了对称加密秘钥分发存在风险的问题。
在握手完成之后,通信双方就可以加密传输了。

使用JSSE实现SSL/TSL
JSSE全称是 java security socket extension,我们下面将使用JSSE来实现SSL。
我们首先需要使用上一篇中的生成的证书导出为Java环境可用的keystore文件。
客户端证书导出:
服务器端证书导出:
将CA根证书导出为信任库:
使用SSLServerSocket 、SSLSocket和普通socket并没有太大的不同,只不过需要在使用之前加载证书和信任库而已,如下代码:
SSL服务端:
package server; import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.TrustManagerFactory; public class Server {
public static SSLServerSocket serverSocket; public static void init() throws Exception{
int port = 1234; //服务端keystore文件
String keystorePath = "/home/massclouds/keystores/server.keystore";
String keystorePassword = "1234"; //由根证书导出的信任库
String trustKeystorePath = "/home/massclouds/keystores/ca-trust.keystore";
String trustKeystorePassword = "123456"; //========= 加载服务端keystore文件和根证书信任库 begin ===============
SSLContext sslContext = SSLContext.getInstance("SSL"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509"); TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509"); KeyStore keyStore = KeyStore.getInstance("pkcs12");
KeyStore trustKeystore = KeyStore.getInstance("jks"); FileInputStream keyStoreFis = new FileInputStream(keystorePath);
keyStore.load(keyStoreFis, keystorePassword.toCharArray()); FileInputStream trustKeyStoreFis = new FileInputStream(trustKeystorePath);
trustKeystore.load(trustKeyStoreFis, trustKeystorePassword.toCharArray()); kmf.init(keyStore, keystorePassword.toCharArray());
tmf.init(trustKeystore); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); serverSocket = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket(port);
//========= 加载服务端keystore文件和根证书信任库 end =============== //是否需要客户端认证
serverSocket.setNeedClientAuth(true); } public static void process() throws Exception{
String bye = "bye bye !"; while(true){
Socket socket = serverSocket.accept();
byte[] inputBytes = new byte[1024];
InputStream input = socket.getInputStream();
input.read(inputBytes); System.out.println(new String(inputBytes)); OutputStream out = socket.getOutputStream();
out.write(bye.getBytes(), 0, bye.getBytes().length);
out.flush();
}
} public static void main(String[] args) throws Exception {
init();
process();
}
}
SSL客户端:
package client; import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory; public class Client {
public static SSLSocket sslSocket ; public static void init() throws Exception{
String host = "localhost";
int port = 1234; //客户端
String keystorePath = "/home/massclouds/keystores/client.keystore";
String keystorePassword = "1234"; //CA
String trustKeystorePath = "/home/massclouds/keystores/ca-trust.keystore";
String trustKeystorePassword = "123456"; SSLContext sslContext = SSLContext.getInstance("SSL"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509"); TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509"); KeyStore keyStore = KeyStore.getInstance("pkcs12");
KeyStore trustKeyStore = KeyStore.getInstance("jks"); FileInputStream keyStoreFis = new FileInputStream(keystorePath);
keyStore.load(keyStoreFis, keystorePassword.toCharArray()); FileInputStream trustKeyStoreFis = new FileInputStream(trustKeystorePath);
trustKeyStore.load(trustKeyStoreFis, trustKeystorePassword.toCharArray()); kmf.init(keyStore, keystorePassword.toCharArray());
tmf.init(trustKeyStore); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(host, port);
} public static void process() throws Exception{
String hello = "do what you love and keep doing !";
OutputStream output = sslSocket.getOutputStream();
output.write(hello.getBytes(), 0, hello.getBytes().length);
output.flush(); byte[] inputBytes = new byte[1024];
InputStream input = sslSocket.getInputStream();
input.read(inputBytes); System.out.println(new String(inputBytes));
} public static void main(String[] args) throws Exception{
init();
process();
} }
在tomcat中部署HTTPS
实验环境是centos 7,tomcat所在目录是/usr/local/tomcat 。
我们将上面导出的keystore文件放在/root/keystore目录下
下面我们来配置 服务端单向认证
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="/root/keystore/server.keystore"
keystorePass="1234"
keystoreType="pkcs12"
/>
配置完成后如果希望我们的服务器证书通过浏览器的验证还需要两步:
1. 讲在第一篇中生产的CA根证书导入到操作系统中。
2. 通过服务器证书中使用者的CN来配置客户端机器的域名映射,例如我的环境中需要配置
然后通过www.server.com来访问。这样就可以让服务器证书通过认证了。
在apache http server中配置https
实验环境 centos 7 Apache/2.4.6
默认情况下httpd是没有mod_ssl模块的,我们首先安装mod_ssl。
这个时候其实就已经支持https了,此时使用的证书和私钥是(配置文件是/etc/httpd/conf.d/ssl.conf):
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
此时服务器证书肯定是无法通过客户端认证的,启动服务访问:

我们从/etc/httpd/conf.d/ssl.conf中可以看到配置了一个443端口的virtual host,我们可以将这个虚拟主机中的 SSLCertificateFile SSLCertificateKeyFile 这两个指令配置为我们前面所讨论的服务器证书和私钥,这里有个问题是:我们上面在产生私钥时都使用 -aes256 参数加密了,所以每次在启动httpd时都会要求我们输入这个秘钥。 我们可以不用这个参数重新生产一遍服务器的私钥和证书(过程不再赘述)。
SSLCertificateKeyFile /etc/pki/tls/private/server-key.pem
剩下的工作和tomcat中就一样了,在客户端操作系统中导入根证书,配置服务器证书中对应的域名,然后就可以安全访问了。
将所有http请求重定向到https
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule .* https://%{HTTP_HOST}/%{REQUEST_URI} [R=301,L,QSA]
</IfModule>
HTTPS学习(二):原理与实践的更多相关文章
- Canal和Otter讨论二(原理与实践)
上次留下的问题 问题一: 跨公网部署Otter 参考架构图 解析 a. 数据涉及网络传输,S/E/T/L几个阶段会分散在2个或者更多Node节点上,多个Node之间通过zookeeper进行协同工 ...
- 深入浅出深度学习:原理剖析与python实践_黄安埠(著) pdf
深入浅出深度学习:原理剖析与python实践 目录: 第1 部分 概要 1 1 绪论 2 1.1 人工智能.机器学习与深度学习的关系 3 1.1.1 人工智能——机器推理 4 1.1.2 机器学习—— ...
- kafka原理和实践(二)spring-kafka简单实践
系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...
- WebSocket原理与实践(二)---WebSocket协议
WebSocket原理与实践(二)---WebSocket协议 WebSocket协议是为了解决web即时应用中服务器与客户端浏览器全双工通信问题而设计的.协议定义ws和wss协议,分别为普通请求和基 ...
- 20145308 《网络对抗》 MAL_免杀原理及实践 学习总结
20145308 <网络对抗> MAL_免杀原理及实践 学习总结 实践内容 (1)理解免杀技术原理 (2)正确使用msf编码器,veil-evasion,自己利用shellcode编程等免 ...
- 【百度】大型网站的HTTPS实践(一)——HTTPS协议和原理
大型网站的HTTPS实践(一)——HTTPS协议和原理 原创 网络通信/物联网 作者:AIOps智能运维 时间:2018-11-09 15:07:39 349 0 前言 百度于2015年上线了全站 ...
- 2017.2.9 深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二)-----配置文件详解
深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二) ------配置文件详解 8.2 MyBatis-Spring应用 8.2.1 概述 本文主要讲述通过注解配置MyBa ...
- C++学习书籍推荐《C++程序设计原理与实践》下载
百度云及其他网盘下载地址:点我 编辑推荐 <C++程序设计原理与实践>是经典程序设计思想与C++开发实践的完美结合,是C++之父回归校园后对C++编程原理和技巧的全新阐述.书中全面地介绍了 ...
- Spring Boot自动配置原理与实践(二)
前言 在之前的博文(Spring Boot自动配置原理与实践(一))中,已经介绍了Spring boot的自动配置的相关原理与概念,本篇主要是对自动配置的实践,即自定义Starter,对原理与概念加深 ...
- 一篇读懂HTTPS:加密原理、安全逻辑、数字证书等
1.引言 HTTPS(全称: Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版.本文,就来深入介绍下其 ...
随机推荐
- 我的 2020:出书、办签售会、发展 VS Code 中文社区、成为开源先锋、全网 10 万粉丝、10 场演讲、内推 21 人、955.WLB 发扬光大
感觉写 2019 年终总结还是在不久之前.转眼间,2020 已经接近尾声了.是时候来写写 2020 年的年终总结了. 出书 今年最高兴的事情之一,就是出了全球首本 VS Code 中文书 -- < ...
- JPA 缓存
JPA有两种类型的缓存: EntityManager自身就是一种缓存.事务中从数据库获取的和写入到数据库的数据会被缓存(什么样的数据会被缓存,在后面有介绍).在一个程序中也许会有很多个不同的Entit ...
- Centos7对外开放端口
(1)查看对外开放的端口状态 查询已开放的端口 netstat -anp 查询指定端口是否已开 firewall-cmd --query-port=666/tcp 提示 yes,表示开启:no表示未开 ...
- [LeetCode98]98. Validate Binary Search Tree判断二叉搜索树
判断二叉搜索树的方法是: 中序遍历形成递增序列 //全局变量记录中序遍历产生的序列,因为要递归,所以要用全局变量 List<Integer> list = new ArrayList< ...
- 吞食鱼2(FeedingFrenzyTwo) 修改器
吞食鱼2(FeedingFrenzyTwo) 修改器 童年回忆系列.小时候特别喜欢玩这类游戏,软件不大,很慢的网速也不会下载太久,然后对配置要求不高,很破的电脑也可以玩得很开心.不过也有糟心的时候啊, ...
- java数组之system.arrayCopy
public class ArrayDemo { /* public static void main(String[] args) { int[] a=new int[4]; int[] b=new ...
- AtCoder Beginner Contest 187 F - Close Group
题目链接 点我跳转 题目大意 给你一张完全图,你可以删除任意数量的边 要求删除完后剩余的所有子图必须是完全图 问完全子图数量最少是多少 解题思路 定义 \(ok[i]\) 表示状态为 \(i\) 时所 ...
- 初学VBA
一个最基本的VBA程序 Sub test() //宏开始 Dim ge As Range //定义变量 For Each ge In Range("a1:a10") //从a1到a ...
- LAMP搭建 转
LAMP搭建 LAMP环境配置安装注意安装步骤及说明事项. (一) 安装gcc gcc glibc-devel glibc-headers kernel-headers libgo ...
- Linux 设置静态IP
由于工作需要,安装一套Linux系统.安装完成后发现这个家伙居然不能上网,然后看了下IP,(命令 ip a)发现是127.0.0.1 下面是我的界面: inet 是127.0.0.1/8 还有6个网卡 ...