Skip to content

Commit f0ed288

Browse files
authored
add Connection.use_(certificate|privatekey) (#1121)
* add `Connection.use_(certificate|privatekey)` * bump minimum cryptography version * deduplicate tests * black! * max line length
1 parent a3483a7 commit f0ed288

File tree

5 files changed

+107
-44
lines changed

5 files changed

+107
-44
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Changes:
2929
- Add ``OpenSSL.SSL.Connection.set_verify`` and ``OpenSSL.SSL.Connection.get_verify_mode``
3030
to override the context object's verification flags.
3131
`#1073 <https://github.com/pyca/pyopenssl/pull/1073>`_
32+
- Add ``OpenSSL.SSL.Connection.use_certificate`` and ``OpenSSL.SSL.Connection.use_privatekey``
33+
to set a certificate per connection (and not just per context) `#1121 <https://github.com/pyca/pyopenssl/pull/1121>`_.
3234

3335
22.0.0 (2022-01-29)
3436
-------------------

setup.py

100755100644
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def find_meta(meta):
9696
package_dir={"": "src"},
9797
install_requires=[
9898
# Fix cryptographyMinimum in tox.ini when changing this!
99-
"cryptography>=37.0.2,<39",
99+
"cryptography>=38.0.0,<39",
100100
],
101101
extras_require={
102102
"test": ["flaky", "pretend", "pytest>=3.0.1"],

src/OpenSSL/SSL.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ def use_certificate(self, cert):
962962
:param cert: The X509 object
963963
:return: None
964964
"""
965+
# Mirrored at Connection.use_certificate
965966
if not isinstance(cert, X509):
966967
raise TypeError("cert must be an X509 instance")
967968

@@ -1023,6 +1024,7 @@ def use_privatekey(self, pkey):
10231024
:param pkey: The PKey object
10241025
:return: None
10251026
"""
1027+
# Mirrored at Connection.use_privatekey
10261028
if not isinstance(pkey, PKey):
10271029
raise TypeError("pkey must be a PKey instance")
10281030

@@ -1788,6 +1790,36 @@ def get_verify_mode(self):
17881790
"""
17891791
return _lib.SSL_get_verify_mode(self._ssl)
17901792

1793+
def use_certificate(self, cert):
1794+
"""
1795+
Load a certificate from a X509 object
1796+
1797+
:param cert: The X509 object
1798+
:return: None
1799+
"""
1800+
# Mirrored from Context.use_certificate
1801+
if not isinstance(cert, X509):
1802+
raise TypeError("cert must be an X509 instance")
1803+
1804+
use_result = _lib.SSL_use_certificate(self._ssl, cert._x509)
1805+
if not use_result:
1806+
_raise_current_error()
1807+
1808+
def use_privatekey(self, pkey):
1809+
"""
1810+
Load a private key from a PKey object
1811+
1812+
:param pkey: The PKey object
1813+
:return: None
1814+
"""
1815+
# Mirrored from Context.use_privatekey
1816+
if not isinstance(pkey, PKey):
1817+
raise TypeError("pkey must be a PKey instance")
1818+
1819+
use_result = _lib.SSL_use_PrivateKey(self._ssl, pkey._pkey)
1820+
if not use_result:
1821+
self._context._raise_passphrase_exception()
1822+
17911823
def set_ciphertext_mtu(self, mtu):
17921824
"""
17931825
For DTLS, set the maximum UDP payload size (*not* including IP/UDP

tests/test_ssl.py

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
socket,
3030
)
3131
from sys import getfilesystemencoding, platform
32+
from typing import Union
3233
from warnings import simplefilter
3334
from weakref import ref
3435

@@ -621,17 +622,6 @@ def test_type(self):
621622
"""
622623
assert is_consistent_type(Context, "Context", TLSv1_METHOD)
623624

624-
def test_use_privatekey(self):
625-
"""
626-
`Context.use_privatekey` takes an `OpenSSL.crypto.PKey` instance.
627-
"""
628-
key = PKey()
629-
key.generate_key(TYPE_RSA, 1024)
630-
ctx = Context(SSLv23_METHOD)
631-
ctx.use_privatekey(key)
632-
with pytest.raises(TypeError):
633-
ctx.use_privatekey("")
634-
635625
def test_use_privatekey_file_missing(self, tmpfile):
636626
"""
637627
`Context.use_privatekey_file` raises `OpenSSL.SSL.Error` when passed
@@ -685,37 +675,6 @@ def test_use_privatekey_file_unicode(self, tmpfile):
685675
FILETYPE_PEM,
686676
)
687677

688-
def test_use_certificate_wrong_args(self):
689-
"""
690-
`Context.use_certificate_wrong_args` raises `TypeError` when not passed
691-
exactly one `OpenSSL.crypto.X509` instance as an argument.
692-
"""
693-
ctx = Context(SSLv23_METHOD)
694-
with pytest.raises(TypeError):
695-
ctx.use_certificate("hello, world")
696-
697-
def test_use_certificate_uninitialized(self):
698-
"""
699-
`Context.use_certificate` raises `OpenSSL.SSL.Error` when passed a
700-
`OpenSSL.crypto.X509` instance which has not been initialized
701-
(ie, which does not actually have any certificate data).
702-
"""
703-
ctx = Context(SSLv23_METHOD)
704-
with pytest.raises(Error):
705-
ctx.use_certificate(X509())
706-
707-
def test_use_certificate(self):
708-
"""
709-
`Context.use_certificate` sets the certificate which will be
710-
used to identify connections created using the context.
711-
"""
712-
# TODO
713-
# Hard to assert anything. But we could set a privatekey then ask
714-
# OpenSSL if the cert and key agree using check_privatekey. Then as
715-
# long as check_privatekey works right we're good...
716-
ctx = Context(SSLv23_METHOD)
717-
ctx.use_certificate(load_certificate(FILETYPE_PEM, root_cert_pem))
718-
719678
def test_use_certificate_file_wrong_args(self):
720679
"""
721680
`Context.use_certificate_file` raises `TypeError` if the first
@@ -2180,6 +2139,76 @@ def test_construction(self):
21802139
assert isinstance(new_session, Session)
21812140

21822141

2142+
@pytest.fixture(params=["context", "connection"])
2143+
def ctx_or_conn(request) -> Union[Context, Connection]:
2144+
ctx = Context(SSLv23_METHOD)
2145+
if request.param == "context":
2146+
return ctx
2147+
else:
2148+
return Connection(ctx, None)
2149+
2150+
2151+
class TestContextConnection:
2152+
"""
2153+
Unit test for methods that are exposed both by Connection and Context
2154+
objects.
2155+
"""
2156+
2157+
def test_use_privatekey(self, ctx_or_conn):
2158+
"""
2159+
`use_privatekey` takes an `OpenSSL.crypto.PKey` instance.
2160+
"""
2161+
key = PKey()
2162+
key.generate_key(TYPE_RSA, 1024)
2163+
2164+
ctx_or_conn.use_privatekey(key)
2165+
with pytest.raises(TypeError):
2166+
ctx_or_conn.use_privatekey("")
2167+
2168+
def test_use_privatekey_wrong_key(self, ctx_or_conn):
2169+
"""
2170+
`use_privatekey` raises `OpenSSL.SSL.Error` when passed a
2171+
`OpenSSL.crypto.PKey` instance which has not been initialized.
2172+
"""
2173+
key = PKey()
2174+
key.generate_key(TYPE_RSA, 1024)
2175+
ctx_or_conn.use_certificate(
2176+
load_certificate(FILETYPE_PEM, root_cert_pem)
2177+
)
2178+
with pytest.raises(Error):
2179+
ctx_or_conn.use_privatekey(key)
2180+
2181+
def test_use_certificate(self, ctx_or_conn):
2182+
"""
2183+
`use_certificate` sets the certificate which will be
2184+
used to identify connections created using the context.
2185+
"""
2186+
# TODO
2187+
# Hard to assert anything. But we could set a privatekey then ask
2188+
# OpenSSL if the cert and key agree using check_privatekey. Then as
2189+
# long as check_privatekey works right we're good...
2190+
ctx_or_conn.use_certificate(
2191+
load_certificate(FILETYPE_PEM, root_cert_pem)
2192+
)
2193+
2194+
def test_use_certificate_wrong_args(self, ctx_or_conn):
2195+
"""
2196+
`use_certificate_wrong_args` raises `TypeError` when not passed
2197+
exactly one `OpenSSL.crypto.X509` instance as an argument.
2198+
"""
2199+
with pytest.raises(TypeError):
2200+
ctx_or_conn.use_certificate("hello, world")
2201+
2202+
def test_use_certificate_uninitialized(self, ctx_or_conn):
2203+
"""
2204+
`use_certificate` raises `OpenSSL.SSL.Error` when passed a
2205+
`OpenSSL.crypto.X509` instance which has not been initialized
2206+
(ie, which does not actually have any certificate data).
2207+
"""
2208+
with pytest.raises(Error):
2209+
ctx_or_conn.use_certificate(X509())
2210+
2211+
21832212
class TestConnection:
21842213
"""
21852214
Unit tests for `OpenSSL.SSL.Connection`.

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extras =
1010
deps =
1111
coverage>=4.2
1212
cryptographyMain: git+https://github.com/pyca/cryptography.git
13-
cryptographyMinimum: cryptography==37.0.2
13+
cryptographyMinimum: cryptography==38.0.0
1414
randomorder: pytest-randomly
1515
setenv =
1616
# Do not allow the executing environment to pollute the test environment

0 commit comments

Comments
 (0)