Total Pageviews

Wednesday, 4 April 2018

TLS/SSL

背景介绍     SSL(Secure Sockets Layer 安全套接层)为Netscape公司所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取及窃听。它已被广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输。
    SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层:
  • SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
  • SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
    到了1999年,SSL 因为应用广泛,已经成为互联网上的事实标准。IETF 就在那年把 SSL 标准化。标准化之后的名称改为 TLS(是“Transport Layer Security”的缩写),中文叫做“传输层安全协议”。 很多相关的文章都把这两者并列称呼(SSL/TLS),因为这两者可以视作同一个东西的不同阶段。
    以上文字大部分摘自百度百科。

版本

  • 1995: SSL 2.0, 由Netscape提出,这个版本由于设计缺陷,并不安全,很快被发现有严重漏洞,已经废弃。
  • 1996: SSL 3.0. 写成RFC,开始流行。目前(2015年)已经不安全,必须禁用。
  • 1999: TLS 1.0. 互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版.
  • 2006: TLS 1.1. 作为 RFC 4346 发布。主要fix了CBC模式相关的如BEAST攻击等漏洞
  • 2008: TLS 1.2. 作为RFC 5246 发布 。增进安全性。目前(2015年)应该主要部署的版本,请确保你使用的是这个版本
  • 2015之后: TLS 1.3,还在制订中,支持0-rtt,大幅增进安全性,砍掉了aead之外的加密方式

作用

1)认证用户和服务器,确保数据发送到正确的客户机和服务器;
2)加密数据以防止数据中途被窃取;
3)维护数据的完整性,确保数据在传输过程中不被篡改。

基本的运行过程

    SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。但是,这里有两个问题。
(1)如何保证公钥不被篡改?
    解决方法:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。
(2)公钥加密计算量太大,如何减少耗用的时间?
    解决方法:每一次对话(session),客户端和服务器端都生成一个"对话密钥"(session key),用它来加密信息。由于"对话密钥"是对称加密,所以运算速度非常快,而服务器公钥只用于加密"对话密钥"本身,这样就减少了加密运算的消耗时间。
    因此,SSL/TLS协议的基本过程是这样的:
(1) 客户端向服务器端索要并验证公钥。
(2) 双方协商生成"对话密钥"。
(3) 双方采用"对话密钥"进行加密通信。
    上面过程的前两步,又称为"握手阶段"(handshake)。

握手阶段的详细过程


"握手阶段"涉及四次通信,我们一个个来看。需要注意的是,"握手阶段"的所有通信都是明文的。
客户端发出请求(ClientHello)
    首先,客户端(通常是浏览器)先向服务器发出加密通信的请求,这被叫做ClientHello请求。
在这一步,客户端主要向服务器提供以下信息。
(1) 支持的协议版本,比如TLS 1.0版。
(2) 一个客户端生成的随机数,稍后用于生成"对话密钥"。
(3) 支持的加密方法,比如RSA公钥加密。
(4) 支持的压缩方法。
    这里需要注意的是,客户端发送的信息之中不包括服务器的域名。也就是说,理论上服务器只能包含一个网站,否则会分不清应该向客户端提供哪一个网站的数字证书。这就是为什么通常一台服务器只能有一张数字证书的原因。     对于虚拟主机的用户来说,这当然很不方便。2006年,TLS协议加入了一个Server Name Indication扩展,允许客户端向服务器提供它所请求的域名。
服务器回应(SeverHello)
    服务器收到客户端请求后,向客户端发出回应,这叫做SeverHello。服务器的回应包含以下内容。
(1) 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。
(2) 一个服务器生成的随机数,稍后用于生成"对话密钥"。
(3) 确认使用的加密方法,比如RSA公钥加密。
(4) 服务器证书。
    除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供"客户端证书"。比如,金融机构往往只允许认证客户连入自己的网络,就会向正式客户提供USB密钥,里面就包含了一张客户端证书。
客户端回应
    客户端收到服务器回应以后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。 如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。
(1) 一个随机数。该随机数用服务器公钥加密,防止被窃听。
(2) 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(3) 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。
    上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称"pre-master key"。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把"会话密钥"。
    此外,如果前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息。
服务器的最后回应
    服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的"会话密钥"。然后,向客户端最后发送下面信息。
(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。
    至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用"会话密钥"加密内容。

单项认证和双向认证

    SSL/TLS握手过程可以分成两种类型:
1)SSL/TLS 单向认证:
    客户端会认证服务器端身份,而服务器端不会去对客户端身份进行验证。 一般web应用都是采用单向认证的,原因很简单,用户数目广泛,且无需做在通讯层做用户身份验证,一般都在应用逻辑层来保证用户的合法登入。

2)SSL/TLS 双向认证:
    就是双方都会互相认证,也就是两者之间将会交换证书。企业应用对接会要求对客户端(相对而言)做身份验证,如系统间使用WebService,AMQP等技术,这时需要做双向认证。
    我们知道,握手过程实际上就是通信双方协商交换一个用于对称加密的密钥的过程,而且握手过程是明文的。
    以下是其流程图(摘自rfc5246),括号中的步骤是可选的。如果是单向认证,那么蓝色字体部分是不需要的。
