python Tornado https openssl SSL Error: Unable to verify the first certificate

前几天用 Tornado 框架写了一个服务, 微信小程序需要去连接它. http很简单
但是微信小程序强制要求用到https 悲剧就开始了.
https 证书是从阿里云上下载下来的, crt证书
我的证书是从阿里云下载的免费证书, Tornado 支持的好像是apache的证书 crt结尾的.

理论上证书应该没什么问题. 搞上去以后发现用浏览器去访问的时候 https能正常访问的但是postman和微信小程序开发工具不支持.
我的错误代码如下

#完整打分流程V2
import os
import os.path
# import sys
# sys.path.append('../') 

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.autoreload
import cv2 
import json 



from handlers import OnlineRecognition, XiuZheng #这里的AA是文件名 

# import handlers

from tornado.options import define, options 

# import ssl

# ctx = ssl.SSLContext()
# ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
# ctx.options &= ~ssl.OP_NO_TLSv1_1
# ctx.options &= ~ssl.OP_NO_TLSv1_2
# ctx.options &= ~ssl.OP_NO_SSLv2
# ctx.options &= ~ssl.OP_NO_SSLv3

# ctx.options &= ~ssl.OP_NO_SSLv3
# ctx.options &= ~ssl.OP_NO_SSLv3
# ctx.options = ssl.OP_ALL
# ctx.options |= ssl.OP_NO_TLSv1_1
# ctx.options = ssl.OP_NO_TLSv1_2
# ctx.options |= ssl.OP_NO_SSLv2
# ctx.options |= ssl.OP_NO_SSLv3
import ssl
# context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
# context.load_cert_chain('PATH_TO_PUBLIC_KEY','PATH_TO_PRIVATE_KEY')

# print(context.options)
# 定义端口用于指定HTTP服务监听的端口
# 如果命令行中带有port同名参数则会称为全局tornado.options的属性,若没有则使用define定义。
define("port", type=int, default=443, help="run on the given port")
# 调试模式
define("debug", type=bool, default=True, help="debug mode")


# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug start
import platform
if platform.system() == "Windows":
    import asyncio
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug  end
 
static_path = os.path.join(os.path.dirname(__file__), 'static')
static_path = static_path.replace("\\","/")

crtpath =  os.getcwd()+ "/https/**.**.com_public.crt"
keypath =  os.getcwd()+ "/https/**.**.com.key"
#print(crtpath,keypath)
if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[ 
            (r'/', OnlineRecognition.OnlineRecognitionHandler)
         ],
        template_path=os.path.join(os.path.dirname(__file__), "templates"),
        settings = {
      'static_path' : static_path ,'debug' : True, "compiled_template_cache":False },
        static_path =static_path
    )

    ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) # 这里自己控制ssl
    # ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) #这样初始化强制使用ssl的加密版本是TLSV1_2 , 结果试了也不行.
    ssl_ctx.load_cert_chain(crtpath, keypath) #自己控制ssl的选项

    http_server = tornado.httpserver.HTTPServer(app,
        ssl_options = ssl_ctx
        # ssl_options={
     
        #    "certfile": crtpath,
        #    "keyfile": keypath
        # }
        )
    print("web 服务正在监听",options.port)
    http_server.listen(options.port)
    print("web 服务已启动...")
    
    # tornado.ioloop.IOLoop.instance().start()
    instance = tornado.ioloop.IOLoop.instance()
    tornado.autoreload.start(5)
    instance.start()
  

但是用postman和微信小程序开发工具就提示错误. 我就郁闷了…
提示报错如下

SSL Error: Unable to verify the first certificate

openssl s_client -connect www.abaefew.com:443   #这里是我的域名.

返回错误

python Tornado https openssl SSL Error: Unable to verify the first certificate_第1张图片
从图上也可看的出我服务器上用的加密版本是TLSV1.3

后来查看了下交换信息, 感觉应该是第4,5,6这几步可能出错了.

但是不知道为什么…云里雾里的.

后来搜到了这篇文章
https://www.cnblogs.com/Kempff/p/12631172.html
看他的代码,里面用到了第三个证书, 于是我突然间想到我从阿里云上下载的是3个文件呢.
一个叫 chain.crt 好像一直没用过. 于是我就用它试了一下…
居然神奇的成功了.
python Tornado https openssl SSL Error: Unable to verify the first certificate_第2张图片
..compublic.crt是公钥, 应该是第二步发给客户端的证书.
..com.key应该是秘钥, 用来解密的. 要保存好.
..com_chain.crt应该是根证书, 估计跟阿里云有关系吧… 瞎猜的, 有知道的麻烦说一下.

然后我的代码改成下面这样就可以了.
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=chainpath)
cafile参数必须指向 ..com_chain.crt 也叫CA证书

#完整打分流程V2
import os
import os.path
# import sys
# sys.path.append('../') 

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.autoreload
import cv2 
import json 
import ssl 

from handlers import OnlineRecognition, XiuZheng #这里的AA是文件名 
 
from tornado.options import define, options 
 

# print(context.options)
# 定义端口用于指定HTTP服务监听的端口
# 如果命令行中带有port同名参数则会称为全局tornado.options的属性,若没有则使用define定义。
define("port", type=int, default=443, help="run on the given port")
# 调试模式
define("debug", type=bool, default=True, help="debug mode")


# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug start
import platform
if platform.system() == "Windows":
    import asyncio
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# windows 系统下 tornado 使用 使用 SelectorEventLoop 解决一个bug  end
 
static_path = os.path.join(os.path.dirname(__file__), 'static')
static_path = static_path.replace("\\","/")

chainpath =  os.getcwd()+ "/https/ai.px82.com_chain.crt"
crtpath =  os.getcwd()+ "/https/ai.px82.com_public.crt"
keypath =  os.getcwd()+ "/https/ai.px82.com.key"
#print(crtpath,keypath)
if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[ 
            (r'/', OnlineRecognition.OnlineRecognitionHandler), 
            (r'/OnlineRecognition', OnlineRecognition.OnlineRecognitionHandler), 
            (r'/XiuZheng', XiuZheng.XiuZhengHandler),#修整AI算法的识别名称
         ],
        template_path=os.path.join(os.path.dirname(__file__), "templates"),
        settings = {
      'static_path' : static_path ,'debug' : True, "compiled_template_cache":False },
        static_path =static_path
    )
	# cafile 这个参数是必须得. 网上的都没有提起过.
    ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=chainpath) 
#    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) #这种是强制使用TLSV1.2协议
#    ssl_ctx.verify_mode = ssl.CERT_NONE 不管客户端怎样都不断开连接,  好像没啥用.
#    ssl_ctx.check_hostname =False 不做域名一致验证
    ssl_ctx.load_cert_chain(crtpath, keypath)

    http_server = tornado.httpserver.HTTPServer(app,
        ssl_options = ssl_ctx #下面的这种写法不能用. 
        # ssl_options={
     
        #    "certfile": crtpath,
        #    "keyfile": keypath
        # }
        )
    print("web 服务正在监听",options.port)
    http_server.listen(options.port)
    print("web 服务已启动...")
    
    # tornado.ioloop.IOLoop.instance().start()
    instance = tornado.ioloop.IOLoop.instance()
    tornado.autoreload.start(5)
    instance.start()
  

总结一下, 对ssl加密这套流程不是很懂. 导致的一个失误, 网上给的教程里面也多数没有写关于这个的说明.

你可能感兴趣的:(python)