近期在研究ssl,主要的开发工具是python。期间走了不少弯路。现在这里总结一下。

在python上使用SSL有许多场景,我主要关注的是使用python访问HTTPS资源,以及使用python提供HTTPS服务。(HTTPS是SSL在WEB上的应用之一)

一、使用python访问HTTPS网站

这应该算是最简单也是最常见的场景了。我们使用python做为客户端去访问公网上的网站,而这个网站为了传输安全(避免被劫持或者窃听)使用了HTTPS服务,传输过程内容都经过了SSL加密。下面来看下具体的python代码,这里使用的是python2.7.11,用的是python自带的urllib2。当然你也可以使用requests或者pycurl等第三方库,都可以,原理都是一样的,只是实现的手段有些差异而已。

import

urllib2

import

ssl

if

__name__ == '__main__':

myurl="https://www.baidu.com"

req

= urllib2.Request(myurl)

try:

response = urllib2.urlopen(req)

print "HTTP return code:%d" % response.getcode()

strResult= response.read()

print strResult

except Exception ,ex:

print "Found Error :%s" % str(ex)

运行它,我们就可以得到这个网站上的内容了。并且传输过程都经过了加密。是不是很简单。整个的传输过程大概是这样的,客户端请求SSL连接握手(其中会跟服务器有些SSL协议之间的交互,这个我们不用详细去研究)服务器把自己的证书传给客户端,客户端对这个证书进行认证(每台客户端电脑上都会默认安装一些权威CA(证书签发机构)的证书,这个认证就是使用这些证书进行的,python的ssl模块会自动加载这些权威CA证书,当然你也可以自己指定,这个后面会谈到),确保这个证书是由可信的CA颁布的(客户端对服务端证书进行认证这个操作,是从python2.7.9开始才有的,以前版本的是不进行认证的),之后就是利用证书交换密钥后,使用密钥对传输内容进行加密传输。

互联网上大部分的正规网站都是会有自己的证书的,这些证书都是经过权威CA认证的,可以被认为是可信的(其实这个也不一定,前段时间就有过谷歌封杀赛门铁克CA签发的证书的事件,有兴趣的可以去网上搜索看看)。但是同样也有不少网站,由于各种各样的原因没有这些经过权威CA认证的证书,毕竟申请这些证书流程比较麻烦,并且要支付一定的费用。这些网站也可以提供安全的HTTPS服务,但由于他们的证书是自签名的或者是由非权威的CA颁发的(这两种证书后面会谈到),所以会被认为是不可信的。当使用浏览器打开这些网站时,浏览器会提醒你这是不安全的连接,当然这个不安全是指网站未经权威认证,所以网站本身可能不安全,但在网络传输层面上来看,传输是安全的。那我们访问这些网站时,使用上面的代码会有错误提示,下面代码就是访问一个这是我自己建立的一个HTTPS网站https://127.0.0.1:8443,该网站使用的是自签名的证书。如果你使用谷歌浏览器访问会提示你:您的连接不是私密连接,如果你强制连接则会提示不安全

import

urllib2

import

ssl

if

__name__ == '__main__':

myurl="https://127.0.0.1:8443"

req

= urllib2.Request(myurl)

try:

response = urllib2.urlopen(req)

print "HTTP return code:%d" % response.getcode()

strResult= response.read()

print strResult

except Exception ,ex:

print "Found Error :%s" % str(ex)

运行这个就会出现错误:

提示证书校验错误。这时如果我们想要访问这样的网站就要把客户端的证书校验关闭。在前面加上一句:ssl._create_default_https_context =

ssl._create_unverified_context即可

import

urllib2

import

ssl

ssl._create_default_https_context =

ssl._create_unverified_context

if

__name__ == '__main__':

myurl="https://127.0.0.1:8443"

req

= urllib2.Request(myurl)

try:

response = urllib2.urlopen(req)

print "HTTP return code:%d" % response.getcode()

strResult= response.read()

print strResult

except Exception ,ex:

print "Found Error :%s" % str(ex)

当然也可以自己创建一个不校验的SSL上下文,然后引用这个上下文来打开url

ctx =

ssl._create_unverified_context()

然后

response =

urllib2.urlopen(req,context=ctx)

