完成HTTPS请求的主体

  • 首先是服务端(server),要生成证书请求(csr),提交给CA(Certificate Authority),即证书授权中心,获得一张证书。这个证书里面包括了服务端的公钥,CA使用其私钥对服务端的公钥进行加密后得到的签名。
  • 然后是证书授权中心(CA),负责接收证书请求(包含请求主体的主体信息、公钥和签名算法),使用自己的私钥对请求中的信息进行加密签名。此前CA需要先给自己颁发一个根证书,下面还要用来给其他主体的证书请求颁发证书。
  • 最后是客户端(client),在访问HTTPS网站时,收到了服务端发来的证书,此时客户端要向CA查询这张证书是否合法,从而决定是否与此网站通信。这是就要用CA的公钥来解密这张证书中的数字签名,看是否与证书的明文部分一致。这意味着CA的根证书需要保存在浏览器中,各大浏览器开发商一般都会把根证书内置在浏览器中
openssl基础命令

openssl genrsa命令:
openssl req 命令:
openssl x509 命令:

openssl模拟该过程
  • openssl安装
$ sudo apt-get install openssl
$ sudo apt-get install libssl-dev
  • 生成CA根证书
  • 创建私钥:
    $ openssl genrsa -out ca-key.pem 1024
  • 创建 csr 证书请求
    $ openssl req -new -key ca-key.pem -out ca-req.csr -subj "/C=CN/ST=BJ/L=BJ/O=BD/OU=BD/CN=CS"
  • 生成根证书(用自己(CA)的私钥来签名):
    $ openssl x509 -req -in ca-req.csr -out ca-cert.pem -signkey ca-key.pem -days 3650
  • 生成服务端证书
  • 创建服务端私钥:
    $ openssl genrsa -out server-key.pem 1024
  • 创建csr证书请求:
    $ openssl req -new -out server-req.csr -key server-key.pem -subj "/C=CN/ST=SH/L=SH/O=TM/OU=TM/CN=CS"
  • 生成证书(用根证书和CA的私钥来签名):
    $ openssl x509 -req -in server-req.csr -out server-cert.pem -signkey server-key.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -days 3650
  • 确认证书
    openssl verify -CAfile ca-cert.pem server-cert.pem
基于Python的简单HTTPS服务

调用Python的http,ssl库,实现简单的HTTPS服务,这里提供服务端的证书与私钥即可。这里的CA证书是我们本地生成的,浏览器不会信任,访问时点击允许访问不安全的网站即可。若要避免这一问题,需要在网上购买权威CA的服务,生成被浏览器信任的证书。

下面的小程序实现了GET请求的处理,将调用其他Python程序来进一步处理请求。

#-*-coding:utf-8-*-
from http import server
from http.server import BaseHTTPRequestHandler  
import socket  
import ssl  
import sys
import subprocess
import os

class RequestHandler(BaseHTTPRequestHandler):
	def send_content(self, page, status = 200):
		self.send_response(status)
		self.send_header("Content-type", "text/html")
		self.send_header("Content-Length", str(len(page)))
		self.end_headers()
		# print(type(page))
		if type(page) == type('a'):
			self.wfile.write(bytes(page, encoding = 'utf-8'))
		elif type(page) == type(b'1'):
			self.wfile.write(page)
		print("response: ",page)

	def runpy(self, path, query):
		try:
			res = subprocess.check_output("python %s %s"%(path, query),
										stderr = subprocess.STDOUT,
										shell = True)
			self.send_content(res)
		except subprocess.CalledProcessError as exc:
			res = "returncode: %r" % exc.returncode
			res += "cmd: %s" % exc.cmd
			res += "output: %s" % exc.output
			self.send_content(res)

	def do_GET(self):
		print("self.path",self.path)
		if "?" in self.path:
			path, query = self.path.split('?')
			self.full_path = os.getcwd() + path
			if (os.path.isfile(self.full_path) and self.full_path.endswith('.py')):
				print("runing %s"%path.lstrip('/'))
				self.runpy(path.lstrip('/'), query)
			else:
				print("%s not exist"%path)
				self.send_content("%s not exist"%path)
		else:
			self.send_content("<h1 align='center'>Hello, World</h1>")

port = 443
try:
	httpd = server.HTTPServer(("0.0.0.0", port),RequestHandler)
	print("Listening on 0.0.0.0:443")
except:
	port = 8443
	httpd = server.HTTPServer(("0.0.0.0", port),RequestHandler)
	print("Listening on 0.0.0.0:8443")

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain("server-cert.pem","server-key.pem") #自己添加
httpd.socket = context.wrap_socket(httpd.socket, server_side = True)

try:
	print("HTTTPS Server listening on 0.0.0.0:%d"%port)
	httpd.serve_forever()
except KeyboardInterrupt:
	print("User quit.")
	exit()