说起 HTTP 的那些事,则不得不提 HTTPS ,而说起 HTTPS ,则不得不提数字证书。这篇博客将从 Java 的角度,学习 HTTPS 和数字证书技术,并分享爬虫开发的过程中针对爬取 HTTPS 站点时可能遇到的一些问题。
在前面的几篇博客里,其实已经略微提到过 HTTPS 了,譬如使用 HttpsURLConnection 类发送 HTTPS 请求,在使用代理时 HTTP 和 HTTPS 的一些差异等等。关于 HTTPS 的概念就不废话了,下面直接进入正题。

一、访问 HTTPS 站点

在前面的第一篇博客《模拟 HTTP 请求》里,介绍了两种方法来模拟发送 HTTP 请求,访问 HTTP 站点。一种方式是通过 java.net 自带的HttpURLConnection,另一种方式是通过 Apache 的 HttpClient,这两种方式各有各的优势。这里也使用这两种方式来访问 HTTPS 站点,从下面的代码可以看到,和前面访问 HTTP 站点几乎完全一样。

1.1 使用 HttpURLConnection

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@Test

publicvoidbasicHttpsGet()throwsException {

String url = "https://www.xkwechat.com";

URL obj = newURL(url);

HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

con.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) ...");

con.setRequestProperty("Accept-Language","en-US,en;q=0.5");

con.setRequestMethod("GET");

String responseBody = readResponseBody(con.getInputStream());

System.out.println(responseBody);

}

1.2 使用 HttpClient

1

2

3

4

5

6

7

8

9

10

11

12

@Test

publicvoidbasicHttpsGet()throwsException {

String url = "https://www.xkwechat.com";

HttpGet request = newHttpGet(url);

request.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) ...");

CloseableHttpClient httpclient = HttpClients.createDefault();

CloseableHttpResponse response = httpclient.execute(request);

String responseBody = readResponseBody(response);

System.out.println(responseBody);

}

具体的代码解释参见第一篇博客,这里不再赘述。一般情况下,访问 HTTPS 站点就和访问 HTTP 站点一样简单,无论是 HttpURLConnection 还是 HttpClient ,都将底层的实现细节封装了起来,给我们提供了一致的对外接口,所以我们不用关心 HTTPS 的实现原理。对底层细节的封装,本来是一件好事,也是一种好的设计方式,可以让开发人员使用起来更方便,提高开发效率,但是对于那些不求甚解的人来说,可能带来的困惑比之带来的方便要更多。

1.3 遭遇 PKIX path building failed

使用上面的代码作为爬虫程序爬取成千上万的网页,在大多数情况下,无论是 HTTP 也好,HTTPS 也罢,都可以很好的工作。不过有时候,你可能没那么好的运气,有些站点在墙外,被强大的防火长城拒之门外,这时你可以找一些境外代理,通过《使用代理》这篇博客中介绍的方法来解决;有些站点需要使用身份认证输入用户名密码才能访问,这可以使用上一篇博客《代理认证》中介绍的方法来解决;另外,在访问有些 HTTPS 站点时,你还可能会遇到下面的异常:

javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

要解决这个异常,这就是我们这篇将要介绍的内容。

二、证书认证的原理

大多数人第一次遇到上面的异常时的反应,估计都是一脸茫然,因为这个异常信息提示比较模糊,对于不懂 HTTPS 的人来说,什么 SSLHandshake ,什么 PKIX path ,完全不知所云。所以我们要先弄懂 HTTPS 的工作原理,才好去解决这个问题。我们知道 HTTPS 其实就是 HTTP + SSL/TLS 的合体,它其实还是 HTTP 协议,只是在外面加了一层,SSL 是一种加密安全协议,引入 SSL 的目的是为了解决 HTTP 协议在不可信网络中使用明文传输数据导致的安全性问题。可以说,整个互联网的通信安全,都是建立在 SSL/TLS 的安全性之上的。

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

