openssl双向认证 tomcat_【网络编程之认证请求】单双向认证的一个小demo

最近好好把密码学和网络请求相关部分学习了一下,特此记录一番,如果有讲错的地方,还忘大佬指出,勿让我误导他人,十分感谢。

我们知道,在网络请求应答中,我们需要确认对方的身份,只有服务器是可靠的,为了安全我们才继续请求以接受响应,相信很多爬虫工程师在请求的时候会遇到一个ssl错误。

比如我们来看一个demo

服务端代码

const https = require('https');const fs = require('fs');const options = {  key: fs.readFileSync('server-key.pem'),  cert: fs.readFileSync('server-cert.pem')};https.createServer(options, (req, res) => {  res.writeHead(200);  res.end('hello world\n');}).listen(8000);

客户端代码

import requestsdef main():    url = "https://127.0.0.1:8000"    res = requests.get(url)    print(res.text)if __name__ == '__main__':    main()

84f2c47bdf82fe958dbc206ebf8c5548.png

这个错误是由于服务器的证书是我自己签发的,而系统并不认。

我们去浏览器看看。

ac124392c77b90a02d19a0defc361182.png

需要我们手动点击高级后再请求相应的地址

ed822e3aed24aeefd7c2e8337883ad00.png

然后浏览器才会继续请求返回响应

或者我们在python里面我们可以将requests的verify参数设置为False以请求成功

fba8d6f48c1a1e0f1349d5677f5524b8.png

接下来就不得不说说这里面涉及到的证书

在上面的服务端里面有两个证书相关文件,一个是

lingzhiyi-server-key.pem

,另一个是

lingzhiyi-server-cert.pem

其实里面也就是一串字符串,可以用文本编辑器打开看看

fba107fdfd584eec31337d3c22151754.png

e96fe2d75eb216b57d6d21e1a6791db3.png

客户端发送请求的时候,首先服务端将证书发送过来,但是我们并没有公钥去解密并验证,所以会抛出上述的ssl证书verify错误

而为什么我们具体写爬虫代码的时候,很多时候不需要管证书,我想,我想是因为我们的系统里面内置了很多证书,证书里面有相关的公钥去解密

比如我们打开百度首页后(使用chrome),点击那把锁,可以看到如下信息

e752aeb1140405c0dc549e21f9732008.png

里面放了一个公钥,这个公钥就是用来验证服务器证书的,因为有这个内置证书,因此访问百度的时候是显示安全可靠。

右边显示百度的证书不是在第一层,说明它不是CA证书,也就是不是由百度自己签发的,从这里也能看出有一个证书链,而最顶层的叫做根证书。

接下来我们使用openssl这个库来构建我们的证书,然后用对服务端代码增加一些内容,以验证双向认证,即客户端检测服务端,服务端检测客户端

可以去这里下载windows的openssl

http://slproweb.com/products/Win32OpenSSL.html

安装后需要把bin路径加入到系统path环境变量,以便我们在任何地方都能够使用openssl命令

首先创建一个CA证书,用来给自己的客户端和服务端签发应用证书

openssl genrsa -out lingzhiyi-ca-key.pem -des 4096openssl req -new -key lingzhiyi-ca-key.pem -out lingzhiyi-ca-csr.pemopenssl x509 -req -days 3650 -in lingzhiyi-ca-csr.pem -signkey lingzhiyi-ca-key.pem -out lingzhiyi-ca-cert.pem

创建服务端证书,并使用CA证书签发

openssl genrsa -out lingzhiyi-server-key.pem 2048openssl req -new -key lingzhiyi-server-key.pem -config openssl.cnf -out lingzhiyi-server-csr.pemopenssl x509 -req -days 730 -CA lingzhiyi-ca-cert.pem -CAkey lingzhiyi-ca-key.pem -CAcreateserial -in lingzhiyi-server-csr.pem -out lingzhiyi-server-cert.pem -extensions v3_req  -extfile openssl.cnf

创建客户端证书,并使用CA证书签发

openssl genrsa -out lingzhiyi-client-key.pem 2048openssl req -new -key lingzhiyi-client-key.pem -config openssl.cnf -out lingzhiyi-client-csr.pemopenssl x509 -req -days 730 -CA lingzhiyi-ca-cert.pem -CAkey lingzhiyi-ca-key.pem -CAcreateserial -in lingzhiyi-client-csr.pem -out lingzhiyi-client-cert.pem -extensions v3_req  -extfile openssl.cnf

都成功创建完成后应该如下所示

2d899fc2291bb67ef2c5a5f1f0aebecc.png

openssl.cnf是一个配置文件,如果不借用它的话,需要注意一个地方

填Common Name (e.g. server FQDN or YOUR name)的时候,最好都填127.0.0.1,不然后面请求测试的时候还是通不过。

现在我们的服务端代码如下:

const https = require('https');const fs = require('fs');const options = {  key: fs.readFileSync('lingzhiyi-server-key.pem'),  cert: fs.readFileSync('lingzhiyi-server-cert.pem'),  // This is necessary only if using client certificate authentication.  requestCert: true,  rejectUnauthorized: false,  // This is necessary only if the client uses a self-signed certificate.  ca: [ fs.readFileSync('lingzhiyi-ca-cert.pem') ]};var server = https.createServer(options, function (req, res) {    console.log(req.client.authorized)    if (req.client.authorized) {        res.writeHead(200, {"Content-Type":"application/json"});        res.end('fine! successed');    } else {        console.log("res.connection.authroizationError: " + res.connection.authorizationError);        res.writeHead(403, {"Content-Type":"application/json"});        res.end('denied! your need send a cert');    }}).listen(8000);

服务端代码里面增加了requestCert字段,为true的话会表示需要客户端传证书,否则不被认证;rejectUnauthorized一般配合requestCert使用。

可以依次去尝试下传不同的参数返回的结果,客户端代码如下

```pythonimport requestsdef main():    url = "https://127.0.0.1:8000"    # res = requests.get(url)    # res = requests.get(url,verify=False)    # res = requests.get(url,cert=("lingzhiyi-client-cert.pem","lingzhiyi-client-key.pem"))    # res = requests.get(url,cert=("lingzhiyi-client-cert.pem","lingzhiyi-client-key.pem"),verify=False)    res = requests.get(url,cert=("lingzhiyi-client-cert.pem","lingzhiyi-client-key.pem"),verify="lingzhiyi-ca-cert.pem")    print(res.text)if __name__ == '__main__':    main()```

感觉还有很多东西想表达,但是限于能力,没表达出来,就先到这里吧,可能以上内容还有不少错误,希望大佬看到的话能不吝赐教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值