Java爬虫(八)-- httpClient进阶:HTTPS和证书认证(原理总结篇)

一、前言

本篇文章承接上一篇,对应讲述一些我在接触SSL协议、证书认证时学到的一些原理性知识。因为本身不是科班出身,网络方面很多对我来说都是新知识,特在此记录一下。

二、HTTPS

HTTPS = HTTP + SSL/TLS 协议,即加密过后的HTTP通信。它其实还是HTTP协议,只是在外面加了一层,SSL 是一种加密安全协议,引入SSL的目的是为了解决HTTP协议在不可信网络中使用明文传输数据导致的安全性问题。可以说,整个互联网的通信安全,都是建立在 SSL/TLS 的安全性之上的。

Protocol Year RFC Description
SSL1.0 1994 NetScape公司设计1.0版 但是未发布
SSL2.0 1995.02 NetScape公司发布SSL 2.0版
SSL3.0 1996 RFC 6101 NetScape公司发布SSL 3.0版
TLS 1.0 1999 RFC 2246 互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版。
TLS 1.1 2006.04 RFC 4346 发布TLS1.1版
TLS 1.2 2008.08 RFC 5246 发布TLS1.2版

2.1 SSL/TLS 协议及其握手过程

SSL/TLS 的基本运行过程如下:

  1. 客户端向服务端索要公钥并且进行验证
  2. 双方协商生成“对话密钥”
  3. 双方采用“对话密钥”进行加密通信

第一步中的这个公钥就是放在证书中的,防止篡改,只要证书是可信的,那么公钥也是可信的。

这里先说下,SSL/TLS协议是采用了非对称加密的加密思路,也就是说客户端通过用服务端传过来的公钥进行加密,然后服务端收到密文之后用自己的私钥进行解密。

那么第2步的“对话密钥”是什么呢,这里就涉及对称加密的思路了。因为非对称加密的缺点就是速度慢,所以在每次session中,客户端和服务端之间都会生成一个“对话密钥”(session key),用它来加密信息。因为对称加密的优点就是速度快,而服务器发来的公钥就只是用来加密“对话密钥”而已。

所以说SSL/TLS协议的设计中运用了大量的密码学原理和思想。

在这里顺便科普一下常见的加密算法:

  • 哈希(散列)加密
    • 哈希算法又称散列,它是一种将任意长度的数据转化为固定长度的算法
    • 哈希算法是不可逆的
    • 常见的哈希算法有 MD5 和 SHA1
  • 对称加密
    • 对称加密指的是加密和解密使用相同一个密钥
    • 对称加密的优点是速度快,缺点是密钥管理不方便,必须共享密钥
    • 常见的对称加密算法有 DES、AES、Blowfish 等
  • 非对称加密
    • 非对称加密指的是加密和解密使用不同的密钥,其中一个是公钥,另一个是私钥,公钥是公开的,私钥只有自己知道
    • 使用公钥加密的数据必须使用私钥解密,使用私钥加密的数据必须使用公钥解密
    • 公钥和私钥之间存在着某种联系,但是从公钥不能(或很难)推导出私钥
    • 非对称加密的缺点是速度慢,优点是密钥管理很方便
    • 常见的非对称加密算法有 RSA、ECC 等

至于具体的算法原理我暂时没有去深究。

这个过程中的前两步又称为“握手过程”。

说到握手,阮一峰先生的博客文章《图解SSL/TLS协议》中通过cloudFlare的几张生动的说明图很形象地描述了这个过程。具体可以看这篇文章哦。

这里从另外一篇参考文章中截取了一张图来说明:

第一步(ClientHello),客户端给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。

第二步(SeverHello),服务端确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。

第三步,客户端确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给服务端。

第四步,服务端使用自己的私钥,获取客户端发来的随机数(即Premaster secret)。

第五步,客户端和服务端根据约定的加密方法,使用前面的三个随机数,生成”对话密钥”(session key),用来加密接下来的整个对话过程。

第一步第二步打比方就是男女双方打招呼(say hello),告诉对方自己的一些基本情况,然后双方就开始尝试交往了。

如果有看我前一篇对应的博文的话,会发现我第一个报错,

    Unsupported record version SSLv2Hello
    javax.net.ssl.SSLException: Unsupported record version SSLv2Hello
    ...
    ...

就是第二步中,服务器确认使用的加密通信协议版本,发现客户端与服务器支持的版本不一致,服务器关闭加密通信导致的。

而第二个错,

javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
...
...
Caused by: java.io.EOFException: SSL peer shut down incorrectly
...
...

则应该是在第四步中服务器在获取客户端信息时,要验证客户端的证书,发现没有证书导致的。

这里补充讲一下双向认证和单向认证的概念:

单向认证指的就是这个过程中,只有客户端对服务端的证书进行认证,然后获取服务端传来的公钥。

而双向认证指的就是,不只客户端对服务端要进行认证,服务端也要对客户端进行认证,客户端也需要带着证书过来,让服务端获取到客户端的公钥。