学过计算机网络的同学肯定都还记得 TCP 在建立连接时的三次握手,之所以需要 TCP 三次握手,是因为网络中存在延迟的重复分组,可能会导致服务器重复建立连接造成不必要的开销。SSL/TLS 协议在建立连接时与此类似,也需要客户端和服务器之间进行握手,但是其目的却大相径庭,在 SSL/TLS 握手的过程中,客户端和服务器彼此交换并验证证书,并协商出一个 “对话密钥” ,后续的所有通信都使用这个 “对话密钥” 进行加密,保证通信安全。

网上有很多 SSL/TLS 握手的示意图,其中下面这副非常全面,也非常专业,想深入了解 SSL/TLS 的同学可以研究下。

http://www.cheat-sheets.org/saved-copy/Ssl_handshake_with_two_way_authentication_with_certificates-1.pdf

阮一峰在他的 《SSL/TLS协议运行机制的概述》 和 《图解SSL/TLS协议》 两篇博客中详细介绍了 SSL/TLS 的原理,感兴趣的同学可以去看看。我这里使用 IBM Tivoli Risk Manager 用户手册 里的一张图(因为这张图比较浅显易懂)来大概的说明下我们在平时使用浏览器访问 HTTPS 站点时,中间发生的握手过程。

整个 SSL/TLS 的握手和通信过程,简单来说,其实可以分成下面三个阶段:

  1. 打招呼
  • 当用户通过浏览器访问 HTTPS 站点时,浏览器会向服务器打个招呼(ClientHello),服务器也会和浏览器打个招呼(ServerHello)。所谓的打招呼,实际上是告诉彼此各自的 SSL/TLS 版本号以及各自支持的加密算法等,让彼此有一个初步了解。
  1. 表明身份、验证身份
  • 第二步是整个过程中最复杂的一步,也是 HTTPS 通信中的关键。为了保证通信的安全,首先要保证我正在通信的人确实就是那个我想与之通信的人,服务器会发送一个证书来表明自己的身份,浏览器根据证书里的信息进行核实(为什么通过证书就可以证明身份呢?怎么通过证书来验证对方的身份呢?这个后面再说)。如果是双向认证的话,浏览器也会向服务器发送客户端证书。
  • 双方的身份都验证没问题之后,浏览器会和服务器协商出一个 “对话密钥” ,要注意这个 “对话密钥” 不能直接发给对方,而是要用一种只有对方才能懂的方式发给他,这样才能保证密钥不被别人截获(或者就算被截获了也看不懂)。
  1. 通信
  • 至此,握手就结束了。双方开始聊天,并通过 “对话密钥” 加密通信的数据。

握手的过程大致如此,我们现在已经了解到 HTTPS 通信需要进行一次握手,所以上面看到的 javax.net.ssl.SSLHandshakeException 这个异常,我们也不难理解,实际上也就是在 SSL/TLS 握手的过程中出现了问题。当然,这其中还有很多很多细节,下面继续。

2.2 HTTPS 中的密码学

HTTPS 协议之所以复杂,是为了保证通信过程中数据的安全性,而要保证通信安全,它在协议中运用了大量的密码学原理,可以说 HTTPS 是集密码学之大成。无论是在 SSL/TLS 握手的过程中,还是在加密通信的过程中,HTTPS 都涉及了大量的密码学概念,譬如,在证书的数字签名中使用了哈希算法和非对称加密算法,在加密通信的过程中使用了对称加密算法,为了防止传输的数据被篡改和重放还使用了 MAC(消息认证码)等。

要想深入了解 HTTPS 的工作原理,下面这些概念还是得好好研究下,网上已经有很多文章介绍这些概念了,我在这里总结一下。

  • 哈希

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

2.3 关于证书

