我正在尝试从客户端对服务器进行REST调用。
服务器端
我使用Flask作为Web服务器。我使用以下命令生成了证书(cert.pem
)和公钥(key.pem
)。
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
以下是服务器端代码。
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return {'hello': 'world'}
@app.route('/someroute/<arg1>,<arg2>', methods=['GET'])
def fn1(arg1,arg2):
res = fn2(arg1, arg2)
return str(res)
def fn2(un,pwd):
#Do something and return a number
return num
if __name__ == '__main__':
context = ('cert.pem', 'key.pem')
app.run(host="ip", port=port1, debug=True, ssl_context=context)
客户端
我有一个烧瓶应用程序,我需要从中调用上面的服务器。以下是我用来执行此操作的代码。
import requests
# Below is the part of a function which gets called upon hitting a route in Flask.
ret = requests.get("https://<ip>:<port1>/<someroute>/arg1,arg2", verify=True)
print ret.text
这会引发以下错误。
requests.exceptions.SSLError:[Errno 1] _ssl.c:510:错误:14090086:SSL例程:SSL3_GET_SERVER_CERTIFICATE:证书验证失败
由于客户端不知道证书,因此预计会出现此错误。如果我跳过证书验证(verify=False
),一切都工作得很好。
如何让客户端信任此未知服务器SSL证书?我已经看到了需要在客户端使用Nginx的解决方案。我们可以不对flask(客户端)本身进行一些更改以使其信任我的服务器吗?如果是的话,我应该在Flask中做出哪些确切的改变?另外,建议urllib
是否优于requests
。
更新1
我在这里有另一个查询。从上面,我了解requests.get
每次我们进行REST调用时都会验证证书(如果我错了,请纠正我)。但是,这看起来像是一个额外的负载。我们可以在开始时没有建立加密连接,并通过使用私钥加密来交换数据吗?那么,如何实现开始部分 - 通过将公钥加密到服务器来发送私钥,然后通过使用私钥加密数据来开始交换数据。
答案 0 :(得分:0)
OC(原始评论):
服务器端
我正在使用Flask作为Web服务器。我已经使用以下命令生成了证书(cert.pem)和公钥(key.pem)。
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 36
如果您阅读man req
时说,
-out filename
This specifies the output filename to write to or standard output by default.
-keyout filename
This gives the filename to write the newly created private key to. If this option is not specified then the filename present in the configuration file is used.
在这里,cert.pem
可以称为公共证书,而key.pem
确实是您的私钥(请安全,私密地保存)。 FTFY。
OC(原始评论):
由于客户端不知道证书,因此预期会出现此错误。如果我跳过证书验证(verify = False),一切工作都很好。
如何使客户端信任此未知服务器SSL证书?我已经看到需要在客户端使用Nginx的解决方案。我们是否可以对flask(客户端)本身进行一些更改以使其信任我的服务器?如果是,我应该在Flask中进行哪些确切更改?另外,建议urllib是否比请求更好。
我会反过来回答,但首先要介绍一些背景知识。我发现这些链接对于深入了解SSL / TLS握手如何发生以及如何进行身份验证非常有用
现在,回到您的问题,
requests
> urllib
(个人观点,但是requests
确实有很好的支持并且很成熟-并不意味着urllib
不是)
您似乎只想执行服务器身份验证,因为您的客户端需要服务器的公共证书。来自How SSL and TLS provide authentication:
所需的证书如下,其中CA X将证书颁发给SSL或TLS客户端,CA Y将证书颁发给SSL或TLS服务器:
仅用于服务器身份验证,SSL或TLS服务器需要:
CA Y向服务器颁发的个人证书
服务器的私钥
以及SSL或TLS客户端需求:
- CA Y的CA证书
requests.get
中使用它并引用this from requests
,您可以使用受信任的CA证书来验证CA_BUNDLE文件或目录的路径:
requests.get('https://github.com', verify='/path/to/certfile')
一旦您这样做并获得了良好的证书,它就会起作用。
注意:OC中的openssl
命令未显示密钥/证书对的任何主题,这仍然会导致证书验证失败,请参阅说明如何生成自我密码的文档。签名证书。由于必须提供SAN(SubjectAltNames),因此在主题中仅提供CommonName(CN)便无法将来证明您的过程。
编辑: IBM文档中的CA讨论可能会引起混淆,但是由于这是一个自签名证书,因此您不必担心,只需将其视为Flask服务器为SSL / TLS服务器,并且任何客户端都可以该服务器可以使用服务器的公钥进行服务器验证。
OC(原始评论):
从上面可以理解,每次我们进行REST调用时,requests.get都会对证书进行验证(如果我输入错了,请更正我)。但是,这看起来像是一个额外的负担。
是的。它将每次执行验证。为避免这种情况,您可以使用requests
SSL Cert Verification
requests.Session()
OC(原始评论):
我们是否可以一开始就没有建立加密连接,而是通过使用私钥加密数据来交换数据?因此,如何实现开始部分-通过使用公钥加密私钥到服务器,然后通过使用私钥加密数据开始交换数据。
在列出的IBM文档中对此进行了很好的解释