Client Server
1 Client Hello

2 Server Hello
3 certificate
4 (server_key_exchange)
5 (certificate_request)
6 server_hello_done
7 (certificate)
8 client_key_exchange
9 (certifiate_verify)
10 change_cypher_spec
----finished----


11 change_cypher_spec
----finished----

参考文档

[图解SSL/TLS协议]
http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
 
[SSL/TLS协议运行机制的概述]
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
 
[聊聊HTTPS和SSL/TLS协议]
http://www.techug.com/https-ssl-tls
 
[使用wireshark观察SSL/TLS握手过程--双向认证/单向认证]
http://blog.csdn.net/fw0124/article/details/40983787
 
[SSH HTTPS 公钥、秘钥、对称加密、非对称加密、 总结理]
http://my.oschina.net/shede333/blog/359290
 
[数字证书原理]
https://www.centos.bz/2012/08/digital-certificates-principle/
 
[CA认证原理以及实现(上)]
http://yale.iteye.com/blog/1675344
 
[CA认证原理以及实现(下)]
http://yale.iteye.com/blog/1675355
-------------------------------------------------
 

搭建私有CA与基于OpenSSL的双向身份认证

0x00 前言

互联网上的Web应用由于用户数目广泛,都是采用单向身份认证的,只需要客户端验证服务端的身份。但如果是企业内部的应用对接,客户端数量有限,可能就会要求对客户端也做身份验证,这时就需要一个双向认证方案。本文通过搭建私有CA,利用OpenSSL工具,实现服务端与客户端的双向身份认证。安全协议采用HTTPS,这里用到的HTTPS除了能够进行身份认证以外,还能保证通信的保密性和完整性。

0x01 组建方案

组建方案如下图所示:

  • 整个方案包括一个私有CA、一个服务端、一个客户端,用于验证HTTPS双向身份认证的可行性。
  • 服务端的系统环境为CentOS7,Web服务器采用Tomcat7。客户端的系统环境为Windows,浏览器采用Firefox。
  • 由私有CA负责生成所有的私钥、证书等文件,并通过线下安全途径分发到服务端和客户端。
  • 其中,CA的私钥最为重要需加密后离线保管;服务端持有CA证书、服务器私钥、服务器证书3个文件;客户端持有CA证书、客户端私钥、客户端证书3个文件。
  • 客户端与服务端通过HTTPS安全协议进行双向身份认证、加密传输。另外,需要注意的是,HTTPS实现的是传输层的身份认证,不含应用层鉴权功能。


0x02 私有CA的设置

安装与配置OpenSSL

OpenSSL默认安装完后,另外需要创建以下几个文件:

touch /etc/pki/CA/index.txt       #index文件用于记录已经签发的证书
touch /etc/pki/CA/serial
echo 00 > /etc/pki/CA/serial      #serial文件用于存放证书的序列号,自动递增

因为,服务器证书请求文件的国家、省、市要和CA证书一致,这个在openssl.cnf默认配置中指定了。
可以通过修改/etc/pki/tls/openssl.cnf文件,调整为非强制一致。
将下面的几个match 修改为 optional

# For the CA policy  
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match

自签CA证书

生成根证书私钥 cakey.pem

openssl genrsa  -des3 -out cakey.pem  2048 这一步需要输入密码,密码用于加密私钥,使私钥非明文存于磁盘  

生成根证书签发申请文件 ca.csr

openssl req -new -key cakey.pem -out ca.csr -subj "/C=CN/O=CAorganization/CN=myCA"

自签发根证书 cacert.pem

openssl x509 -req -days 3650 -sha256 -extensions v3_ca -signkey cakey.pem -in ca.csr -out  cacert.pem  

服务端的私钥与证书

生成服务端私钥 serverkey.pem

openssl genrsa -out serverkey.pem 2048  

生成证书请求文件 server.csr

openssl req -new \
-sha256 \
-key serverkey.pem \
-subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=myserver.test.com" \
-reqexts SAN \
-config <(cat /etc/pki/tls/openssl.cnf \
	<(printf "[SAN]\nsubjectAltName=DNS:myserver.test.com,DNS:myserver2.test.com")) \
-out server.csr  

使用根证书签发服务端证书 servercert.pem

openssl ca -in server.csr \
-md sha256 \
-days 3650 \
-keyfile cakey.pem \
-cert cacert.pem \
-extensions SAN \
-config <(cat /etc/pki/tls/openssl.cnf \
    <(printf "[SAN]\nsubjectAltName=DNS:myserver.test.com,DNS:myserver2.test.com")) \
-out servercert.pem  

客户端的私钥与证书

生成客户端私钥 clientkey.pem

openssl genrsa -out  clientkey.pem 2048  

生成证书请求文件 client.csr

openssl req -new \
-sha256 \
-key clientkey.pem \
-subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=myclient.test.com" \
-reqexts SAN \
-config <(cat /etc/pki/tls/openssl.cnf \
    <(printf "[SAN]\nsubjectAltName=DNS:myclient.test.com,DNS:myclient2.test.com")) \
-out client.csr  

