使用PEM证书的HTTPS连接

8 投票
3 回答
32017 浏览
提问于 2025-04-16 17:02

我正在尝试使用PEM证书发送HTTPS请求,代码大概是这样的:

import httplib  
CERT_FILE = '/path/certif.pem'
conn = httplib.HTTPSConnection('10.10.10.10','443', cert_file =CERT_FILE)   
conn.request("POST", "/") 
response = conn.getresponse()       
print response.status, response.reason
conn.close()

但我遇到了以下错误:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.6/httplib.py", line 914, in request
self._send_request(method, url, body, headers)
File "/usr/lib/python2.6/httplib.py", line 951, in _send_request
self.endheaders()
File "/usr/lib/python2.6/httplib.py", line 908, in endheaders
self._send_output()
File "/usr/lib/python2.6/httplib.py", line 780, in _send_output
self.send(msg)
File "/usr/lib/python2.6/httplib.py", line 739, in send
self.connect()
File "/usr/lib/python2.6/httplib.py", line 1116, in connect
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
File "/usr/lib/python2.6/ssl.py", line 338, in wrap_socket
suppress_ragged_eofs=suppress_ragged_eofs)
File "/usr/lib/python2.6/ssl.py", line 118, in __init__
cert_reqs, ssl_version, ca_certs)
ssl.SSLError: [Errno 336265225] _ssl.c:339: error:140B0009:SSL       
routines:**SSL_CTX_use_PrivateKey_file**:PEM lib

当我从httplib中去掉cert_file时,我得到了以下响应:

200 ok

当我按照MattH的建议添加了认证头,并且发送了一个空的请求内容时,这个请求也能成功。

但是,当我把请求的路径、内容和头信息都填好后(我简化了一下...)

body = '<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">blablabla</S:Envelope>'
URLprov = "/syncaxis2/services/XXXsyncService"
auth_header = 'Basic %s' %  (":".join(["xxx","xxxxx"]).encode('Base64').strip('\r\n'))
conn.request("POST",URLprov,body,{'Authenticate':auth_header})

我却收到了401未授权的响应!

如你所见,首先,我被要求提供私钥!我作为客户端,为什么需要私钥呢?然后,当我去掉私钥和证书,只填路径、内容和头信息时,我又收到了401未授权的错误,消息是WWW-Authenticate: Basic realm="SYNCNB Server Realm"。

有没有人能解释一下这个问题?有没有其他方法可以在Python中使用证书发送HTTPS请求?

3 个回答

1

你现在做的事情是想连接一个需要用客户端证书进行身份验证的网络服务。

确定你有的是PEM文件,而不是PKCS#12文件吗?PEM文件的样子是这样的(是的,我知道我这里包含了一个私钥……这只是我为这个例子生成的一个虚拟文件):

