aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py12
-rw-r--r--src/cryptography/x509.py26
-rw-r--r--tests/test_x509.py28
-rw-r--r--tests/test_x509_ext.py32
4 files changed, 91 insertions, 7 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index bba407db..7ccb39a4 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -230,9 +230,17 @@ def _encode_subject_alt_name(backend, san):
)
gn.type = backend._lib.GEN_EMAIL
gn.d.rfc822Name = asn1_str
+ elif isinstance(alt_name, x509.UniformResourceIdentifier):
+ gn = backend._lib.GENERAL_NAME_new()
+ assert gn != backend._ffi.NULL
+ asn1_str = _encode_asn1_str(
+ backend, alt_name._encoded, len(alt_name._encoded)
+ )
+ gn.type = backend._lib.GEN_URI
+ gn.d.uniformResourceIdentifier = asn1_str
else:
- raise NotImplementedError(
- "Only DNSName and RegisteredID supported right now"
+ raise ValueError(
+ "{0} is an unknown GeneralName type".format(alt_name)
)
res = backend._lib.sk_GENERAL_NAME_push(general_names, gn)
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 8bed79e2..58e1a37c 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -13,6 +13,8 @@ import idna
import six
+from six.moves import urllib_parse
+
from cryptography import utils
from cryptography.hazmat.primitives import hashes
@@ -966,7 +968,31 @@ class UniformResourceIdentifier(object):
if not isinstance(value, six.text_type):
raise TypeError("value must be a unicode string")
+ parsed = urllib_parse.urlparse(value)
+ if not parsed.hostname:
+ netloc = ""
+ elif parsed.port:
+ netloc = (
+ idna.encode(parsed.hostname) +
+ ":{0}".format(parsed.port).encode("ascii")
+ ).decode("ascii")
+ else:
+ netloc = idna.encode(parsed.hostname).decode("ascii")
+
+ # Note that building a URL in this fashion means it should be
+ # semantically indistinguishable from the original but is not
+ # guaranteed to be exactly the same.
+ uri = urllib_parse.urlunparse((
+ parsed.scheme,
+ netloc,
+ parsed.path,
+ parsed.params,
+ parsed.query,
+ parsed.fragment
+ )).encode("ascii")
+
self._value = value
+ self._encoded = uri
value = utils.read_only_property("_value")
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 2539be47..94eeab2b 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -13,7 +13,7 @@ import pytest
import six
-from cryptography import x509
+from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends.interfaces import (
DSABackend, EllipticCurveBackend, RSABackend, X509Backend
@@ -27,6 +27,14 @@ from .hazmat.primitives.test_ec import _skip_curve_unsupported
from .utils import load_vectors_from_file
+@utils.register_interface(x509.GeneralName)
+class FakeGeneralName(object):
+ def __init__(self, value):
+ self._value = value
+
+ value = utils.read_only_property("_value")
+
+
def _load_cert(filename, loader, backend):
cert = load_vectors_from_file(
filename=filename,
@@ -1011,6 +1019,12 @@ class TestCertificateSigningRequestBuilder(object):
x509.RFC822Name(u"test@example.com"),
x509.RFC822Name(u"email"),
x509.RFC822Name(u"email@em\xe5\xefl.com"),
+ x509.UniformResourceIdentifier(
+ u"https://\u043f\u044b\u043a\u0430.cryptography"
+ ),
+ x509.UniformResourceIdentifier(
+ u"gopher://cryptography:70/some/path"
+ ),
]),
critical=False,
).sign(private_key, hashes.SHA256(), backend)
@@ -1040,6 +1054,12 @@ class TestCertificateSigningRequestBuilder(object):
x509.RFC822Name(u"test@example.com"),
x509.RFC822Name(u"email"),
x509.RFC822Name(u"email@em\xe5\xefl.com"),
+ x509.UniformResourceIdentifier(
+ u"https://\u043f\u044b\u043a\u0430.cryptography"
+ ),
+ x509.UniformResourceIdentifier(
+ u"gopher://cryptography:70/some/path"
+ ),
]
def test_invalid_asn1_othername(self, backend):
@@ -1069,13 +1089,11 @@ class TestCertificateSigningRequestBuilder(object):
x509.NameAttribute(x509.OID_COMMON_NAME, u"SAN"),
])
).add_extension(
- x509.SubjectAlternativeName([
- x509.UniformResourceIdentifier(u"http://test.com"),
- ]),
+ x509.SubjectAlternativeName([FakeGeneralName("")]),
critical=False,
)
- with pytest.raises(NotImplementedError):
+ with pytest.raises(ValueError):
builder.sign(private_key, hashes.SHA256(), backend)
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 84a40995..7b135828 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1105,6 +1105,38 @@ class TestRFC822Name(object):
assert gn._encoded == b"email@xn--eml-vla4c.com"
+class TestUniformResourceIdentifier(object):
+ def test_no_parsed_hostname(self):
+ gn = x509.UniformResourceIdentifier(u"singlelabel")
+ assert gn.value == u"singlelabel"
+
+ def test_with_port(self):
+ gn = x509.UniformResourceIdentifier(u"singlelabel:443/test")
+ assert gn.value == u"singlelabel:443/test"
+
+ def test_idna_no_port(self):
+ gn = x509.UniformResourceIdentifier(
+ u"http://\u043f\u044b\u043a\u0430.cryptography"
+ )
+ assert gn.value == u"http://\u043f\u044b\u043a\u0430.cryptography"
+ assert gn._encoded == b"http://xn--80ato2c.cryptography"
+
+ def test_idna_with_port(self):
+ gn = x509.UniformResourceIdentifier(
+ u"gopher://\u043f\u044b\u043a\u0430.cryptography:70/some/path"
+ )
+ assert gn.value == (
+ u"gopher://\u043f\u044b\u043a\u0430.cryptography:70/some/path"
+ )
+ assert gn._encoded == b"gopher://xn--80ato2c.cryptography:70/some/path"
+
+ def test_query_and_fragment(self):
+ gn = x509.UniformResourceIdentifier(
+ u"ldap://cryptography:90/path?query=true#somedata"
+ )
+ assert gn.value == u"ldap://cryptography:90/path?query=true#somedata"
+
+
class TestRegisteredID(object):
def test_not_oid(self):
with pytest.raises(TypeError):