|
一、HTTPS是什么
HTTPS相当于HTTPS协议,英文全称Hyper Text Transfer Protocol over SecureSocket Layer,是超文本传输安全协议的意思,最初是由网景公司创建。HTTPS在HTTP上面提供了一个传输级的安全层,目前安全层所用的协议是SSL(Secure Socket Layer)和其继任者TLS(Transport Layer Security),SSL是一个比较复杂的协议,已有商用和开源的实现版本,例如OpenSSL。所有HTTP请求和响应数据在传输到网络前都由安全层进行加密,下图是Https和Http的网络协议层:

HTTPS具有如下特点:
- 采用混合加密技术,中间者无法直接查看明文内容。
- 通过证书认证客户端访问的是自己的服务器。
- 防止传输的内容被中间人冒充或者篡改,保护数据完整性。
1、HTTP和HTTPS区别
- "HTTP"和"HTTPS"都是超文本传输协议,但不同的是"HTTPS"是加了安全协议,所以更多用于敏感的通信,比如交易场景。
- HTTPS主要由两部分组成:HTTP+ SSL / TLS,也就是在 HTTP上又加了一层处理加密信息的模块。
- HTTPS协议是由SSL+HTTP构建的可进行加密传输、身份认证的网络协议,要比 HTTP安全,可防止数据在传输过程中被窃取、改变,确保数据的完整性。
2、HTTPS的实现原理
HTTPS的整体过程分为证书验证和数据传输阶段,具体的交互过程如下:

按照上图描述,各阶段解析如下:
- 客户端(如浏览器)发起 HTTPS 请求。
- 服务端返回HTTPS 证书。
- 客户端验证证书是否合法,如果不合法则提示告警。
<li data-pid="h0xkRp2O">数据传输阶段
- 当证书验证合法后,在本地生成随机数。
- 通过公钥加密随机数,并把加密后的随机数传输到服务端。
- 服务端通过私钥对随机数进行解密。
- 服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输。
3、数据加密传输机制
如同日常生活中的钥匙一样,它可以加密一段信息,也可以对加密后的数据进行解密。常见的对称加密算法有: DES、3DES、Blowfish、IDEA、RC4、RC5、RC6 和 AES。

公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。甲方只能用其专用密钥解密由其公用密钥加密后的任何信息。
常见的非对称加密算法有: RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)。

算法 | 优点 | 缺点 | 对称加密 | 算法公开、计算量小、加密速度快、加密效率高 | 在数据传送前,发送方和接收方必须商定好秘钥,然后使双方都能保存好秘钥。其次如果一方的秘钥被泄露,那么加密信息也就不安全了。另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一秘钥,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担。 | 非对称加密 | 安全 | 速度较慢 | 1、加密和解密过程不同
对称加密的加密过程和解密过程使用的同一个密钥,加密过程相当于用原文+密钥可以传输出密文,同时解密过程用密文-密钥可以推导出原文。但非对称加密采用了两个密钥,一般使用公钥进行加密,使用私钥进行解密。
2、加密解密速度不同
对称加密解密的速度比较快,适合数据比较长时的使用。非对称加密和解密花费的时间长、速度相对较慢,只适合对少量数据的使用。
3、传输的安全性不同
对称加密的过程中无法确保密钥被安全传递,密文在传输过程中是可能被第三方截获的,如果密码本也被第三方截获,则传输的密码信息将被第三方破获,安全性相对较低。
- 非对称加密的加解密效率是非常低的,而 HTTP的应用场景中通常端与端之间存在大量的交互,非对称加密的效率是无法接受的。
- 在HTTPS 的场景中只有服务端保存了私钥,一对公私钥只能实现单向的加解密,所以 HTTPS 中内容传输加密采取的是对称加密,而不是非对称加密。
4、HTTPS证书
HTTPS证书就是我们常说的CA证书或者SSL证书。为什么需要 CA 认证机构颁发证书?
HTTP 协议被认为不安全是因为传输过程容易被监听者监听、伪造服务器,而 HTTPS 协议主要解决的便是网络传输的安全性问题。
首先我们假设不存在认证机构,任何人都可以制作证书,这带来的安全风险便是经典的“中间人攻击”问题。“中间人攻击”的具体过程如下:

- 本地请求被劫持(如DNS劫持等),所有请求均发送到中间人的服务器。
- 中间人服务器返回中间人自己的证书。
- 客户端创建随机数,通过中间人证书的公钥对随机数加密后传送给中间人,然后凭随机数构造对称加密对传输内容进行加密传输。
- 中间人因为拥有客户端的随机数,可以通过对称加密算法进行内容解密。
- 中间人以客户端的请求内容再向正规网站发起请求。
- 因为中间人与服务器的通信过程是合法的,正规网站通过建立的安全通道返回加密后的数据。
- 中间人凭借与正规网站建立的对称加密算法对内容进行解密。
- 中间人通过与客户端建立的对称加密算法对正规内容返回的数据进行加密传输。
- 客户端通过与中间人建立的对称加密算法对返回结果数据进行解密。
由于缺少对证书的验证,所以客户端虽然发起的是 HTTPS 请求,但客户端完全不知道自己的网络已被拦截,传输内容被中间人全部窃取。
- 颁发机构信息
- 公钥
- 公司信息
- 域名
- 有效期
- 指纹
- ......
<li data-pid="z1V0Ucfa">证书的合法性依据
- 权威机构是要有认证的,不是随便一个机构都有资格颁发证书。
- 证书的可信性基于信任制,权威机构需要对其颁发的证书进行信用背书,只要是权威机构生成的证书,我们就认为是合法的。所以权威机构会对申请者的信息进行审核,不同等级的权威机构对审核的要求也不一样,于是证书也分为免费的、便宜的和贵的。
<li data-pid="zOAIGF3T">验证证书的合法性

浏览器发起 HTTPS 请求时,服务器会返回网站的 SSL 证书,浏览器需要对证书做以下验证:
- 验证域名、有效期等信息是否正确。证书上都有包含这些信息,比较容易完成验证。
- 判断证书来源是否合法。每份签发证书都可以根据验证链查找到对应的根证书,操作系统、浏览器会在本地存储权威机构的根证书,利用本地根证书可以对对应机构签发证书完成来源验证。
- 判断证书是否被篡改。需要与 CA 服务器进行校验。
- 判断证书是否已吊销。通过CRL(Certificate Revocation List 证书注销列表)和 OCSP(Online Certificate Status Protocol 在线证书状态协议)实现,其中 OCSP 可用于第3步中以减少与 CA 服务器的交互,提高验证效率。
- 证书的可信性是基于信任制的,权威证书机构需要为其生成的证书进行信用背书,只要是权威机构颁发的证书,客户端(以浏览器为例)默认就是认为是安全的,自动添加到信任列表,无须手动安装证书。也有些网站是需要手动安装证书的,通常需要到指定的官网下载证书,然后安装到信任列表。
回答一个问题:既然证书是公开的,如果要发起中间人攻击,我在官网上下载一份证书作为我的服务器证书,那客户端肯定会认同这个证书是合法的,如何避免这种证书冒用的情况?
其实这就是非加密对称中公私钥的用处,虽然中间人可以得到证书,但私钥是无法获取的,一份公钥是不可能推算出其对应的私钥,中间人即使拿到证书也无法伪装成合法服务端,因为无法对客户端传入的加密数据进行解密。
我们可以向 CA 申请证书,但全世界的顶级 CA(Root CA) 就那么几个,每天都有很多人要向它申请证书,它也忙不过来啊,怎么办呢?想想看在一个公司里如果大家都找 CEO 办事,他是不是要疯了,那他能怎么办?授权,他会把权力交给 CTO,CFO 等,这样你们只要把 CTO 之类的就行了,CTO 如果也忙不过来呢,继续往下授权啊。
同样的,既然顶级 CA 忙不过来,那它就向下一级,下下级 CA 授权即可,这样我们就只要找一级/二级/三级 CA 申请证书即可。怎么证明这些证书被 Root CA 授权过了呢,小一点的 CA 可以让大一点的 CA 来签名认证。比如一级 CA 让 Root CA 来签名认证,二级 CA 让一级 CA 来签名认证,Root CA 没有人给他签名认证,只能自己证明自己了,这个证书就叫「自签名证书」或者「根证书」,我们必须信任它,不然证书信任链是走不下去的(这个根证书前文我们提过,其实是内置在操作系统中的)。