简单来说,数字证书就好比介绍信上的公章,有了它,就可以证明这份介绍信确实是由某个公司发出的,而证书可以用来证明任何一个东西的身份,只要这个东西能出示一份证明自己身份的证书即可,譬如可以用来验证某个网站的身份,可以验证某个文件是否可信等等。《数字证书及 CA 的扫盲介绍》 和 《数字证书原理》 这篇博客对数字证书进行了很通俗的介绍。

知道了证书是什么之后,我们往往更关心它的原理,在上面介绍 SSL/TLS 握手的时候留了两个问题:为什么通过证书就可以证明身份呢?怎么通过证书来验证对方的身份呢?

这就要用到上面所说的非对称加密了,非对称加密的一个重要特点是:使用公钥加密的数据必须使用私钥才能解密,同样的,使用私钥加密的数据必须使用公钥解密。正是因为这个特点,网站就可以在自己的证书中公开自己的公钥,并使用自己的私钥将自己的身份信息进行加密一起公开出来,这段被私钥加密的信息就是证书的数字签名,浏览器在获取到证书之后,通过证书里的公钥对签名进行解密,如果能成功解密,则说明证书确实是由这个网站发布的,因为只有这个网站知道他自己的私钥(如果他的私钥没有泄露的话)。

在非对称加密算法中,最出众的莫过于 RSA 算法,关于 RSA 算法的数学细节,可以参考阮一峰的《RSA算法原理(一)》和《RSA算法原理(二)》这两篇博客,强烈推荐。

当然,如果只是简单的对数字签名进行校验的话,还不能完全保证这个证书确实就是网站所有,黑客完全可以在中间进行劫持,使用自己的私钥对网站身份信息进行加密,并将证书中的公钥替换成自己的公钥,这样浏览器同样可以解密数字签名,签名中身份信息也是完全合法的。这就好比那些地摊上伪造公章的小贩,他们可以伪造出和真正的公章完全一样的出来以假乱真。为了解决这个问题,信息安全的专家们引入了 CA 这个东西,所谓 CA ,全称为 Certificate Authority ,翻译成中文就是证书授权中心,它是专门负责管理和签发证书的第三方机构。因为证书颁发机构关系到所有互联网通信的身份安全,因此一定要是一个非常权威的机构,像 GeoTrust、GlobalSign 等等,这里有一份常见的 CA 清单。如果一个网站需要支持 HTTPS ,它就要一份证书来证明自己的身份,而这个证书必须从 CA 机构申请,大多数情况下申请数字证书的价格都不菲,不过也有一些免费的证书供个人使用,像最近比较火的 Let's Encrypt 。从安全性的角度来说,免费的和收费的证书没有任何区别,都可以为你的网站提供足够高的安全性,唯一的区别在于如果你从权威机构购买了付费的证书,一旦由于证书安全问题导致经济损失,可以获得一笔巨额的赔偿。

如果用户想得到一份属于自己的证书,他应先向 CA 提出申请。在 CA 判明申请者的身份后,便为他分配一个公钥,并且 CA 将该公钥与申请者的身份信息绑在一起,并为之签字后,便形成证书发给申请者。如果一个用户想鉴别另一个证书的真伪,他就用 CA 的公钥对那个证书上的签字进行验证,一旦验证通过,该证书就被认为是有效的。通过这种方式,黑客就不能简单的修改证书中的公钥了,因为现在公钥有了 CA 的数字签名,由 CA 来证明公钥的有效性,不能轻易被篡改,而黑客自己的公钥是很难被 CA 认可的,所以我们无需担心证书被篡改的问题了。

下图显示了证书的申请流程:

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

