【发布时间】:2018-03-27 11:38:25
【问题描述】:
我希望能够在不需要临时文件的情况下将证书传递给 Python 的 ssl 库。看来Python ssl模块cannot do that.
为了解决这个问题,我想从原生 _ssl 模块中检索存储在 ssl._ssl._SSLContext 类中的底层 SSL_CTX 结构。然后,使用 ctypes 我可以使用该上下文从 libssl 手动调用相应的 SSL_CTX_* 函数。如何在 C 中做到这一点显示在 here 中,我会通过 ctypes 做同样的事情。
不幸的是,我被困在我设法从ssl._ssl._SSLContext 挂钩到load_verify_locations 函数但似乎无法获得ssl._ssl._SSLContext 结构实例的正确内存地址。所有load_verify_locations 函数看到的是父ssl.SSLContext 对象。
我的问题是,我如何从ssl.SSLContext 对象的实例获取到本机基类ssl._ssl._SSLContext 的内存?如果我有这个,我可以很容易地访问它的ctx 成员。
到目前为止,这是我的代码。关于如何对原生 Python 模块进行monkeypatch 的学分转到the forbidden fruit project by Lincoln Clarete
Py_ssize_t = hasattr(ctypes.pythonapi, 'Py_InitModule4_64') and ctypes.c_int64 or ctypes.c_int
class PyObject(ctypes.Structure):
pass
PyObject._fields_ = [
('ob_refcnt', Py_ssize_t),
('ob_type', ctypes.POINTER(PyObject)),
]
class SlotsProxy(PyObject):
_fields_ = [('dict', ctypes.POINTER(PyObject))]
class PySSLContext(ctypes.Structure):
pass
PySSLContext._fields_ = [
('ob_refcnt', Py_ssize_t),
('ob_type', ctypes.POINTER(PySSLContext)),
('ctx', ctypes.c_void_p),
]
name = ssl._ssl._SSLContext.__name__
target = ssl._ssl._SSLContext.__dict__
proxy_dict = SlotsProxy.from_address(id(target))
namespace = {}
ctypes.pythonapi.PyDict_SetItem(
ctypes.py_object(namespace),
ctypes.py_object(name),
proxy_dict.dict,
)
patchable = namespace[name]
old_value = patchable["load_verify_locations"]
libssl = ctypes.cdll.LoadLibrary("libssl.so.1.0.0")
libssl.SSL_CTX_set_verify.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p)
libssl.SSL_CTX_get_verify_mode.argtypes = (ctypes.c_void_p,)
def load_verify_locations(self, cafile, capath, cadata):
print(self)
print(self.verify_mode)
addr = PySSLContext.from_address(id(self)).ctx
libssl.SSL_CTX_set_verify(addr, 1337, None)
print(libssl.SSL_CTX_get_verify_mode(addr))
print(self.verify_mode)
return old_value(self, cafile, capath, cadata)
patchable["load_verify_locations"] = load_verify_locations
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
输出是:
<ssl.SSLContext object at 0x7f4b81304ba8>
2
1337
2
这表明,我要更改的不是 Python 知道的 ssl 上下文,而是其他一些随机内存位置。
要试用上面的代码,您必须运行 https 服务器。使用以下方法生成自签名 SSL 证书:
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost' -nodes
并使用以下代码启动服务器:
import http.server, http.server
import ssl
httpd = http.server.HTTPServer(('localhost', 4443), http.server.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket (httpd.socket, certfile='cert.pem', keyfile='key.pem', server_side=True)
httpd.serve_forever()
然后将以下行添加到我上面的示例代码的末尾:
urllib.request.urlopen("https://localhost:4443", context=context)
【问题讨论】:
-
你怎么称呼这段代码?当我运行你的代码时它就退出了
-
@TarunLalwani 您需要将上下文传递给 urllib 以使用它打开一些 URL。让我修改一下问题。
-
对我来说这个方法根本没有被调用?失败发生在
do_handshake,实际上发生在 SSL c 代码中,ssl.py,self._sslobj.do_handshake()行 -
另一种创建命名管道的方法怎么样,应该作为文件读取?所以没有创建临时文件并维护 SSL API?如果你愿意,我可以尝试做一些演示。
-
@josch,有什么更新吗?
标签: python-3.x ctypes cpython monkeypatching pyopenssl