diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -434,6 +434,13 @@ self._check_connected() return self._sslobj.peer_certificate(binary_form) + def getpeercertchain(self, binary_form=False): + """Returns the certificate chain of the SSL connection + as tuple of dicts. Return None if no chain is provieded.""" + self._checkClosed() + self._check_connected() + return self._sslobj.peer_cert_chain(binary_form) + def selected_npn_protocol(self): self._checkClosed() if not self._sslobj or not _ssl.HAS_NPN: diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1117,6 +1117,59 @@ peer certificate, or None if no certificate was provided. This will\n\ return the certificate even if it wasn't validated."); + +PyDoc_STRVAR(PySSL_peercertchain_doc, +"peer_cert_chain([der=False]) -> tuple of certificates\n\ +\n\ +Returns certificate chain for the peer. If no chain is provided, returns\n\ +None. Otherwise returns a tuple of dicts containing information about the\n\ +certificates. The chain starts with the leaf certificate and ends with the\n\ +root certificate. If called on the client side, the leaf certificate is the\n\ +peer's certificate.\n\ +\n\ +If the optional argument is True, return a list of DER-encoded copies of the\n\ +certificates."); + +static PyObject * +PySSL_peercertchain(PySSLSocket *self, PyObject *args) +{ + PyObject *retval = NULL, *ci=NULL; + int len, i; + int binary_mode = 0; + STACK_OF(X509) *chain; + X509 *cert; + + if (!PyArg_ParseTuple(args, "|p:peer_cert_chain", &binary_mode)) + return NULL; + + if (self->ssl == NULL) + Py_RETURN_NONE; + + chain = SSL_get_peer_cert_chain(self->ssl); + if (chain == NULL) + Py_RETURN_NONE; + + len = sk_X509_num(chain); + if ((retval = PyTuple_New(len)) == NULL) { + return NULL; + } + for (i = 0; i < len; ++i) { + cert = sk_X509_value(chain, i); + if (binary_mode) { + ci = PyBytes_FromString(""); + } else { + ci = _decode_certificate(cert); + } + if (ci == NULL) { + Py_DECREF(retval); + return NULL; + } + PyTuple_SET_ITEM(retval, i, ci); + } + return retval; +} + + static PyObject *PySSL_cipher (PySSLSocket *self) { PyObject *retval, *v; @@ -1694,6 +1747,8 @@ PySSL_SSLpending_doc}, {"peer_certificate", (PyCFunction)PySSL_peercert, METH_VARARGS, PySSL_peercert_doc}, + {"peer_cert_chain", (PyCFunction)PySSL_peercertchain, METH_VARARGS, + PySSL_peercertchain_doc}, {"cipher", (PyCFunction)PySSL_cipher, METH_NOARGS}, #ifdef OPENSSL_NPN_NEGOTIATED {"selected_npn_protocol", (PyCFunction)PySSL_selected_npn_protocol, METH_NOARGS},