可以看出:google.com.hk 的 SSL 证书由 Google Internet Authority G2 这个 CA 来验证,而 Google Internet Authority G2 由 GeoTrust Global CA 来验证,GeoTrust Global CA 由 Equifax Secure Certificate Authority 来验证。这个最顶部的证书,我们称之为根证书(root certificate),那么谁来验证根证书呢?答案是它自己,根证书自己证明自己,换句话来说也就是根证书是不需要证明的。浏览器在验证证书时,从根证书开始,沿着证书链的路径依次向下验证,根证书是整个证书链的安全之本,如果根证书被篡改,整个证书体系的安全将受到威胁。所以不要轻易的相信根证书,当下次你访问某个网站遇到提示说,请安装我们的根证书,它可以让你访问我们网站的体验更流畅通信更安全时,最好留个心眼。在安装之前,不妨看看这几篇博客:《12306的证书问题》、《在线买火车票为什么要安装根证书?》。

最后总结一下,其实上面说的这些,什么非对称加密,数字签名,CA 机构,根证书等等,其实都是 PKI 的核心概念。PKI(Public Key Infrastructure)中文称作公钥基础设施,它提供公钥加密和数字签名服务的系统或平台,方便管理密钥和证书,从而建立起一个安全的网络环境。而数字证书最常见的格式是 X.509 ,所以这种公钥基础设施又称之为 PKIX 。

至此,我们大致弄懂了上面的异常信息,sun.security.validator.ValidatorException: PKIX path building failed,也就是在沿着证书链的路径验证证书时出现异常,验证失败了。

讲了这么多,全都是些理论的东西,下面开始实践吧,看看怎么解决这个异常。

2.4 关于 Java 里的证书

上面所介绍的是浏览器对证书进行验证的过程,浏览器保存了一个常用的 CA 证书列表,在验证证书链的有效性时,直接使用保存的证书里的公钥进行校验,如果在证书列表中没有找到或者找到了但是校验不通过,那么浏览器会警告用户,由用户决定是否继续。与此类似的,操作系统也一样保存有一份可信的证书列表,譬如在 Windows 系统下,你可以运行 certmgr.msc 打开证书管理器查看,这些证书实际上是存储在 Windows 的注册表中,一般情况下位于:\SOFTWARE\Microsoft\SystemCertificates\ 路径下。那么在 Java 程序中是如何验证证书的呢?

和浏览器和操作系统类似,Java 在 JRE 的安装目录下也保存了一份默认可信的证书列表,这个列表一般是保存在$JRE/lib/security/cacerts 文件中。要查看这个文件,可以使用类似 KeyStore Explorer 这样的软件,当然也可以使用 JRE 自带的 keytool 工具(后面再介绍),cacerts 文件的默认密码为 changeit (但是我保证,大多数人都不会 change it)。

我们知道,证书有很多种不同的存储格式,譬如 CA 在发布证书时,常常使用 PEM 格式,这种格式的好处是纯文本,内容是 BASE64 编码的,证书中使用 "-----BEGIN CERTIFICATE-----" 和 "-----END CERTIFICATE-----" 来标识。另外还有比较常用的二进制 DER 格式,在 Windows 平台上较常使用的 PKCS#12 格式等等。当然,不同格式的证书之间是可以相互转换的,我们可以使用 openssl 这个命令行工具来转换,参考 SSL Converter ,另外,想了解更多证书格式的,可以参考这里:Various SSL/TLS Certificate File Types/Extensions 。

在 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 文件格式罢了。

除了 KeyStore 和 TrustStore ,Java 里还有两个类 KeyManager 和 TrustManager 与此息息相关。JSSE 的参考手册中有一张示意图,说明了各个类之间的关系:

可以看出如果要进行 SSL 会话,必须得新建一个 SSLSocket 对象,而 SSLSocket 对象是通过 SSLSocketFactory 来管理的,SSLSocketFactory 对象则依赖于 SSLContext ,SSLContext 对象又依赖于 keyManagerTrustManager 和 SecureRandom。我们这里最关心的是 TrustManager 对象,另外两个暂且忽略,因为正是 TrustManager 负责证书的校验,对网站进行认证,要想在访问 HTTPS 时通过认证,不报 sun.security.validator.ValidatorException 异常,必须从这里开刀。