详述一下握手阶段的3-5步:

  1. 服务器要求客户发送客户自己的证书。收到后,服务器验证客户的证书,如果没有通过验证,拒绝连接;如果通过验证,服务器获得用户的公钥。
  2. 客户浏览器告诉服务器自己所能够支持的通讯对称密码方案。
  3. 服务器从客户发送过来的密码方案中,选择一种加密程度最高的密码方案,用客户的公钥加过密后通知浏览器。
  4. 浏览器针对这个密码方案,选择一个通话密钥,接着用服务器的公钥加过密后发送给服务器。
  5. 服务器接收到浏览器送过来的消息,用自己的私钥解密,获得通话密钥。
  6. 服务器、浏览器接下来的通讯都是用对称密码方案,对称密钥是加过密的

证书

一个数字证书包含如下信息:

  • 证书的发布机构
  • 证书的有效期
  • 公钥
  • 证书所有者(Subject)
  • 签名所使用的算法
  • 指纹以及指纹算法

数字证书可以保证数字证书里的公钥确实是这个证书的所有者(Subject)的,或者证书可以用来确认对方的身份。也就是说,我们拿到一个数字证书,我们可以判断出这个数字证书到底是谁的。

数字证书的介绍可以详见《数字证书原理》这篇博文,写的十分详细。

如果一个网站需要支持 HTTPS ,它就要一份证书来证明自己的身份,而这个证书必须从 CA 机构申请,CA指的是Certificate Authority,证书授权中心。大多数情况下申请数字证书的价格都不菲,不过也有一些免费的证书供个人使用。从安全性的角度来说,免费的和收费的证书没有任何区别,都可以为你的网站提供足够高的安全性,唯一的区别在于如果你从权威机构购买了付费的证书,一旦由于证书安全问题导致经济损失,可以获得一笔巨额的赔偿。

这里在讲一下上一篇博文中提到的一个报错:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed

CA 证书可以具有层级结构,它建立了自上而下的信任链,下级 CA 信任上级 CA ,下级 CA 由上级 CA 颁发证书并认证。

浏览器在验证证书时,从根证书开始,沿着证书链的路径依次向下验证,根证书是整个证书链的安全之本,如果根证书被篡改,整个证书体系的安全将受到威胁。

根证书需要验证吗?Emm..根证书是自己验证自己,也就是不需要被验证,所以根证书是整个证书体系的安全之本。

在说回上面那个错误,PKIX,这是个什么东西呢?看下面:

PKI(Public Key Infrastructure)中文称作公钥基础设施,它提供公钥加密和数字签名服务的系统或平台,方便管理密钥和证书,从而建立起一个安全的网络环境。而数字证书最常见的格式是 X.509 ,所以这种公钥基础设施又称之为 PKIX 。

所以,上面那个报错的意思就是在沿着证书链的路径验证证书时出现异常,验证失败了。

Java中的证书

浏览器保存了一个常用的CA证书列表,在验证证书链的有效性时,直接使用保存的证书里的公钥进行校验,如果在证书列表中没有找到或者找到了但是校验不通过,那么浏览器会警告用户,由用户决定是否继续。与此类似的,操作系统也一样保存有一份可信的证书列表。

Java也是一样,在JRE的安装目录下可以找到相对应的证书目录$JRE/lib/security/cacerts,里面保存着一些常见的证书列表。可以使用JRE自带的keytool工具(后面再介绍)cacerts文件的默认密码为changeit。(change it…Emmmm, funny uhh.)

在Java平台下,证书常常被存储在KeyStore文件中,上面说的cacerts文件就是一个KeyStore文件,KeyStore不仅可以存储数字证书,还可以存储密钥,存储在KeyStore文件中的对象有三种类型Certificate、PrivateKey和SecretKey。Certificate就是证书,PrivateKey是非对称加密中的私钥,SecretKey用于对称加密,是对称加密中的密钥。KeyStore文件根据用途,也有很多种不同的格式:JKS、JCEKS、PKCS12、DKS等等,PixelsTech上有一系列文章对KeyStore有深入的介绍,可以学习下:Different types of keystore in Java 。

到目前为止,我们所说的KeyStore其实只是一种文件格式而已,实际上在Java的世界里KeyStore文件分成两种:KeyStore和TrustStore,这两个东西从文件格式来看其实是一样的。从字面意思上可以看出来差别:KeyStore保存私钥,用来加解密或者为别人做签名;TrustStore保存一些可信任的证书,访问HTTPS时对被访问者进行认证,以确保它是可信任的。所以准确来说,上面的cacerts文件应该叫做TrustStore而不是KeyStore,只是它的文件格式是KeyStore文件格式罢了。

在程序中怎么管理他们呢?

这就涉及到Java中的KeyManagerTrustManager

可以看出如果要进行SSL会话,必须得新建一个SSLSocket对象,而SSLSocket对象是通过SSLSocketFactory来管理的,SSLSocketFactory对象则依赖于SSLContextSSLContext对象又依赖于keyManagerTrustManagerSecureRandom

这几个对象在上篇博文中启用ssl会话的代码中都有体现。

小结

本篇文章中讲了一些我被卡在之前那几个bug的过程中,学到的一些知识。很感谢参考资料中的文章,都非常的优秀。

希望能够对你有所帮助。

参考资料

[1] https://www.cnblogs.com/UnGeek/p/6047843.html

[2] http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html

[3] http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html

[4] http://www.cnblogs.com/JeffreySun/archive/2010/06/24/1627247.html

[5] https://program-think.blogspot.com/2010/02/introduce-digital-certificate-and-ca.html#head-1

[6] http://www.aneasystone.com/archives/2016/04/java-and-https.html

你可能感兴趣的:(java开发笔记)