使用根证书签发客户端证书 clientcert.pem

openssl ca -in client.csr \
-md sha256 \
-days 3650 \
-keyfile cakey.pem \
-cert cacert.pem \
-extensions SAN \
-config <(cat /etc/pki/tls/openssl.cnf \
<(printf "[SAN]\nsubjectAltName=DNS:myclient.test.com,DNS:myclient2.test.com")) \
-out clientcert.pem  

0x03 服务器端的设置

安装Java、Tomcat、创建JKS存放目录

Java、Tomcat安装完后,创建存放JKS文件的目录;服务端的Tomcat将用到的JKS格式的私钥和证书

mkdir -p  /data/live  #此目录之后用于存放JKS文件

把服务端私钥和证书制作成JKS文件

得到 fullchain_and_key.p12

openssl pkcs12 -export -in  servercert.pem -inkey serverkey.pem  -out fullchain_and_key.p12 -name ssl

这一步需要输入密码,假设此处输入密码为: mypassword

得到 ssl.jks

keytool -importkeystore -deststorepass myJKSpassasdf -destkeypass myJKSpassasdf -destkeystore ssl.jks -srckeystore fullchain_and_key.p12 -srcstoretype PKCS12 -srcstorepass  mypassword -alias ssl

移位文件到/data/live

mv  ssl.jks  /data/live

把信任根证书制作成JKS文件

得到ssl2.jks

keytool -import -alias cacert -file  cacert.pem  -keystore ssl2.jks  -deststorepass myJKSpassasdf2  -destkeypass myJKSpassasdf2

移位文件到/data/live

mv  ssl2.jks  /data/live

配置Tomcat

修改Tomcat的server.xml,配置HTTPS功能

 <Connector port="443" protocol="org.apache.coyote.http11.Http11Protocol"
           URIEncoding="UTF-8"  maxThreads="500" SSLEnabled="true" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS"  
           keystoreFile="/data/live/ssl.jks" keystorePass="myJKSpassasdf" 
           truststoreFile="/data/live/ssl2.jks" truststorePass="myJKSpassasdf2"                
sslEnabledProtocols="TLSv1,TLSv1.1,TLSv1.2,SSLv2Hello"
ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,   
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,  
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,  
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,  
TLS_ECDHE_RSA_WITH_RC4_128_SHA,  
TLS_RSA_WITH_AES_128_CBC_SHA256,  
TLS_RSA_WITH_AES_128_CBC_SHA,  
TLS_RSA_WITH_AES_256_CBC_SHA256,  
TLS_RSA_WITH_AES_256_CBC_SHA,  
SSL_RSA_WITH_RC4_128_SHA" />

一些参数说明:

  • port该设置为HTTPS端口号。
  • 如果clientAuth该设置为“false”,则为单向SSL验证,不需要验证客户端证书。
  • 如果clientAuth设置为“true”,表示强制双向SSL验证。
  • 如果clientAuth设置为“want”,则表示可以验证客户端证书,但如果客户端没有有效证书,也不强制验证。
  • keystore就是用来存服务端自身的私钥和证书。
  • truststore就是用来存根证书文件的。

在服务端强制HTTP访问跳转为HTTPS

在server.xml文件,将redirectPort的8443端口,一律改为443(因为我们之前已经设置了443端口作为HTTPS端口)
在web.xml文件,插入这样一段:

<login-config>  
    <!-- Authorization setting for SSL -->  
    <auth-method>CLIENT-CERT</auth-method>  
    <realm-name>Client Cert Users-only Area</realm-name>  
</login-config>  
<security-constraint>  
<!-- Authorization setting for SSL -->  
	<web-resource-collection >  
    	<web-resource-name >SSL</web-resource-name>  
    	<url-pattern>/*</url-pattern>  
	</web-resource-collection>  
	<user-data-constraint>  
    	<transport-guarantee>CONFIDENTIAL</transport-guarantee>  
	</user-data-constraint>  
</security-constraint> 

最后重启tomcat生效

0x04 客户端的设置

修改客户端hosts文件

hosts中加入一行 192.168.1.10 myserver.test.com
其中,假设192.168.1.10是服务端的IP,根据实际情况改写。
注意:这里hosts文件作用是代替DNS域名解析,仅为测试使用。

客户端浏览器导入CA根证书

Firefox浏览器导入cacert.pem文件:


 

客户端浏览器导入自己的私钥和证书

X509格式转化为pkcs12格式的文件

openssl pkcs12 -export -in  clientcert.pem -inkey clientkey.pem    -out  client.p12 -name client_ssl  

这一步需要输入密码,假设此处输入密码为: mypassword

将client.p12导入到浏览器:

0x05 双向身份验证与效果

浏览器打开URL: https://myserver.test.com
选择客户端证书

 成功打开网站:

--------------
相关帖子:
http://briteming.blogspot.com/2018/04/tls.html
http://briteming.blogspot.com/2018/04/ssltls-ciphersuite.html

No comments:

Post a Comment