三、自定义 TrustManager 绕过证书检查

我们知道了 TrustManager 是专门负责校验证书的,那么最容易想到的方法应该就是改写 TrustManager 类,让它不要对证书做校验,这种方法虽然粗暴,但是却相当有效,而且 Java 中的 TrustManager 也确实可以被重写,下面是示例代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

@Test

publicvoidbasicHttpsGetIgnoreCertificateValidation()throwsException {

String url = "https://kyfw.12306.cn/otn/";

// Create a trust manager that does not validate certificate chains

TrustManager[] trustAllCerts = newTrustManager[] {

newX509TrustManager() {

publicX509Certificate[] getAcceptedIssuers() {

returnnull;

}

publicvoidcheckClientTrusted(X509Certificate[] certs, String authType) {

// don't check

}

publicvoidcheckServerTrusted(X509Certificate[] certs, String authType) {

// don't check

}

}

};

SSLContext ctx = SSLContext.getInstance("TLS");

ctx.init(null, trustAllCerts, null);

LayeredConnectionSocketFactory sslSocketFactory = newSSLConnectionSocketFactory(ctx);

CloseableHttpClient httpclient = HttpClients.custom()

.setSSLSocketFactory(sslSocketFactory)

.build();

HttpGet request = newHttpGet(url);

request.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) ...");

CloseableHttpResponse response = httpclient.execute(request);

String responseBody = readResponseBody(response);

System.out.println(responseBody);

}

我们新建了一个匿名类,继承自 X509TrustManager 接口,这个接口提供了三个方法用于验证证书的有效性:getAcceptedIssuerscheckClientTrustedcheckServerTrusted,我们在验证的函数中直接返回,不做任何校验,这样在访问 HTTPS 站点时,就算是证书不可信,也不会抛出异常,可以继续执行下去。

这种方法虽然简单,但是却有一个最严重的问题,就是不安全。因为不对证书做任何合法性校验,而且这种处理是全局性的,不管青红皂白,所有的证书都不会做验证,所以就算遇到不信任的证书,代码依然会继续与之通信,至于通信的数据安全不安全就不能保证了。所以如果你只是想在测试环境做个实验,那没问题,但是如果你要将代码发布到生产环境,请慎重。

四、使用证书

对于有些证书,我们基本上确定是可以信任的,但是这些证书又不在 Java 的 cacerts 文件中,譬如 12306 网站,或者使用了 Let's Encrypt 证书的一些网站,对于这些网站,我们可以将其添加到信任列表中,而不是使用上面的方法统统都相信,这样程序的安全性仍然可以得到保障。

4.1 使用 keytool 导入证书

简单的做法是将这些网站的证书导入到 cacerts 文件中,这样 Java 程序在校验证书的时候就可以从 cacerts 文件中找到并成功校验这个证书了。上面我们介绍过 JRE 自带的 keytool 这个工具,这个工具小巧而强悍,拥有很多功能。首先我们可以使用它查看 KeyStore 文件,使用下面的命令可以列出 KeyStore 文件中的所有内容(包括证书、私钥等):

$ keytool -list -keystore cacerts

然后通过下面的命令,将证书导入到 cacerts 文件中:

$ keytool -import -alias 12306 -keystore cacerts -file 12306.cer

要想将网站的证书导入 cacerts 文件中,首先要获取网站的证书,譬如上面命令中的 12306.cer 文件,它是使用浏览器的证书导出向导保存的。如下图所示:

关于 keytool 的更多用法,可以参考 keytool 的官网手册,SSLShopper 上也有一篇文章列出了常用的 keytool 命令。

4.2 使用 KeyStore 动态加载证书

