通过https进行的客户端证书认证失败。

4
我正在尝试使用Python 2.7中的此示例代码来实现https客户端认证。不幸的是,客户端脚本似乎没有正确进行身份验证,我也无法找出原因。
我按照以下步骤生成了测试CA和服务器/客户端证书:
# Generate CA key and certificate
openssl genrsa -des3 -out test.ca.key 8192
openssl req -new -key test.ca.key -x509 -days 30 -out test.ca.crt

# Generate server key and certificate
openssl genrsa -out www.testsite.com.key 1024
openssl req -new -key www.testsite.com.key -out www.testsite.com.csr
openssl x509 -req -days 30 -in www.testsite.com.csr -CA test.ca.crt -CAkey test.ca.key -CAcreateserial -out www.testsite.com.crt

# Generate client key and certificate
openssl genrsa -out testclient.key 1024
openssl req -new -key testclient.key -out testclient.csr
openssl x509 -req -days 30 -in testclient.csr -CA test.ca.crt -CAkey test.ca.key -CAcreateserial -out testclient.crt

现在,如果我生成一个PKCS#12证书:
openssl pkcs12 -export -clcerts -in testclient.crt -inkey testclient.key -out testclient.p12

如果我将testclient.p12导入Firefox,我可以如预期地浏览测试网站,因此服务器和密钥看起来已正确配置。然而,在尝试上述引用的示例代码时:

import urllib2, httplib

class HTTPSClientAuthHandler(urllib2.HTTPSHandler):
    def __init__(self, key, cert):
        urllib2.HTTPSHandler.__init__(self)
        self.key = key
        self.cert = cert
    def https_open(self, req):
        return self.do_open(self.getConnection, req)
    def getConnection(self, host, timeout=300):
        return httplib.HTTPSConnection(host, key_file=self.key, cert_file=self.cert)

opener = urllib2.build_opener(HTTPSClientAuthHandler('testclient.key', 'testclient.crt') )
response = opener.open("https://www.testsite.com/")
print response.read()

我遇到了403错误:

...

Traceback (most recent call last):
  File "./cert2.py", line 21, in <module>
    response = opener.open("https://www.testsite.com/")
  File "/usr/lib64/python2.6/urllib2.py", line 395, in open
    response = meth(req, response)
  File "/usr/lib64/python2.6/urllib2.py", line 508, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib64/python2.6/urllib2.py", line 433, in error
    return self._call_chain(*args)
  File "/usr/lib64/python2.6/urllib2.py", line 367, in _call_chain
    result = func(*args)
  File "/usr/lib64/python2.6/urllib2.py", line 516, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 403: Forbidden

我哪里出了差错?

编辑:这是该网站的Apache配置:

<VirtualHost *:80>
  ServerAdmin webmaster@testsite.com
  ServerName www.testsite.com

  DocumentRoot /var/www/testsite/
  <Directory "/">
    Options FollowSymLinks
    AllowOverride None
  </Directory>

  RewriteEngine On
  RewriteCond %{SERVER_PORT} 80
  RewriteRule ^/?(.*) https://www.testsite.com/$1 [L]

  ErrorLog ${APACHE_LOG_DIR}/error.log
  LogLevel warn
  CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

<VirtualHost *:443>
  ServerAdmin webmaster@testsite.com

  DocumentRoot /var/www/testsite/
  ServerName www.testsite.com

  SSLEngine on
  SSLCertificateFile    /etc/ssl/certs/www.testsite.com.crt
  SSLCertificateKeyFile /etc/ssl/private/www.testsite.com.key
  SSLCertificateChainFile /etc/ssl/ca/test.ca.crt
  SSLCACertificateFile /etc/ssl/ca/test.ca.crt

  <Location />
    SSLRequireSSL
    SSLVerifyClient require
    SSLVerifyDepth 10
  </Location>

  <Directory />
    Options FollowSymLinks
    AllowOverride None
  </Directory>
  <Directory /var/www/testsite>
    Options FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
  </Directory>

  ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/testsite/
  <Directory "/usr/lib/cgi-bin/testsite">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
  </Directory>

  ErrorLog /var/log/apache2/error.log
  LogLevel warn
  CustomLog /var/log/apache2/access.log combined
</VirtualHost>

你解决过这个问题吗?我也遇到了完全相同的问题。 - dougzor
@dougzor 不需要了。我简化了要求,只使用curl完成了我所需的功能。 - glibdud
2个回答

2
我认为这不是证书问题。HTTP 403(禁止)已经是服务器通过安全通道向您发送的http状态代码,因此ssl似乎工作正常。您只是试图访问未经授权的内容。
附加说明:也请在浏览器中检查相同的url!

谢谢,我正在访问与浏览器测试中请求的相同URL(测试成功)相同的URL。 - glibdud
@glibdud 你需要 HTTPSClientAuthHandler 吗?在很多情况下,客户端根本没有经过身份验证。我猜你还没有设置浏览器来支持服务器的客户端密钥。 - pasztorpisti
是的,服务器已配置为需要客户端证书,并且在我通过浏览器访问页面时请求证书。在浏览器中,我可以提供证书并获得访问权限,但是在包含的Python代码中无法正常工作。 - glibdud

1
如果您需要在Python中使用客户端,可以使用requests,特别是this
import requests
resp = requests.get('https://www.testsite.com/', cert=('testclient.crt', 'testclient.key'))

print resp.status_code
print resp.text

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接