使用PKCS#12证书时httplib的HTTPSConnection出错

5 投票
2 回答
6099 浏览
提问于 2025-04-15 21:32

我正在尝试使用httplib的HTTPSConnection进行客户端验证,使用的是PKCS #12证书。我知道这个证书是有效的,因为我可以在MSIE和Firefox中用它连接到服务器。

这是我的连接函数(证书里包含了私钥)。我把它简化到最基本的样子:

def connect(self, cert_file, host, usrname, passwd):
    self.cert_file = cert_file
    self.host = host

    self.conn = httplib.HTTPSConnection(host=self.host, port=self.port, key_file=cert_file, cert_file=cert_file)

    self.conn.putrequest('GET', 'pathnet/,DanaInfo=200.222.1.1+')
    self.conn.endheaders()
    retCreateCon = self.conn.getresponse()

    if is_verbose:
        print "Create HTTPS connection, " + retCreateCon.read()

(注意:请不要对硬编码的路径发表评论 - 我先想让它工作,之后再美化。这个硬编码的路径是正确的,因为我在MSIE和Firefox中可以连接到它。我把IP地址换了。)

当我尝试使用PKCS#12证书(.pfx文件)运行这个时,我收到了一个看起来像是openSSL的错误。这里是完整的错误追踪信息:

  File "Usinghttplib_Test.py", line 175, in 
    t.connect(cert_file=opts["-keys"], host=host_name, usrname=opts["-username"], passwd=opts["-password"])
  File "Usinghttplib_Test.py", line 40, in connect
    self.conn.endheaders()
  File "c:\python26\lib\httplib.py", line 904, in endheaders
    self._send_output()
  File "c:\python26\lib\httplib.py", line 776, in _send_output
    self.send(msg)
  File "c:\python26\lib\httplib.py", line 735, in send
    self.connect()
  File "c:\python26\lib\httplib.py", line 1112, in connect
    self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
  File "c:\python26\lib\ssl.py", line 350, in wrap_socket
    suppress_ragged_eofs=suppress_ragged_eofs)
  File "c:\python26\lib\ssl.py", line 113, in __init__
    cert_reqs, ssl_version, ca_certs) ssl.SSLError: [Errno 336265225] _ssl.c:337: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib

注意,openSSL的错误(列表中的最后一项)提到了“PEM lib”,我觉得很奇怪,因为我并没有尝试使用PEM证书。

为了好玩,我把PKCS#12证书转换成了PEM证书,然后用那个运行了相同的代码。在这种情况下,我没有收到错误,系统提示我输入PEM密码短语,代码也确实尝试连接到服务器。(我收到了“服务不可用,请稍后再试”的响应,但我认为这是因为服务器不接受PEM证书。我在Firefox中也无法使用PEM证书连接到服务器。)

httplib的HTTPSConnection应该支持PKCS#12证书吗?(也就是pfx文件。)如果是的话,为什么看起来openSSL在尝试在PEM库中加载它?我是不是做错了什么?

任何建议都欢迎。

编辑:证书文件同时包含证书和私钥,这就是我为HTTPSConnection的key_file和cert_file参数提供相同文件名的原因。

2 个回答

5

这并不奇怪。Python的库参考文档对此解释得很清楚。从 http://docs.python.org/library/httplib.html 可以看到:

类 httplib.HTTPSConnection(主机[, 端口[, 密钥文件[, 证书文件[, 严格模式[, 超时[, 源地址]]]]]])

这是一个HTTPConnection的子类,用于与安全服务器进行SSL加密通信。默认端口是443。密钥文件是一个PEM格式的文件,里面包含你的私钥。证书文件是一个PEM格式的证书链文件。

4

在openSSL的邮件列表上,我和Mounir Idrassi聊过。他提到openSSL确实支持PKCS#12文件,而根据我收到的错误信息,看来httplib在加载密钥时调用了错误的函数。

他是这么说的:

关于你遇到的错误,似乎你使用的Python模块在调用SSL_CTX_use_PrivateKey_file时,给它传入了PKCS#12文件名。但这个函数不支持,因为SSL_CTX_use_PrivateKey_file只接受两种格式:SSL_FILETYPE_PEM和SSL_FILETYPE_ASN1。

(我把PKCS#12文件名当作密钥文件传给httplib,因为这种文件格式同时包含了证书和私钥。)

要解决这个问题,你有两个选择: - 要么给Python模块提供一个PEM格式的私钥文件。 - 要么修改这个Python模块的源代码,使用我之前提到的PKCS#12函数来提取私钥为EVP_PKEY,然后调用SSL_use_PrivateKey,而不是SSL_CTX_use_PrivateKey_file,同时使用SSL_use_certificate来设置相关的证书。

(我尝试了第一个方法,但没有成功。这并不意味着它一定不可行,只是我没有做到。)

撰写回答