Expand SSL cert support

- Capture the remote SSL certificate
- Expose the remote cert as an attribute on Response
- Expand the certutils.SSLCert interface to expose more cert info
This commit is contained in:
Aldo Cortesi
2012-04-02 16:19:00 +12:00
parent bb03255da0
commit ab1d8fa350
10 changed files with 72 additions and 14 deletions

View File

@@ -1,4 +1,4 @@
import subprocess, os, ssl, hashlib, socket, time
import os, ssl, hashlib, socket, time
from pyasn1.type import univ, constraint, char, namedtype, tag
from pyasn1.codec.der.decoder import decode
import OpenSSL
@@ -136,7 +136,6 @@ class _GeneralNames(univ.SequenceOf):
sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, 1024)
class SSLCert:
def __init__(self, pemtxt):
"""
@@ -144,6 +143,46 @@ class SSLCert:
"""
self.cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pemtxt)
@classmethod
def from_der(klass, der):
pem = ssl.DER_cert_to_PEM_cert(der)
return klass(pem)
def digest(self, name):
return self.cert.digest(name)
@property
def notbefore(self):
return self.cert.get_notBefore()
@property
def notafter(self):
return self.cert.get_notAfter()
@property
def has_expired(self):
return self.cert.has_expired()
@property
def subject(self):
return self.cert.get_subject().get_components()
@property
def serial(self):
return self.cert.get_serial_number()
@property
def keyinfo(self):
pk = self.cert.get_pubkey()
types = {
OpenSSL.crypto.TYPE_RSA: "RSA",
OpenSSL.crypto.TYPE_DSA: "DSA",
}
return (
types.get(pk.type(), "UNKNOWN"),
pk.bits()
)
@property
def cn(self):
cn = None
@@ -171,4 +210,3 @@ def get_remote_cert(host, port):
return SSLCert(s)
# end nocover

View File

@@ -566,11 +566,12 @@ class Response(HTTPMsg):
content: Response content
timestamp: Seconds since the epoch
"""
def __init__(self, request, code, msg, headers, content, timestamp=None):
def __init__(self, request, code, msg, headers, content, peercert, timestamp=None):
assert isinstance(headers, ODictCaseless)
self.request = request
self.code, self.msg = code, msg
self.headers, self.content = headers, content
self.peercert = peercert
self.timestamp = timestamp or utils.timestamp()
controller.Msg.__init__(self)
self.replay = False
@@ -640,6 +641,7 @@ class Response(HTTPMsg):
self.headers = ODictCaseless._from_state(state["headers"])
self.content = state["content"]
self.timestamp = state["timestamp"]
self.peercert = state["peercert"]
def _get_state(self):
return dict(
@@ -647,6 +649,7 @@ class Response(HTTPMsg):
msg = self.msg,
headers = self.headers._get_state(),
timestamp = self.timestamp,
peercert = self.peercert,
content = self.content
)
@@ -658,6 +661,7 @@ class Response(HTTPMsg):
str(state["msg"]),
ODictCaseless._from_state(state["headers"]),
state["content"],
state.get("peercert"),
state["timestamp"],
)

View File

@@ -229,6 +229,7 @@ class ServerConnection:
self.port = request.port
self.scheme = request.scheme
self.close = False
self.cert = None
self.server, self.rfile, self.wfile = None, None, None
self.connect()
@@ -239,6 +240,8 @@ class ServerConnection:
if self.scheme == "https":
server = ssl.wrap_socket(server)
server.connect((addr, self.port))
if self.scheme == "https":
self.cert = server.getpeercert(True)
except socket.error, err:
raise ProxyError(502, 'Error connecting to "%s": %s' % (self.host, err))
self.server = server
@@ -275,7 +278,7 @@ class ServerConnection:
content = ""
else:
content = read_http_body(self.rfile, self, headers, True, self.config.body_size_limit)
return flow.Response(self.request, code, msg, headers, content)
return flow.Response(self.request, code, msg, headers, content, self.cert)
def terminate(self):
try:

View File

@@ -1,2 +1,2 @@
IVERSION = (0, 7)
IVERSION = (0, 8)
VERSION = ".".join(str(i) for i in IVERSION)