使用 keytool 导入证书,这种方法不仅简单,而且保证了代码的安全性,最关键的是代码不用做任何修改。所以我比较推荐这种方法。但是这种方法有一个致命的缺陷,那就是你需要修改 JRE 目录下的文件,如果你的程序只是在自己的电脑上运行,那倒没什么,可如果你的程序要部署在其他人的电脑上或者公司的服务器上,而你没有权限修改 JRE 目录下的文件,这该怎么办?如果你的程序是一个分布式的程序要部署在成百上千台机器上,难道还得修改每台机器的 JRE 文件吗?好在我们还有另一种通过编程的手段来实现的思路,在代码中动态的加载 KeyStore 文件来完成证书的校验,抱着知其然知其所以然的态度,我们在最后也实践下这种方法。通过编写代码可以更深刻的了解KeyStoreTrustManagerFactorySSLContext 以及 SSLSocketFactory 这几个类之间的关系。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

@Test

publicvoidbasicHttpsGetUsingSslSocketFactory()throwsException {

String keyStoreFile = "D:\\somnus\\cc.ks";

String password = "somnus";

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

FileInputStream in = newFileInputStream(keyStoreFile);

ks.load(in, password.toCharArray());

System.out.println(KeyStore.getDefaultType().toString());

System.out.println(TrustManagerFactory.getDefaultAlgorithm().toString());

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmf.init(ks);

SSLContext ctx = SSLContext.getInstance("TLS");

ctx.init(null, tmf.getTrustManagers(), null);

LayeredConnectionSocketFactory sslSocketFactory = newSSLConnectionSocketFactory(ctx);

String url = "https://somnus.com";

/**

* Return the page with content:

*  401 Authorization Required

*/

CloseableHttpClient httpclient = HttpClients.custom()

.setSSLSocketFactory(sslSocketFactory)

.build();

HttpGet request = newHttpGet(url);

request.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) ...");

CloseableHttpResponse response = httpclient.execute(request);

String responseBody = readResponseBody(response);

System.out.println(responseBody);

}

上面的代码使用了 HttpClient ,如果是使用 HttpsURLConnection 只需要改动下面两行即可:

1

2

HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

con.setSSLSocketFactory(ctx.getSocketFactory());

最后的最后,我们还可以通过下面的属性来指定 trustStore ,这样也不需要编写像上面那样大量繁琐的代码,另外,参考我前面的博客,这些属性还可以通过 JVM 的参数来设置。

1

2

System.setProperty("javax.net.ssl.trustStore","D:\\somnus\\cc.ks");

System.setProperty("javax.net.ssl.trustStorePassword","somnus");

小结

至此,我们的 HTTPS 之旅就要告一段落了。在学习 HTTPS 的过程中,我时时不经意的会被 HTTPS 中的一些技术或技巧感触到,特别是证书的认证过程以及非对称加密算法的原理,真的是积累了人类无穷的智慧,让人不得不感叹数学的美妙。而在学习过程中越是刨根问底,越是一发不可收拾,中间牵扯到的细节太多,太深入反而让人不自觉的迷失了方向。