二、使用OPENSSL生成证书

1、生成自签名的证书

在使用python提供HTTPS服务之前,我们需要先生成证书,这里请参考https://www.cnblogs.com/wucao/archive/2017/02/27/6474711.html

这篇文章讲解了如何使用openssl生成证书。在WINDOWS平台记得要设置环境变量,参考我的一篇博文http://blog.sina.com.cn/s/blog_5d18f85f0102xdm7.html。

使用命令:openssl req -x509

-newkey rsa:2048 -nodes -days 365 -keyout private.pem -out

cert.crt

之后会要求我们输入一些组织信息,你可以根据你的实际情况填写。之后就生成了自签名的证书cert.crt,私钥是private.pem

2、自己建立一个CA(证书签发机构),然后用自己的CA来颁发证书。这个我们后面讨论双向认证的时候会用到。详细内容请参考http://blog.csdn.net/gx_1983/article/details/47866537

这里我只是简单的写一下我自己的步骤

1)

保证openssl的bin目录在path环境变量里面。创建一个工作目录,这里我使用ca这个目录。然后在ca下面再创建目录demoCA。之后在demoCA下创建空白文本文件index.txt和serial,并且打开serial写入字符01

2)

先造成CA的KEY和证书

openssl

req -new -x509 -days 36500 -key ca.key -out ca.crt

执行后会出现提示,主要是要求输入一些组织方面的信息,请按要求填写即可

执行成功后会造成ca.key和ca.crt 两个文件,ca.key为私钥需要妥善保管,不要轻易给别人。ca.crt为证书可以随意传播。

3)

生成服务器的私钥和证书,其中证书使用了CA的私钥进行签名:

l

生成server私钥

openssl genrsa -out server.key 2048

l

使用server私钥生成server端证书请求文件

openssl req -new -key server.key -out

server.csr

一样需要回答一些组织方面的问题

l

使用server证书请求文件通过CA生成由CA签名的证书

openssl ca -in server.csr -out server.crt -cert

ca.crt -keyfile ca.key

l

验证server证书

openssl verify -CAfile ca.crt

server.crt

这样就得到两个文件:一个是私钥server.key;一个是证书server.crt

4)

用跟3)同样的方法生成客户端的client.key和client.crt这两个文件在后面的双向认证里面会用到

三、使用python提供HTTPS服务

我们有了证书和私钥了,下面就可以正式使用python建立一个HTTPS网站了。这里我使用框架实现,用的是twisted。使用的证书是之前用OPENSSL生成的自签名证书。

#-*

-coding: utf-8 -* -

from

twisted.web import server, resource

from

twisted.internet import reactor,ssl

class

MainResource(resource.Resource):

isLeaf = True

# 用于处理GET类型请求

def

render_GET(self, request):

# name参数

name = 'World'

if request.args.has_key('name'):

name = request.args['name'][0]

# 设置响应编码