-----BEGIN RSA PRIVATE KEY-----                                                                     
MIICXQIBAAKBgQDDOKpQZexZtGMqb7F1OMwdcFpcQ/pqtCoOVCGIAUxT3uP0hOw8                                    
CZNjLT2LoG4Tdl7Cl6t66SNzMVyUeFUrk5rkfnCJ+W9RIPkht3mv5A8yespeH27x                                    
FjGVbyQ/3DvDOp9Hc2AOPbYDUMRmVa1amawxwqAFPBp9UZ3/vfU8nxwExwIDAQAB                                    
AoGBAMCvt3svfr9zysViBTf8XYtZD/ctqYeUWEZYR9hj36CQyVLZuAnyMaWcS7j7                                    
GmrfVNygs0LXxoO2Xvi0ZOxj/mZ6EcZd8n37LxTo0GcWvAE4JjPr7I4MR2OvGYa/                                    
1696e82xwEnUdpyBv9z3ebleowQ1UWP88iq40oZYukUeignRAkEA9c7MABi5OJUq                                    
hf9gwm/IBie63wHQbB2wVgB3UuCYEa4Zd5zcvJIKz7NfhsZKKcZJ6CBVxwUd84aQ                                    
Aue2DRwYQwJBAMtQ5yBA8howP2FDqcl9sovYR0jw7Etb9mwsRNzJwQRYYnqCC5yS                                    
nOaNn8uHKzBcjvkNiSOEZFGKhKtSrlc9qy0CQQDfNMzMHac7uUAm85JynTyeUj9/                                    
t88CDieMwNmZuXZ9P4HCuv86gMcueex5nt/DdVqxXYNmuL/M3lkxOiV3XBavAkAA                                    
xow7KURDKU/0lQd+x0X5FpgfBRxBpVYpT3nrxbFAzP2DLh/RNxX2IzAq3JcjlhbN                                    
iGmvgv/G99pNtQEJQCj5AkAJcOvGM8+Qhg2xM0yXK0M79gxgPh2KEjppwhUmKEv9                                    
o9agBLWNU3EH9a6oOfsZZcapvUbWIw+OCx5MlxSFDamg                                                        
-----END RSA PRIVATE KEY-----                                                                    
-----BEGIN CERTIFICATE-----                                                                         
MIIDfjCCAuegAwIBAgIJAOYJ/e6lsjrUMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD                                    
VQQGEwJVUzELMAkGA1UECBMCRkwxDjAMBgNVBAcTBVRhbXBhMRQwEgYDVQQKEwtG                                    
b29iYXIgSW5jLjEQMA4GA1UECxMHTnV0IEh1dDEXMBUGA1UEAxMOd3d3LmZvb2Jh                                    
ci5jb20xGjAYBgkqhkiG9w0BCQEWC2Zvb0BiYXIuY29tMB4XDTExMDUwNTE0MDk0                                    
N1oXDTEyMDUwNDE0MDk0N1owgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJGTDEO                                    
MAwGA1UEBxMFVGFtcGExFDASBgNVBAoTC0Zvb2JhciBJbmMuMRAwDgYDVQQLEwdO                                    
dXQgSHV0MRcwFQYDVQQDEw53d3cuZm9vYmFyLmNvbTEaMBgGCSqGSIb3DQEJARYL                                    
Zm9vQGJhci5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMM4qlBl7Fm0                                    
YypvsXU4zB1wWlxD+mq0Kg5UIYgBTFPe4/SE7DwJk2MtPYugbhN2XsKXq3rpI3Mx                                    
XJR4VSuTmuR+cIn5b1Eg+SG3ea/kDzJ6yl4fbvEWMZVvJD/cO8M6n0dzYA49tgNQ                                    
xGZVrVqZrDHCoAU8Gn1Rnf+99TyfHATHAgMBAAGjge8wgewwHQYDVR0OBBYEFHZ+                                    
CPLqn8jlT9Fmq7wy/kDSN8STMIG8BgNVHSMEgbQwgbGAFHZ+CPLqn8jlT9Fmq7wy                                    
/kDSN8SToYGNpIGKMIGHMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxDjAMBgNV                                    
BAcTBVRhbXBhMRQwEgYDVQQKEwtGb29iYXIgSW5jLjEQMA4GA1UECxMHTnV0IEh1                                    
dDEXMBUGA1UEAxMOd3d3LmZvb2Jhci5jb20xGjAYBgkqhkiG9w0BCQEWC2Zvb0Bi                                    
YXIuY29tggkA5gn97qWyOtQwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOB                                    
gQAv13ewjgrIsm3Yo8tyqTUHCr/lLekWcucClaDgcHlCAH+WU8+fGY8cyLrFFRdk                                    
4U5sD+P313Adg4VDyoocTO6enA9Vf1Ar5XMZ3l6k5yARjZNIbGO50IZfC/iblIZD                                    
UpR2T7J/ggfq830ACfpOQF/+7K+LgFLekJ5dIRuD1KKyFg==                                                    
-----END CERTIFICATE-----

可以看看这个问题。

9

听起来你需要的东西跟我之前提供的一个答案有点像,那个答案是关于如何进行 简单的客户端证书认证。为了方便你,这里有一段稍微修改过的代码,适合你的问题:

import httplib
import urllib2

PEM_FILE = '/path/certif.pem' # Renamed from PEM_FILE to avoid confusion
CLIENT_CERT_FILE = '/path/clientcert.p12' # This is your client cert!

# HTTPS Client Auth solution for urllib2, inspired by
# http://bugs.python.org/issue3466
# and improved by David Norton of Three Pillar Software. In this
# implementation, we use properties passed in rather than static module
# fields.
class HTTPSClientAuthHandler(urllib2.HTTPSHandler):
    def __init__(self, key, cert):
        urllib2.HTTPSHandler.__init__(self)
        self.key = key
        self.cert = cert
    def https_open(self, req):
        #Rather than pass in a reference to a connection class, we pass in
        # a reference to a function which, for all intents and purposes,
        # will behave as a constructor
        return self.do_open(self.getConnection, req)
    def getConnection(self, host):
        return httplib.HTTPSConnection(host, key_file=self.key, cert_file=self.cert)


cert_handler = HTTPSClientAuthHandler(PEM_FILE, CLIENT_CERT_FILE)
opener = urllib2.build_opener(cert_handler)
urllib2.install_opener(opener)

f = urllib2.urlopen("https://10.10.10.10")
print f.code
2

请查看 http://docs.python.org/library/httplib.html

httplib.HTTPSConnection 不会对服务器的证书进行任何验证。

如果你需要提供自己的私有证书,那是因为服务器在进行基于证书的客户端认证。也就是说,服务器会检查客户端是否有一个被它信任的证书颁发机构(CA)签发的证书,这样才能访问服务器的资源。


如果你没有指定证书这个可选参数,你应该还是能连接到HTTPS服务器,但无法验证服务器的证书。


更新

根据你提到的你尝试了基本认证,看起来服务器还是希望你使用基本认证来进行身份验证。要么你的凭证无效(你有没有单独验证过?),要么你的 Authenticate 头部格式不正确。你可以修改你的示例代码,加入一个基本认证的头部和一个空的POST负载:

import httplib  
conn = httplib.HTTPSConnection('10.10.10.10','443')   
auth_header = 'Basic %s' % (":".join(["myusername","mypassword"]).encode('Base64').strip('\r\n'))
conn.request("POST", "/","",{'Authorization':auth_header}) 
response = conn.getresponse()       
print response.status, response.reason
conn.close()

撰写回答