证书信任链现在我们看看如果站点申请的是二级 CA 颁发的证书,client 收到之后会如何验证这个证书呢,实际上 service 除了传给二级 CA 的证书外,还会把证书信任链也一起传给客户端,这样客户端会按如下步骤进行验证:
浏览器就使用信任的根证书(根公钥)解析证书链的根证书得到一级证书的公钥+摘要验签;拿一级证书的公钥解密一级证书,拿到二级证书的公钥和摘要验签;再然后拿二级证书的公钥解密 server 传过来的二级证书,得到服务器的公钥和摘要验签,验证过程就结束了。
每个设备中都会存有一些默认的可信的根证书,但很多CA是不使用根证书进行签名的,而是使用中间层证书进行签名,但是如果我们的服务器上没有中间件证书,而客户端的浏览器里只有CA的根证书,这样就会导致证书信任链不全,浏览器会认为当前的链接是一个不安全的页面。解决的方案就是在服务器的证书中配置上中间商的 crt 证书即可。一般我们在中间商买到的证书基本都是:
-----BEGIN CERTIFICATE-----
# 我是证书内容
-----END CERTIFICATE----- 只需要把证书提供商给你的中间商证书的内容 append 在这个 crt 文件下即可,像这样:
-----BEGIN CERTIFICATE-----
# 证书内容 1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
# 证书内容 2
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
# 证书内容 3
-----END CERTIFICATE-----这样就可以了。当然也可以通过工具自动补全。
二、HTTPS认证机制
客户端向服务器发送HTTPS请求时,会验证服务端的证书状态。在验证证书时,可以分为这几个场景:单向认证、忽略认证、双向认证。结合实际使用场景,单行认证、双向认证都是比较常用的方案,我们着重描述下:
1、单向认证
客户端只对服务端身份进行认证,具体认证过程如下图:

- 发送客户端SSL版本或者TLS版本、加密算法种类、随机数等信息给服务端。
- 服务端给客户端返回SSL版本、加密算法种类、随机数等信息,以及服务器公钥证书,该证书用于客户端获取SSL非对称加密公钥,这个公钥就是用来在后面客户端发送随机数C的时候对随机数C等信息加密传输给服务端的,该公钥用于加密对称加密的密钥。
- 客户端校验服务端证书是否合法,合法继续,不合法告警:
- 证书是否过期
- 发型服务器证书的CA是否可靠
- 返回的公钥是否能正确解开返回证书中的数字签名
- 服务器证书上的域名是否和服务器的实际域名相匹配
- 验证通过后,将继续进行通信,否则,终止通信
- 客户端发送自己可支持的对称加密方案给服务端,供其选择。
- 服务端选择加密程序较高的加密方式。
- 服务端将选择好的加密方案以明文的方式发送给客户端。
- 客户端收到加密方式后,通过特定的算法产生随机数C,在RSA算法中,这个有专门的名称&#34;pre-master Key&#34;,作为对称加密密钥,使用服务端非对称加密私钥进行对对称加密的密钥加密,然后发送给服务端;这里的随机数C就通过非对称加密发送给服务端。
- 服务端使用非对称加密的私钥进行解密拿到随机数C,然后使用三个随机数生成对应的对称加密密钥。
- 最后客户端与服务端的对称加密交互,是协定使用对称加密的密钥,如果服务端和客户端分别可以解密成功,则协定成功,并且双方在发送的时候分别通知握手结束。
2、双向认证
客服端不仅对服务端身份进行认证,同时服务端也需要客户端发送自己的身份信息,对客户端进行认证,具体认证过程如下图:

- 客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
- 服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书。
- 客户端使用服务端返回的信息验证服务器的合法性,包括:
- 证书是否过期
- 发型服务器证书的CA是否可靠
- 返回的公钥是否能正确解开返回证书中的数字签名
- 服务器证书上的域名是否和服务器的实际域名相匹配
- 验证通过后,将继续进行通信,否则,终止通信
- 服务端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端。
- 验证客户端的证书,通过验证后,会获得客户端的公钥。
- 客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择。
- 服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。
- 将加密方案通过使用之前获取到的公钥进行加密,返回给客户端。
- 客户端收到服务端返回的加密方案密文后,使用自己的私钥进行解密,获取具体加密方式,而后,产生该加密方式的随机码,用作加密过程中的密钥,使用之前从服务端证书中获取到的公钥进行加密后,发送给服务端。
- 服务端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。
3、SSL和TLS
SSL:安全套接字层(Secure Socket Layer)位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。
TLS:传输层安全协议(Transport Layer Security)用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。
总结:SSL有1,2,3三个版本,但现在只使用版本3,TLS是SSL的标准化后的产物,有TLS1.0 、TLS1.1、TLS1.2三个版本,TLS1.0和SSL3.0几乎没有区别,事实上我们现在用的都是TLS,但因为历史上习惯了SSL这个称呼,平常还是以叫SSL为多。
三、使用Https服务
1、搭建HTTPS服务器
- 生成SSL证书。目前各大云厂商下都可以轻松申请到证书。
- 配置Nginx。
- 保存配置文件后,重启Nginx:nginx -s reload。
- 再去访问网址,即可发现浏览器网站输入项左侧出现绿色安全锁。
# 1、下载证书到服务器,下载后可以得到两个文件:xxxx.pem和 xxxx.key。
# 2、在Nginx的安装目录下,新建文件夹cert,并将上述两个文件复制到该文件夹
# 3、修改Nginx配置文件/etc/nginx/conf.d/default.conf:
server {
listen 443;
server_name xxx.com;
charset utf-8;
# access_log /var/log/nginx/host.access.log main;
ssl on;
ssl_certificate cert/xxxx.pem;
ssl_certificate_key cert/xxxx.key;
ssl_session_timeout 5m;
ssl_ciphers ....
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi.sock;
}
}
2、HTTP服务支持HTTPS
使用场景:访问请求由HTTP转向HTTPS,但是原服务器代码都是基于HTTP协议开发的。
解决方案:可以通过配置Nignx反向代理实现,代理服务器接受来自Internet上的HTTPS请求,然后转发至内部网络的HTTP服务器。
实现方案:
# 配置Nginx
server {
listen 443;
server_name www.xxx.com;
ssl on;
ssl_certificate ssl/user.pem;
ssl_certificate_key ssl/user.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
# error_page 404 /404.html;
include enable-php.conf;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header Access-Control-Allow-Headers Content-Type;
location / {
proxy_pass http://www.xxx.com:80;
}
……
}3、HTTP服务访问外部HTTPS
使用场景:HTTP服务器访问外部HTTPS服务。
解决方案:可以通过配置Nignx正向代理实现,代理服务器接受HTTP请求,然后转发至外部的HTTPS服务。
实现方案:
# 支持http代理
server {
listen 8099; // 样例
resolver 223.5.5.5; // DNS解析
location / {
proxy_pass http://$http_host$request_uri;
proxy_set_header HOST $http_host;
proxy_buffers 256 4k;
proxy_max_temp_file_size 0k;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_next_upstream error timeout invalid_header http_502;
}
……
}
# 支持https代理
server {
listen 8443; // 样例
resolver 223.5.5.5; // DNS解析
proxy_connect;
proxy_connect_allow 443 563;
proxy_connect_connect_timeout 10s;
proxy_connect_read_timeout 10s;
proxy_connect_send_timeout 10s;
location / {
proxy_pass http://$host;
proxy_set_header HOST $host;
}
……
}四、请求Https服务
通过代码的方式来解释这几种验证方式。以JAVA程序为例,通过HttpClient来举例:
1、自定义SSL/TLS
HttpClient利用SSLConnectionSocketFactory类创建SSL连接。SSLConnectSocketFactory类允许高度自定义,可以把javax.net.ssl.SSLContext接口的实例作为参数传入,并使用它来创建自定义配置的SSL连接,举例如下:
KeyStore trustStore = <..>;
SSLContext sslContext = SSLContext.custom().loadTrustMaterial(trustStore).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory (sslContext);2、主机名验证
除了在SSL/TLS协议级别上进行信任验证和客户端身份验证之外,一旦建立了连接,HttpClient可以选择性地验证目标主机名是否与存储在服务器的X.509证书中的名称匹配。该验证可以提供对服务器信任材料的真实性的额外保证。Javax.net.ssl.HostnameVerifier接口代表主机名验证策略。HttpClient附带了Javax.net.ssl.HostnameVerifier接口的两个实现类。(注意:主机名验证和SSL信任验证这两者不应混淆)
- DefaultHostnameVerifier:HttpClient使用的默认实现类,它应兼容RFC 2818。主机名必须匹配证书指定的任何别名,或在证书持有者没有为别名给出最明确的证书通用名(CN)的情况下。在证书通用名(CN),以及任何subject-alts中都可以出现通配符。如下为DefaultHostnameVerifier的核心代码片段:
public final class DefaultHostnameVerifier implements HostnameVerifier {
......
public boolean verify(String host, SSLSession session) {
try {
Certificate[] certs = session.getPeerCertificates();
X509Certificate x509 = (X509Certificate)certs[0];
this.verify(host, x509);
return true;
} catch (SSLException var5) {
if (this.log.isDebugEnabled()) {
this.log.debug(var5.getMessage(), var5);
}
return false;
}
}
public void verify(String host, X509Certificate cert) throws SSLException {
HostNameType hostType = determineHostFormat(host);
List<SubjectName> subjectAlts = getSubjectAltNames(cert);
if (subjectAlts != null && !subjectAlts.isEmpty()) {
switch (hostType) {
case IPv4:
matchIPAddress(host, subjectAlts);
break;
case IPv6:
matchIPv6Address(host, subjectAlts);
break;
default:
matchDNSName(host, subjectAlts, this.publicSuffixMatcher);
}
} else {
X500Principal subjectPrincipal = cert.getSubjectX500Principal();
String cn = extractCN(subjectPrincipal.getName(&#34;RFC2253&#34;));
if (cn == null) {
throw new SSLException(&#34;Certificate subject for <&#34; + host + &#34;> doesn&#39;t contain &#34; + &#34;a common name and does not have alternative names&#34;);
}
matchCN(host, cn, this.publicSuffixMatcher);
}
}
static void matchIPAddress(String host, List<SubjectName> subjectAlts) throws SSLException {
for(int i = 0; i < subjectAlts.size(); ++i) {
SubjectName subjectAlt = (SubjectName)subjectAlts.get(i);
if (subjectAlt.getType() == 7 && host.equals(subjectAlt.getValue())) {
return;
}
}
throw new SSLPeerUnverifiedException(&#34;Certificate for <&#34; + host + &#34;> doesn&#39;t match any &#34; + &#34;of the subject alternative names: &#34; + subjectAlts);
}
......
}
- NoopHostnameVerifier:作为主机名验证工具,实质上关闭了主机名验证,它接受任何有效的SSL会话并匹配到目标主机。
public class NoopHostnameVerifier implements HostnameVerifier {
public static final NoopHostnameVerifier INSTANCE = new NoopHostnameVerifier();
public NoopHostnameVerifier() {
}
public boolean verify(String s, SSLSession sslSession) {
return true;
}
public final String toString() {
return &#34;NO_OP&#34;;
}
}HttpClient默认使用DefaultHostnameVerifier类来实现。如果需要,可以指定其他的主机名验证器来实现。
SSLConetext sslContext = SSLContext.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory (sslContext, NoopHostnameVerifier.INSTANCE);3、认证策略
当调用loadTrustMaterial接口服务时,需要加载认证策略。HttpClient默认提供了两个认证策略TrustAllStrategy和TrustSelfSignedStrategy。
public class TrustAllStrategy implements TrustStrategy {
public static final TrustAllStrategy INSTANCE = new TrustAllStrategy();
public TrustAllStrategy() {
}
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}
- TrustSelfSignedStrategy: 信任自签名证书。常态下,权威机构发行的证书都会包含多个证书内容(即chain.length > 1),正式环境下,建议使用TrustAllStrategy,或者自行实现TrustStrategy。
public class TrustSelfSignedStrategy implements TrustStrategy {
public static final TrustSelfSignedStrategy INSTANCE = new TrustSelfSignedStrategy();
public TrustSelfSignedStrategy() {
}
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return chain.length == 1;
}
}为了灵活应对实际场景,建议自行实现TrustStrategy来满足业务需求。
4、关于KeyStore
Java KeyStore 是一个存储密钥记录的&#34;数据库&#34;。 一个 java.security.KeyStore class就代表这样一个KeyStore。一个 KeyStore 可以被写到磁盘反复去读,作为一个整体,,它可以被密码保护,,并且每一个在 KeyStore 中的 key 都可以被其完全独立的密码保护,大大提高了其安全性。
- Private keys & Public keys + certificates:公私钥用于非对称加密。其中公钥可以拥有一个与之匹配的证书。而证书本身是一个验证拥有这个公钥的个人,组织或是设备的文件。证书一般是由认证机构 (CA) 数字签名的,也可以是自签名的(但是不会受信任)。
- Secret keys:用于对称加密。
- Certificate:证书本身(密钥库可以单独存储证书条目),一张证书包含了一个能认证证书中声明的主语的公钥,通常被用于服务器授信。
<li data-pid="t3pU-2OC">常用格式:格式 | 扩展名 | 描述 | JKS | .jsk/ks | [Java
Keystore] 密钥库的 Java 实现版本 (sun.security.provider.JavaKeyStore), Provider 为
SUN; 可以保存私钥和其对应公钥的证书; 或是单独的证书; | JCEKS | .jce | [JCE
Keystore (Java Cryptography Extension KeyStore)] 密钥库的 JCE 实现版本
(com.sun.crypto.provider.JceKeyStore), Provider为 SunJCE, JDK1.4 之后才提供; 相对于
JKS 安全级别更高, 是 JKS 的超集, 支持更多算法, 可以保存全部 3 种类型的 Key | PKCS12 | .p12/.pfx | [PKCS
#12] 个人信息交换语法标准. 可以用在 JAVA 和其他语言 (C, C++, C#) 中.
(sun.security.pkcs12.PKCS12KeyStore), 支持全部 3 种类型的 Key |
// 默认类型为 jks (Java Key Store), 就是利用 Java Keytool 工具生成的 KeyStore 文件
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// 或
KeyStore keyStore = KeyStore.getInstance(&#34;PKCS12&#34;);2. 加载KeyStore
使用前,KeyStore 必须被加载。KeyStore 实例通常被写在磁盘上或是以其他类型的方式存储。这就是为什么 KeyStore 会要求用户在使用前必须先加载。但是,初始化一个空的 KeyStore 也是有可能的。
可以调用KeyStore#load(InputStream, char[])方法加载一个KeyStore,其中:
- InputStream:加载KeyStore数据的输入流;
- KeyStore:密码的字符数组;
举例:
char[] keyStorePassword = &#34;123abc&#34;.toCharArray();
InputStream keyStoreData = new FileInputStream(&#34;keystore.ks&#34;);
keyStore.load(keyStoreData, keyStorePassword);如果你不想加载任何数据到KeyStore,只需要向InputStream参数传入null 值,例如:
keyStore.load(null, keyStorePassword);3. 保存KeyStore
如果需要将 KeyStore 保存,例如在磁盘,数据库等地方,供稍后有需要的时候再使用。可以调用KeyStore的 store()方法完成这个操作:
char[] keyStorePassword = &#34;123abc&#34;.toCharArray();
FileOutputStream keyStoreOutputStream = new FileOutputStream(&#34;data/keystore.ks&#34;);
keyStore.store(keyStoreOutputStream, keyStorePassword);5、几种认证场景的使用方式
认证过程分为加载证书、认证策略、设置Socket三个步骤。前面我们讲了三种认证场景,可以简单对比下:
场景 | 加载证书 | 认证策略 | 设置Socket | 单行认证 | // cerPath:证书文件路劲,cerPwd:证书密码
KeyStore trustStore =
KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new
File(cerPath));
trustStore.load(instream,
cerPwd.toCharArray()); | SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.build(); | SSLConnectionSocketFactory sslsf =
new SSLConnectionSocketFactory(sslcontext, new
String[]{&#34;TLSv1.2&#34;}, null,BrowserCompatHostnameVerifier.INSTANCE);
httpclient =
HttpClients.custom().setSSLSocketFactory(sslsf).build(); | 忽略认证 | SSLContext sslContext =
SSLContext.getInstance(&#34;SSLv3&#34;);
sslContext.init(null, new
TrustManager[]{truseAllManager}, null);
public static TrustManager truseAllManager = new
X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(X509Certificate[] arg0, String
arg1)
throws CertificateException {
}
public void checkClientTrusted(X509Certificate[] arg0, String
arg1)
throws CertificateException {
}
};
或者
SSLContext sslContext = new
SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
public boolean isTrusted(X509Certificate[] chain, String
authType) throws CertificateException {
return true;
}
}).build(); | SSLConnectionSocketFactory sslsf2 = new
SSLConnectionSocketFactory(sslContext,
AllowAllHostnameVerifier.INSTANCE);
httpclient =
HttpClients.custom().setSSLSocketFactory(sslsf2).build(); | 双向认证 | // cerClientPath:客户端证书路径,cerClientPwd:客户端证书密码
KeyStore keyStore = KeyStore.getInstance(&#34;PKCS12&#34;);
keyStore.load(new FileInputStream(new File(cerClientPath)),
cerClientPwd.toCharArray());
// 加载服务端提供的truststore。cerSerPath:服务端证书路径,cerSerPwd:服务端证书密码
// loadKeyMaterial()重载方法是加载客户端证书用的
SSLContext sslcontext =
SSLContexts.custom()
.loadTrustMaterial(new
File(cerSerPath), cerSerPwd.toCharArray(), new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore,
&#34;cmcc&#34;.toCharArray())
.build(); | SSLConnectionSocketFactory sslConnectionSocketFactory = new
SSLConnectionSocketFactory(sslcontext,new
String[]{&#34;TLSv1&#34;},null,SSLConnectionSocketFactory.getDefaultHostnameVerifier());
httpclient =
HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build(); | application.properties中的配置清单,举例如下:
server.port=8089
server.ssl.key-store=classpath:keystore/xxx.jks
server.ssl.key-store-password=xxxxxx
server.ssl.key-store-type=JKS这里面粘贴下loadTrustMaterial方法的实现:
public class SSLContextBuilder {
......
public SSLContextBuilder loadTrustMaterial(KeyStore truststore, TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init(truststore);
TrustManager[] tms = tmfactory.getTrustManagers();
if (tms != null) {
if (trustStrategy != null) {
for(int i = 0; i < tms.length; ++i) {
TrustManager tm = tms;
if (tm instanceof X509TrustManager) {
tms = new TrustManagerDelegate((X509TrustManager)tm, trustStrategy);
}
}
}
TrustManager[] arr$ = tms;
int len$ = tms.length;
for(int i$ = 0; i$ < len$; ++i$) {
TrustManager tm = arr$[i$];
this.trustmanagers.add(tm);
}
}
return this;
}
public SSLContextBuilder loadTrustMaterial(KeyStore truststore) throws NoSuchAlgorithmException, KeyStoreException {
return this.loadTrustMaterial(truststore, (TrustStrategy)null);
}
......
}重载的方法,不需要证书,只需要信任策略即可。
(1)loadTrustMaterial与loadKeyMaterial的区别:
- loadTrustMaterial用于加载服务器端证书,在上面讲到的证书验证阶段会用到,在单向、双向认账场景下都会用到。
- loadKeyMaterial用于加载客户端本地证书,主要用于服务器端的证书验证。在双向认证场景下,服务端也需要验证客户端访问的合法性,通过验证客户端证书,来确保内容访问的合法性。
6、keytool工具使用
keytool -genkey -alias server -keyalg RSA -keystore server.jks2. 导出服务端证书
keytool -export -file server.cer -alias server -keystore server.jks3. 导出客户端密钥
keytool -import -keystore client.jks -file server.cer -alias client4. pem证书转化为jks证书
openssl pkcs12 -export -out dst.pfx -in src.pem -inkey src.key
keytool -importkeystore -srckeystore dst.pfx -destkeystore dst.jks -srcstoretype PKCS12 -deststoretype JKSkeytool: https://blog.csdn.net/Guesshat/article/details/123024693 五、最后
- HTTP协议由HTTP+SSL构建,可以认证用户端和服务端身份,可以加密传输,防止数据在传输过程中被窃取、改变,确保数据的完整性,确保数据发送到正确的客户端和服务端。HTTPS 是当下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。但HTTPS 协议的安全是有范围的,在黑客攻击、拒绝服务攻击和服务器劫持等方面几乎起不到什么作用。
- HTTPS 协议会使页面的加载时间延长近 50%,增加 10%到 20%的耗电。此外,HTTPS 协议还会影响缓存,增加数据开销和功耗 。
- 部署 HTTPS 后,因为 HTTPS 协议的工作要增加额外的计算资源消耗,例如 SSL 协议加密算法和 SSL 交互次数将占用一定的计算资源和服务器成本。在大规模用户访问应用的场景下,服务器需要频繁地做加密和解密操作,几乎每一个字节都需要做加解密,这就产生了服务器成本。
|
|