Java 和 HTTP 的那些事(四) HTTPS 和 证书相关相关推荐

  1. Java 和 HTTP 的那些事(四) HTTPS 和 证书

    Java 和 HTTP 的那些事(四) HTTPS 和 证书 说起 HTTP 的那些事,则不得不提 HTTPS ,而说起 HTTPS ,则不得不提数字证书.这篇博客将从 Java 的角度,学习 HTT ...

  2. Java开发微信公众号(四)---微信服务器post消息体的接收及消息的处理

    在前几节文章中我们讲述了微信公众号环境的搭建.如何接入微信公众平台.以及微信服务器请求消息,响应消息,事件消息以及工具处理类的封装:接下来我们重点说一下-微信服务器post消息体的接收及消息的处理,这 ...

  3. 基于Java语言构建区块链(四)—— 交易(UTXO)

    基于Java语言构建区块链(四)-- 交易(UTXO) 2018年03月11日 00:48:01 wangwei_hz 阅读数:909 标签: 区块链比特币 更多 个人分类: 区块链 文章的主要思想和 ...

  4. Java图书管理系统练习程序(四)

    2019独角兽企业重金招聘Python工程师标准>>> Java图书管理系统练习程序(四) 本部分主要介绍List的基本操作与Java中泛型的使用. 一.Java中泛型的使用 泛型, ...

  5. java多线程做一件事_关于Java的十件事

    java多线程做一件事 那么,您从一开始就一直在使用Java? 还记得那些被称为" Oak"的日子,OO仍然是热门话题,C ++人士认为Java没有机会,Applet还是一件事吗? ...

  6. java你可能不知道的事(2)--堆和栈

    为什么80%的码农都做不了架构师?>>>    在java语言的学习和使用当中你可能已经了解或者知道堆和栈,但是你可能没有完全的理解它们.今天我们就一起来学习堆.栈的特点以及它们的区 ...

  7. SharePoint咨询师之路:设计之前的那些事四:负载均衡 - web服务器

    SharePoint咨询师之路:设计之前的那些事四:负载均衡 - web服务器 提示:本系列只是一个学习笔记系列,大部分内容都可以从微软官方网站找到,本人只是按照自己的学习路径来学习和呈现这些知识.有 ...

  8. 教老婆学java系列之奇妙的数据结构四

    教老婆学java系列之奇妙的数据结构四 数据结构最后一节 思考题:后台处理一件事,耗时较长,怎么将信息显示在页面上 问题解析: 1.前端触发,后端处理时间较长,前端不能等处理完成. 2.如果不等处理完 ...

  9. 【手把手带你学Java EE】多线程那些事,你了解了吗?

    [手把手带你学Java EE]多线程那些事,你了解了吗? 线程 概念 意义 进程和线程的区别 面试题:谈谈进程和线程的区别和联系 Java中的多线程编程 创建线程的方法 方法一 方法二 方法三 &am ...

最新文章

  1. centos7 network-manager 与 interfaces 冲突
  2. plsql查看表空间_最佳实践—PG数据库系统表空间重建
  3. Matlab实现二进制矩阵转换为十进制
  4. SAP UI5 router的初始化逻辑
  5. 一句话讲明白 WebAssembly、微前端等技术背后的核心
  6. 为什么很多人在自媒体转不到钱?
  7. 中国卷绕装置市场趋势报告、技术动态创新及市场预测
  8. Django学习系列之ModelForm
  9. [python]python生成md5
  10. 搭建ASP环境-win7安装IIS并运行ASP程序
  11. matlab pi控制编程,增量式PI控制算法的MATLAB仿真.doc
  12. CV笔记5:图像分割之最大类间方差法、自适应阈值分割(基于python-opencv实现)
  13. Frame Relay - 简单介绍及基本配置
  14. springboot2+shiro 重写filter接口来调用自定义ream的登录校验方式
  15. 计算机控制系统中a d是什么,计算机控制系统中,()代表模拟量输出。A、AIB、AOC、DID、DO - 试题答案网问答...
  16. 在到达胜利之前无法回头
  17. [SRv6]《SRv6网络编程》SRv6网络在电信云中的应用
  18. 总结论文中正负样本的选择
  19. 【FPGA】D触发器的设计并调用D触发器设计4位串行移位寄存器
  20. 一个简单的后台管理系统

热门文章

  1. 干货分享,招银提前批:一面+二面+HR面面经,顺便聊聊程序员应该去银行上班吗?
  2. 我用Python摆摊算命
  3. 移动端扫描车牌识别,新能源车牌OCR识别已研发
  4. JavaScript基本的的语法规则
  5. 如何运营好一个微社区?热门手游社区助力游戏玩家互动
  6. 低功耗蓝牙模块带屏蔽罩的作用有哪些
  7. 不可思议的《魔兽世界》
  8. 使用ul li和不适用ul li的区别
  9. Linux perf sched Summary
  10. win10系统改变文本的默认打开方式