mirror of
https://github.com/certbot/certbot.git
synced 2026-01-21 19:01:07 +03:00
Merge pull request #399 from kuba/get_sans
get_sans_from_csr using pyOpenSSL
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Let's Encrypt client crypto utility functions
|
||||
"""Let's Encrypt client crypto utility functions.
|
||||
|
||||
.. todo:: Make the transition to use PSS rather than PKCS1_v1_5 when the server
|
||||
is capable of handling the signatures.
|
||||
@@ -13,6 +13,7 @@ import Crypto.PublicKey.RSA
|
||||
import Crypto.Signature.PKCS1_v1_5
|
||||
|
||||
import M2Crypto
|
||||
import OpenSSL
|
||||
|
||||
from letsencrypt import le_util
|
||||
|
||||
@@ -231,3 +232,44 @@ def make_ss_cert(key_str, domains, not_before=None,
|
||||
assert cert.verify()
|
||||
# print check_purpose(,0
|
||||
return cert.as_pem()
|
||||
|
||||
|
||||
def _request_san(req): # TODO: implement directly in PyOpenSSL!
|
||||
# constants based on implementation of
|
||||
# OpenSSL.crypto.X509Error._subjectAltNameString
|
||||
parts_separator = ", "
|
||||
part_separator = ":"
|
||||
extension_short_name = "subjectAltName"
|
||||
|
||||
# pylint: disable=protected-access,no-member
|
||||
label = OpenSSL.crypto.X509Extension._prefixes[OpenSSL.crypto._lib.GEN_DNS]
|
||||
assert parts_separator not in label
|
||||
prefix = label + part_separator
|
||||
|
||||
extensions = [ext._subjectAltNameString().split(parts_separator)
|
||||
for ext in req.get_extensions()
|
||||
if ext.get_short_name() == extension_short_name]
|
||||
# WARNING: this function assumes that no SAN can include
|
||||
# parts_separator, hence the split!
|
||||
|
||||
return [part.split(part_separator)[1] for parts in extensions
|
||||
for part in parts if part.startswith(prefix)]
|
||||
|
||||
|
||||
def get_sans_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
"""Get list of Subject Alternative Names from signing request.
|
||||
|
||||
:param str csr: Certificate Signing Request in PEM format (must contain
|
||||
one or more subjectAlternativeNames, or the function will fail,
|
||||
raising ValueError)
|
||||
|
||||
:returns: List of referenced subject alternative names
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
try:
|
||||
request = OpenSSL.crypto.load_certificate_request(typ, csr)
|
||||
except OpenSSL.crypto.Error as error:
|
||||
logging.exception(error)
|
||||
raise
|
||||
return _request_san(request)
|
||||
|
||||
@@ -7,6 +7,7 @@ import tempfile
|
||||
import unittest
|
||||
|
||||
import M2Crypto
|
||||
import OpenSSL
|
||||
import mock
|
||||
|
||||
|
||||
@@ -150,5 +151,41 @@ class MakeSSCertTest(unittest.TestCase):
|
||||
make_ss_cert(RSA512_KEY, ['example.com', 'www.example.com'])
|
||||
|
||||
|
||||
class GetSansFromCsrTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.get_sans_from_csr."""
|
||||
def test_extract_one_san(self):
|
||||
from letsencrypt.crypto_util import get_sans_from_csr
|
||||
csr = pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', 'csr.pem'))
|
||||
self.assertEqual(get_sans_from_csr(csr), ['example.com'])
|
||||
|
||||
def test_extract_two_sans(self):
|
||||
from letsencrypt.crypto_util import get_sans_from_csr
|
||||
csr = pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', 'csr-san.pem'))
|
||||
self.assertEqual(get_sans_from_csr(csr), ['example.com',
|
||||
'www.example.com'])
|
||||
|
||||
def test_extract_six_sans(self):
|
||||
from letsencrypt.crypto_util import get_sans_from_csr
|
||||
csr = pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', 'csr-6sans.pem'))
|
||||
self.assertEqual(get_sans_from_csr(csr),
|
||||
["example.com", "example.org", "example.net",
|
||||
"example.info", "subdomain.example.com",
|
||||
"other.subdomain.example.com"])
|
||||
|
||||
def test_parse_non_csr(self):
|
||||
from letsencrypt.crypto_util import get_sans_from_csr
|
||||
self.assertRaises(OpenSSL.crypto.Error, get_sans_from_csr,
|
||||
"hello there")
|
||||
|
||||
def test_parse_no_sans(self):
|
||||
from letsencrypt.crypto_util import get_sans_from_csr
|
||||
csr = pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', 'csr-nosans.pem'))
|
||||
self.assertEqual([], get_sans_from_csr(csr))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
12
letsencrypt/tests/testdata/csr-6sans.pem
vendored
Normal file
12
letsencrypt/tests/testdata/csr-6sans.pem
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBuzCCAWUCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMRIw
|
||||
EAYDVQQHEwlBbm4gQXJib3IxDDAKBgNVBAoTA0VGRjEfMB0GA1UECxMWVW5pdmVy
|
||||
c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wXDANBgkqhkiG
|
||||
9w0BAQEFAANLADBIAkEA9LYRcVE3Nr+qleecEcX8JwVDnjeG1X7ucsCasuuZM0e0
|
||||
9cmYuUzxIkMjO/9x4AVcvXXRXPEV+LzWWkfkTlzRMwIDAQABoIGGMIGDBgkqhkiG
|
||||
9w0BCQ4xdjB0MHIGA1UdEQRrMGmCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZ4IL
|
||||
ZXhhbXBsZS5uZXSCDGV4YW1wbGUuaW5mb4IVc3ViZG9tYWluLmV4YW1wbGUuY29t
|
||||
ghtvdGhlci5zdWJkb21haW4uZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADQQBd
|
||||
k4BE5qvEvkYoZM/2++Xd9RrQ6wsdj0QiJQCozfsI4lQx6ZJnbtNc7HpDrX4W6XIv
|
||||
IvzVBz/nD11drfz/RNuX
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
8
letsencrypt/tests/testdata/csr-nosans.pem
vendored
Normal file
8
letsencrypt/tests/testdata/csr-nosans.pem
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBFTCBwAIBADBbMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
|
||||
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtleGFt
|
||||
cGxlLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQD0thFxUTc2v6qV55wRxfwn
|
||||
BUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3HgBVy9ddFc8RX4vNZaR+ROXNEz
|
||||
AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAMikGL8Ch7hQCStXH7chhDp6+pt2+VSo
|
||||
wgsrPQ2Bw4veDMlSemUrH+4e0TwbbntHfvXTDHWs9P3BiIDJLxFrjuA=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
3
setup.py
3
setup.py
@@ -38,7 +38,8 @@ install_requires = [
|
||||
'psutil>=2.1.0', # net_connections introduced in 2.1.0
|
||||
'pyasn1', # urllib3 InsecurePlatformWarning (#304)
|
||||
'pycrypto',
|
||||
'PyOpenSSL',
|
||||
# https://pyopenssl.readthedocs.org/en/latest/api/crypto.html#OpenSSL.crypto.X509Req.get_extensions
|
||||
'PyOpenSSL>=0.15',
|
||||
'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary?
|
||||
'pyrfc3339',
|
||||
'python-augeas',
|
||||
|
||||
Reference in New Issue
Block a user