Commit fb046911 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #10022: The dictionary returned by the `getpeercert()` method

of SSL sockets now has additional items such as `issuer` and `notBefore`.
parent 859c4ef0
...@@ -433,11 +433,9 @@ They also have the following additional methods and attributes: ...@@ -433,11 +433,9 @@ They also have the following additional methods and attributes:
certificate was not validated, the dict is empty. If the certificate was certificate was not validated, the dict is empty. If the certificate was
validated, it returns a dict with the keys ``subject`` (the principal for validated, it returns a dict with the keys ``subject`` (the principal for
which the certificate was issued), and ``notAfter`` (the time after which the which the certificate was issued), and ``notAfter`` (the time after which the
certificate should not be trusted). The certificate was already validated, certificate should not be trusted). If a certificate contains an instance
so the ``notBefore`` and ``issuer`` fields are not returned. If a of the *Subject Alternative Name* extension (see :rfc:`3280`), there will
certificate contains an instance of the *Subject Alternative Name* extension also be a ``subjectAltName`` key in the dictionary.
(see :rfc:`3280`), there will also be a ``subjectAltName`` key in the
dictionary.
The "subject" field is a tuple containing the sequence of relative The "subject" field is a tuple containing the sequence of relative
distinguished names (RDNs) given in the certificate's data structure for the distinguished names (RDNs) given in the certificate's data structure for the
...@@ -459,6 +457,10 @@ They also have the following additional methods and attributes: ...@@ -459,6 +457,10 @@ They also have the following additional methods and attributes:
been validated, but if :const:`CERT_NONE` was used to establish the been validated, but if :const:`CERT_NONE` was used to establish the
connection, the certificate, if present, will not have been validated. connection, the certificate, if present, will not have been validated.
.. versionchanged:: 3.2
The returned dictionary includes additional items such as ``issuer``
and ``notBefore``.
.. method:: SSLSocket.cipher() .. method:: SSLSocket.cipher()
Returns a three-value tuple containing the name of the cipher being used, the Returns a three-value tuple containing the name of the cipher being used, the
......
...@@ -109,7 +109,7 @@ class BasicSocketTests(unittest.TestCase): ...@@ -109,7 +109,7 @@ class BasicSocketTests(unittest.TestCase):
# note that this uses an 'unofficial' function in _ssl.c, # note that this uses an 'unofficial' function in _ssl.c,
# provided solely for this test, to exercise the certificate # provided solely for this test, to exercise the certificate
# parsing code # parsing code
p = ssl._ssl._test_decode_cert(CERTFILE, False) p = ssl._ssl._test_decode_cert(CERTFILE)
if support.verbose: if support.verbose:
sys.stdout.write("\n" + pprint.pformat(p) + "\n") sys.stdout.write("\n" + pprint.pformat(p) + "\n")
...@@ -1059,6 +1059,11 @@ else: ...@@ -1059,6 +1059,11 @@ else:
self.fail( self.fail(
"Missing or invalid 'organizationName' field in certificate subject; " "Missing or invalid 'organizationName' field in certificate subject; "
"should be 'Python Software Foundation'.") "should be 'Python Software Foundation'.")
self.assertIn('notBefore', cert)
self.assertIn('notAfter', cert)
before = ssl.cert_time_to_seconds(cert['notBefore'])
after = ssl.cert_time_to_seconds(cert['notAfter'])
self.assertLess(before, after)
s.close() s.close()
finally: finally:
server.stop() server.stop()
......
...@@ -60,6 +60,9 @@ Core and Builtins ...@@ -60,6 +60,9 @@ Core and Builtins
Library Library
------- -------
- Issue #10022: The dictionary returned by the ``getpeercert()`` method
of SSL sockets now has additional items such as ``issuer`` and ``notBefore``.
- ``usenetrc`` is now false by default for NNTP objects. - ``usenetrc`` is now false by default for NNTP objects.
- Issue #1926: Add support for NNTP over SSL on port 563, as well as - Issue #1926: Add support for NNTP over SSL on port 563, as well as
......
...@@ -700,7 +700,7 @@ _get_peer_alt_names (X509 *certificate) { ...@@ -700,7 +700,7 @@ _get_peer_alt_names (X509 *certificate) {
} }
static PyObject * static PyObject *
_decode_certificate (X509 *certificate, int verbose) { _decode_certificate(X509 *certificate) {
PyObject *retval = NULL; PyObject *retval = NULL;
BIO *biobuf = NULL; BIO *biobuf = NULL;
...@@ -729,65 +729,60 @@ _decode_certificate (X509 *certificate, int verbose) { ...@@ -729,65 +729,60 @@ _decode_certificate (X509 *certificate, int verbose) {
} }
Py_DECREF(peer); Py_DECREF(peer);
if (verbose) { issuer = _create_tuple_for_X509_NAME(
issuer = _create_tuple_for_X509_NAME( X509_get_issuer_name(certificate));
X509_get_issuer_name(certificate)); if (issuer == NULL)
if (issuer == NULL) goto fail0;
goto fail0; if (PyDict_SetItemString(retval, (const char *)"issuer", issuer) < 0) {
if (PyDict_SetItemString(retval, (const char *)"issuer", issuer) < 0) {
Py_DECREF(issuer);
goto fail0;
}
Py_DECREF(issuer); Py_DECREF(issuer);
goto fail0;
}
Py_DECREF(issuer);
version = PyLong_FromLong(X509_get_version(certificate) + 1); version = PyLong_FromLong(X509_get_version(certificate) + 1);
if (PyDict_SetItemString(retval, "version", version) < 0) { if (PyDict_SetItemString(retval, "version", version) < 0) {
Py_DECREF(version);
goto fail0;
}
Py_DECREF(version); Py_DECREF(version);
goto fail0;
} }
Py_DECREF(version);
/* get a memory buffer */ /* get a memory buffer */
biobuf = BIO_new(BIO_s_mem()); biobuf = BIO_new(BIO_s_mem());
if (verbose) { (void) BIO_reset(biobuf);
serialNumber = X509_get_serialNumber(certificate);
(void) BIO_reset(biobuf); /* should not exceed 20 octets, 160 bits, so buf is big enough */
serialNumber = X509_get_serialNumber(certificate); i2a_ASN1_INTEGER(biobuf, serialNumber);
/* should not exceed 20 octets, 160 bits, so buf is big enough */ len = BIO_gets(biobuf, buf, sizeof(buf)-1);
i2a_ASN1_INTEGER(biobuf, serialNumber); if (len < 0) {
len = BIO_gets(biobuf, buf, sizeof(buf)-1); _setSSLError(NULL, 0, __FILE__, __LINE__);
if (len < 0) { goto fail1;
_setSSLError(NULL, 0, __FILE__, __LINE__); }
goto fail1; sn_obj = PyUnicode_FromStringAndSize(buf, len);
} if (sn_obj == NULL)
sn_obj = PyUnicode_FromStringAndSize(buf, len); goto fail1;
if (sn_obj == NULL) if (PyDict_SetItemString(retval, "serialNumber", sn_obj) < 0) {
goto fail1;
if (PyDict_SetItemString(retval, "serialNumber", sn_obj) < 0) {
Py_DECREF(sn_obj);
goto fail1;
}
Py_DECREF(sn_obj); Py_DECREF(sn_obj);
goto fail1;
}
Py_DECREF(sn_obj);
(void) BIO_reset(biobuf); (void) BIO_reset(biobuf);
notBefore = X509_get_notBefore(certificate); notBefore = X509_get_notBefore(certificate);
ASN1_TIME_print(biobuf, notBefore); ASN1_TIME_print(biobuf, notBefore);
len = BIO_gets(biobuf, buf, sizeof(buf)-1); len = BIO_gets(biobuf, buf, sizeof(buf)-1);
if (len < 0) { if (len < 0) {
_setSSLError(NULL, 0, __FILE__, __LINE__); _setSSLError(NULL, 0, __FILE__, __LINE__);
goto fail1; goto fail1;
} }
pnotBefore = PyUnicode_FromStringAndSize(buf, len); pnotBefore = PyUnicode_FromStringAndSize(buf, len);
if (pnotBefore == NULL) if (pnotBefore == NULL)
goto fail1; goto fail1;
if (PyDict_SetItemString(retval, "notBefore", pnotBefore) < 0) { if (PyDict_SetItemString(retval, "notBefore", pnotBefore) < 0) {
Py_DECREF(pnotBefore);
goto fail1;
}
Py_DECREF(pnotBefore); Py_DECREF(pnotBefore);
goto fail1;
} }
Py_DECREF(pnotBefore);
(void) BIO_reset(biobuf); (void) BIO_reset(biobuf);
notAfter = X509_get_notAfter(certificate); notAfter = X509_get_notAfter(certificate);
...@@ -839,10 +834,9 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { ...@@ -839,10 +834,9 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) {
PyObject *filename; PyObject *filename;
X509 *x=NULL; X509 *x=NULL;
BIO *cert; BIO *cert;
int verbose = 1;
if (!PyArg_ParseTuple(args, "O&|i:test_decode_certificate", if (!PyArg_ParseTuple(args, "O&:test_decode_certificate",
PyUnicode_FSConverter, &filename, &verbose)) PyUnicode_FSConverter, &filename))
return NULL; return NULL;
if ((cert=BIO_new(BIO_s_file())) == NULL) { if ((cert=BIO_new(BIO_s_file())) == NULL) {
...@@ -864,7 +858,7 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { ...@@ -864,7 +858,7 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) {
goto fail0; goto fail0;
} }
retval = _decode_certificate(x, verbose); retval = _decode_certificate(x);
X509_free(x); X509_free(x);
fail0: fail0:
...@@ -910,7 +904,7 @@ PySSL_peercert(PySSLSocket *self, PyObject *args) ...@@ -910,7 +904,7 @@ PySSL_peercert(PySSLSocket *self, PyObject *args)
if ((verification & SSL_VERIFY_PEER) == 0) if ((verification & SSL_VERIFY_PEER) == 0)
return PyDict_New(); return PyDict_New();
else else
return _decode_certificate (self->peer_cert, 0); return _decode_certificate(self->peer_cert);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment