1
0
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:
James Kasten
2015-05-18 10:28:55 -07:00
5 changed files with 102 additions and 2 deletions

View File

@@ -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)

View File

@@ -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

View 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-----

View File

@@ -0,0 +1,8 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBFTCBwAIBADBbMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtleGFt
cGxlLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQD0thFxUTc2v6qV55wRxfwn
BUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3HgBVy9ddFc8RX4vNZaR+ROXNEz
AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAMikGL8Ch7hQCStXH7chhDp6+pt2+VSo
wgsrPQ2Bw4veDMlSemUrH+4e0TwbbntHfvXTDHWs9P3BiIDJLxFrjuA=
-----END CERTIFICATE REQUEST-----

View File

@@ -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',