request.responseHeaders.addRawHeader("Content-Type", "text/html;

charset=utf-8")

# 响应的内容直接返回

return "

Hello, " + name + ""

if

__name__ == '__main__':

sslContext = ssl.DefaultOpenSSLContextFactory(

'C:/ca/private.pem, # 私钥

'C:/ca/cert.crt' # 证书

)

site

= server.Site(MainResource())

reactor.listenSSL(8080, site, sslContext)

print "监听端口:8080"

reactor.run()

使用之前的客户端访问,记得关闭证书校验。这时可以看到可以顺利访问。如果使用浏览器访问,也可以正常访问,但此时会出现安全提示,忽略这个安全提示后也可以正常访问。

四、SSL双向校验

上面的示例都是客户端对服务器端证书的校验。这也是最常见的单向校验。这里面由客户端来校验服务器端的证书(当然也可以选择不校验)。通常的互联网服务都是这种方式。在这种方式下,客户端不需要有证书。服务器端也不会校验客户端的证书。此外还有一种双向校验模式。在这种模式下,客户端也要有证书,并且在协商过程中提供给服务器端。服务器也会对客户端的证书进行校验。这种模式一般应用于对安全要求比较高的环境,服务器和用户端都需要证书,并且双方都要能被权威CA验证通过。看看下面的服务器端代码:

#-*

-coding: utf-8 -* -

from

twisted.web import server, resource

from

twisted.internet import reactor,ssl

from

OpenSSL import SSL

def

verifyCallback(connection, x509, errnum, errdepth, ok):

if

not ok:

print 'invalid cert from subject:', x509.get_subject()

return False

else:

print "Certs are fine", x509.get_subject()

return True

class

MainResource(resource.Resource):

isLeaf = True

# 用于处理GET类型请求

def

render_GET(self, request):

# name参数

name = 'World'

if request.args.has_key('name'):

name = request.args['name'][0]

# 设置响应编码

request.responseHeaders.addRawHeader("Content-Type", "text/html;

charset=utf-8")

# 响应的内容直接返回

return "

Hello, " + name + ""

if

__name__ == '__main__':

sslContext = ssl.DefaultOpenSSLContextFactory(

'C:/ca/private.pem, # 私钥

'C:/ca/cert.crt' # 证书

)

ctx

= sslContext.getContext()

#这里改为了双向校验模式

ctx.set_verify(

SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,

verifyCallback

)

site

= server.Site(MainResource())

reactor.listenSSL(8080, site, sslContext)

print "监听端口:8080"

reactor.run()

然后再用客户端来访问一下。这时会发现有错误出现,错误提示为:

提示sslv3协议握手失败。原因就是服务器端要求双向校验,但客户端没有提供自己的证书给服务器,所以握手协商失败。

好的,我们再改一下客户端代码,适配这种双向校验模式。这里客户端使用使用的证书是,是之前使用OPENSSL生成的客户端证书,并且使用了我们自己建立的CA进行签名。

import urllib2

import ssl

KEY_FILE="C:/ca/client.key"

CERT_FILE="C:/ca/client.crt"

context =

ssl.SSLContext(ssl.PROTOCOL_SSLv23)

context.check_hostname =

False

context.load_cert_chain(certfile=CERT_FILE,keyfile=KEY_FILE)

context.verify_mode=ssl.CERT_REQUIRED

if __name__ == '__main__':

myurl="https://127.0.0.1:8080/?name=qh"

req

= urllib2.Request(myurl)

try:

response = urllib2.urlopen(req,context=context)

#response = urllib2.urlopen(req)

print "HTTP return code:%d" % response.getcode()

strResult= response.read()

print strResult

except Exception ,ex:

print "Found Error in auth phase:%s" % str(ex)

这里出现错误提示

证书校验失败。这是因为服务器提供的证书没有通过客户端的校验。

这是因为服务器端的证书是使用自签名的证书,当然通不过客户端校验了。这里我们要把服务器端的证书改成我们使用CA签名的证书。修改一下服务器端代码:

sslContext = ssl.DefaultOpenSSLContextFactory(

'C:/ca/server.key', # 私钥

'C:/ca/server.crt' # 证书

)

改过之后再试,可以还出现同样的错误,这是怎么回事呢?原来服务器端的证书是使用我们自建的CA进行签名的,不是权威CA,所以校验失败了。这怎么办呢,我们可以指定CA的证书,把我们的CA证书设置为可信。修改一下客户端,加上一句:

CA_FILE="c:/ca/ca.crt"

context.load_verify_locations(CA_FILE)

注意ca.crt是之前我们使用OPENSSL建立的CA证书(请参考前面第二部分的内容)。修改之后重新运行,发现还是出现错误:

这个错误提示变了,提示CA不知名。同时在服务器端的回调函数也有错误输出:invalid cert from subject:

这说明服务器端校验客户端证书失败,原因当然是服务器端我们没有把我们自己的CA设置为可信。所以同样的在服务器端也要修改一下,加上以下两句:

cafile="c:/ca/ca.crt"

ctx.load_verify_locations( cafile)

重新运行,这下成功了

下面把完整的代码发一下:

服务器端:

#-* -coding: utf-8 -* -

'''

Created on 2018-1-16

@author: qh

'''

from twisted.internet import iocpreactor as

iocpreactor

try:

iocpreactor.install()

except Exception, e:

print "iocp install failed:%s" % str(e)

from twisted.web import server,

resource

from twisted.internet import

reactor,ssl

from OpenSSL import SSL

cafile="c:/ca/ca.crt"

class MainResource(resource.Resource):

isLeaf = True

# 用于处理GET类型请求

def

render_GET(self, request):

# name参数

name = 'World'

if request.args.has_key('name'):

name = request.args['name'][0]

# 设置响应编码

request.responseHeaders.addRawHeader("Content-Type", "text/html;

charset=utf-8")

# 响应的内容直接返回

return "

Hello, " + name + ""

if __name__ == '__main__':

sslContext = ssl.DefaultOpenSSLContextFactory(

'C:/ca/server.key', # 私钥

'C:/ca/server.crt' # 证书

)

ctx

= sslContext.getContext()

ctx.set_verify(

SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,

verifyCallback

)

ctx.load_verify_locations( cafile)

site

= server.Site(MainResource())

reactor.listenSSL(8080, site, sslContext)

print "监听端口:8080"

reactor.run()

客户端:

'''

Created on 2018-3-2

@author: qh

'''

import urllib2

import ssl

KEY_FILE="C:/ca/client.key"

CERT_FILE="C:/ca/client.crt"

CA_FILE="c:/ca/ca.crt"

context =

ssl.SSLContext(ssl.PROTOCOL_SSLv23)

context.check_hostname =

False

context.load_cert_chain(certfile=CERT_FILE,keyfile=KEY_FILE)

context.load_verify_locations(CA_FILE)

context.verify_mode=ssl.CERT_REQUIRED

if __name__ == '__main__':

req

= urllib2.Request(myurl)

try:

response = urllib2.urlopen(req,context=context)

#response = urllib2.urlopen(req)

print "HTTP return code:%d" % response.getcode()

strResult= response.read()

print strResult

except Exception ,ex:

print "Found Error in auth phase:%s" % str(ex)

五、其它

1、context.load_cert_chain(certfile=CERT_FILE,keyfile=KEY_FILE)

这个函数可以只提供一个certfile这一个文件,这时要求CERT_FILE里面包括私钥。其实证书文件和私钥文件都是简单的文本文件,你可以把私钥文件的内容复制到证书文件后面,这样你就可以在这个函数里面只提供一个文件了。

2、如何在request对象里面读出客户端信息呢?

所有的客户端信息都可以request对象里面读出,包括客户端的证书信息,看看下面,你所需要的基本都有了

def render_GET(self, request):

print

request.content.read()

print request.getAllHeaders()

print request.getClientIP()

print request.getHost()

print request.transport.getPeer()

cl=request.transport.getPeerCertificate()

certificate = ssl.Certificate(cl)

print certificate.getIssuer()

print certificate.getSubject()

3、使用POST方法发送JSON数据

def urllib2_open(myurl):

headers = {'Content-Type': 'application/json'}

data={'test':'hello'}

req

= urllib2.Request(myurl,headers=headers,

data=json.dumps(data))

try:

response = urllib2.urlopen(req,context=context)

print "HTTP return code:%d" % response.getcode()

strResult= response.read()

print strResult

except Exception ,ex:

print "Found Error in auth phase:%s" % str(ex)

4、有一个地方我研究的不是特别清楚:默认情况下python是到哪里找权威CA证书的,是在某个文件夹还在通过注册表,还是直接通过操作系统提供的API。

写这么多总算把该写的写完了。之前我只是想用python访问一个HTTPS站点的。但当时很不顺利,不管怎么搞都出现SSLV3_ALERT_HANDSHAKE_FAILURE,当时在网上找了很多类似的解决办法,什么启用SSLV3啊、把加密方式改为ALL啊,另外也用了requests和pycurl等等第三方组件。但所有尝试无一成功。花了我好几天时间。最后终于发现原来这个站点需要双向认证。从这里面感觉自己对SSL了解太少了,走了太多弯路。于是就对SSL好好研究了一下,这才有了上面的内容。这些内容还是比较肤浅,都只限于应用方面,但对我来说足够了。分享给大家,让大家少走我走过的弯路。

python 证书-在python使用SSL(HTTPS)相关推荐

  1. (转)python requests 高级用法 -- 包括SSL 证书错误的解决方案

    (转)python requests 高级用法 -- 包括SSL 证书错误的解决方案 参考文章: (1)(转)python requests 高级用法 -- 包括SSL 证书错误的解决方案 (2)ht ...

  2. python认证考试mac_Mac OS 平台使用 Python 和 Docker 创建测试用 Https Server

    Mac OS 平台使用 Python 和 Docker 创建测试用 Https Server Flask 是我很喜欢的 Python Web Framework,最近需要测试 Https 通信,需要创 ...

  3. 有哪些含金量较高的Python证书?

    1.阿里python工程师实习证书 2.腾讯python工程师实习证书 3.字节python工程师实习证书 4.美团python工程师实习证书 for i in bigComPany:for j in ...

  4. coursera python证书_IBM数据科学证书 | 你想要的一站式新手入门课!

    Hello World!大哥终于回来了...... 虽然大哥已经写过很多数据网课推荐了,但还是有不少小伙伴问有没有大礼包一站式入门数据分析的课程...... 来来来,大哥终于在Coursera上挖到了 ...

  5. jodd忽略ssl证书_关于java访问https资源时,忽略证书信任问题

    java程序在访问https资源时,出现报错 sun.security.validator.ValidatorException: PKIX path building failed: sun.sec ...

  6. https://是什么?HTTPS和HTTP有什么不一样?如何申请SSL(HTTPS)证书?

    一.https://是什么 HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Pr ...

  7. 怎样获得python证书_如何从python中的x509证书中提取公钥?

    已解决 密码学Python 如何从python中的x509证书中提取公钥?10 下面显示了我遵循的代码示例,但是我得到了错误响应 - "无法加载证书".from cryptogra ...

  8. Python有证书吗?python证书是什么级别的呢?怎么才能达到python证书的考试条件呢?

    导语 人生苦短,我用Python!! 学好Python:兼职接单一条龙服务,还能大学毕业之后直接就业赚技术工资. 关于python证书的问题 现在Python越来越流行,很多大学和高中都已经开始将Py ...

  9. 宝塔面板SSL证书显示不安全?这里我教你宝塔SSL证书如何配置及开启HTTPS访问的操作方法

    前提条件 你熟悉宝塔面板搭建网站.我这个建站老鸟都在用宝塔面板建站,主要就是图它的节省时间和精力.大家有不熟悉宝塔面板使用的,可以查看文章:宝塔面板添加WordPress站点详细图文教程 之后你就完全 ...

最新文章

  1. Leetcode: Spiral Matrix
  2. 服务器系统磁盘,服务器系统重装与磁盘阵列
  3. 解决beautifulsoup代码无效问题
  4. WPF实现时间轴(仿Gitee)
  5. adhoc包无法安装_iOS 5.1.1 设备不能安装AdHoc问题版本号
  6. pip 指定镜像源 指定版本 批量安装
  7. 工程linux下创建svn仓库目录结构
  8. struts2登陆拦截器
  9. 简单数独的DFS求解
  10. Sketch的下载与安装
  11. http://www.techpot.net/archives/38147
  12. android 计时器工具类,Android实现计时器功能
  13. 《JAVA与模式》— 调停者模式
  14. 小虎电商浏览器:淘数据店铺数据分析如何?数据精确吗?
  15. 【图像分割】基于计算机视觉实现视网膜图像中的血管分割附matlab代码
  16. Liblinear的使用
  17. 【转】以太坊分片:Overview and Finality
  18. 物体尺寸检测项目--相机标定(像素坐标系/实际程序关键理解)
  19. docker 运行花生壳实现内外网穿透
  20. 微信小程序实现点击拍照长按录像功能

热门文章

  1. 小程序中强制页面刷新
  2. 树莓派python播放报警声(usb音响)
  3. 【08月01日】A股滚动市净率PB历史新低排名
  4. 利用声音传感器控制led灯功能_树莓派 LED+蜂鸣+声音传感器+红外模块组合打造声控/红外控制LED...
  5. 成都男子因女司机别车将其暴打 同款行车记录仪获热销
  6. 【项目实战】基于STM32单片机的智能小车设计(有代码)
  7. Load Balancer as a Service (LBaaS V2.0)
  8. Anaconda打开多个spyder窗口
  9. 如何做好检测报告的质量控制?-LIMS2
  10. 五星好评点亮效果(精灵图)