X509 extensions issue#646 (#666)

* Allowing that create_csr can be called with an additional flag in the csr_config to adjust the BasicConstraints for a CA.

* If there are no SANs, skip adding a blank list of SANs.

* Adding handling for all the extended key usage, key usage, and subject key identifier extensions.

* Fixing lint checks. I was overly verbose.

* This implements marshalling of the certificate extensions into x509 ExtensionType objects in the schema validation code.

* Will create x509 ExtensionType objects in the schema validation stage
* Allows errors parsing incoming options to bubble up to the requestor as ValidationErrors.
* Cleans up create_csr a lot in the certificates/service.py
* Makes BasicConstraints _just another extension_, rather than a hard-coded one
* Adds BasicConstraints option for path_length to the UI for creating an authority
* Removes SAN types which cannot be handled from the UI for authorities and certificates.
* Fixes Certificate() object model so that it doesn't just hard-code only SAN records in the extensions property and actually returns the extensions how you expect to see them. Since Lemur is focused on using these data in the "CSR" phase of things, extensions that don't get populated until signing will be in dict() form.* Trying out schema validation of extensions
This commit is contained in:
Neil Schelly 2017-01-27 15:31:29 -05:00 committed by kevgliss
parent 4af871f408
commit f13a3505f3
8 changed files with 326 additions and 217 deletions

View File

@ -9,6 +9,7 @@ import arrow
from flask import current_app
from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import rsa
from sqlalchemy.orm import relationship
@ -229,16 +230,39 @@ class Certificate(db.Model):
@property
def extensions(self):
# TODO pull the OU, O, CN, etc + other extensions.
names = [{'name_type': 'DNSName', 'value': x.name} for x in self.domains]
return_extensions = {}
cert = lemur.common.utils.parse_certificate(self.body)
for extension in cert.extensions:
if isinstance(extension, x509.BasicConstraints):
return_extensions['basic_constraints'] = extension
elif isinstance(extension, x509.SubjectAlternativeName):
return_extensions['sub_alt_names'] = extension
elif isinstance(extension, x509.ExtendedKeyUsage):
return_extensions['extended_key_usage'] = extension
elif isinstance(extension, x509.KeyUsage):
return_extensions['key_usage'] = extension
elif isinstance(extension, x509.SubjectKeyIdentifier):
return_extensions['subject_key_identifier'] = {'include_ski': True}
elif isinstance(extension, x509.AuthorityInformationAccess):
return_extensions['certificate_info_access'] = {'include_aia': True}
elif isinstance(extension, x509.AuthorityKeyIdentifier):
aki = {
'use_key_identifier': False,
'use_authority_cert': False
}
if extension.key_identifier:
aki['use_key_identifier'] = True
if extension.authority_cert_issuer:
aki['use_authority_cert'] = True
return_extensions['authority_key_identifier'] = aki
elif isinstance(extension, x509.CRLDistributionPoints):
# FIXME: Don't support CRLDistributionPoints yet https://github.com/Netflix/lemur/issues/662
pass
else:
# FIXME: Not supporting custom OIDs yet. https://github.com/Netflix/lemur/issues/665
pass
extensions = {
'sub_alt_names': {
'names': names
}
}
return extensions
return return_extensions
def get_arn(self, account_number):
"""

View File

@ -345,65 +345,23 @@ def create_csr(**csr_config):
x509.NameAttribute(x509.OID_EMAIL_ADDRESS, csr_config['owner'])
]))
builder = builder.add_extension(
x509.BasicConstraints(ca=False, path_length=None), critical=True,
)
extensions = csr_config.get('extensions', {})
critical_extensions = ['basic_constraints', 'sub_alt_names', 'key_usage']
noncritical_extensions = ['extended_key_usage']
for k, v in extensions.items():
if k in critical_extensions and v:
current_app.logger.debug("Add CExt: {0} {1}".format(k, v))
builder = builder.add_extension(v, critical=True)
if k in noncritical_extensions and v:
current_app.logger.debug("Add Ext: {0} {1}".format(k, v))
builder = builder.add_extension(v, critical=False)
if csr_config.get('extensions'):
for k, v in csr_config.get('extensions', {}).items():
if k == 'sub_alt_names':
# map types to their x509 objects
general_names = []
for name in v['names']:
if name['name_type'] == 'DNSName':
general_names.append(x509.DNSName(name['value']))
builder = builder.add_extension(
x509.SubjectAlternativeName(general_names), critical=True
)
# TODO support more CSR options, none of the authority plugins currently support these options
# builder.add_extension(
# x509.KeyUsage(
# digital_signature=digital_signature,
# content_commitment=content_commitment,
# key_encipherment=key_enipherment,
# data_encipherment=data_encipherment,
# key_agreement=key_agreement,
# key_cert_sign=key_cert_sign,
# crl_sign=crl_sign,
# encipher_only=enchipher_only,
# decipher_only=decipher_only
# ), critical=True
# )
#
# # we must maintain our own list of OIDs here
# builder.add_extension(
# x509.ExtendedKeyUsage(
# server_authentication=server_authentication,
# email=
# )
# )
#
# builder.add_extension(
# x509.AuthorityInformationAccess()
# )
#
# builder.add_extension(
# x509.AuthorityKeyIdentifier()
# )
#
# builder.add_extension(
# x509.SubjectKeyIdentifier()
# )
#
# builder.add_extension(
# x509.CRLDistributionPoints()
# )
#
# builder.add_extension(
# x509.ObjectIdentifier(oid)
# )
ski = extensions.get('subject_key_identifier', {})
if ski.get('include_ski', False):
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
critical=False
)
request = builder.sign(
private_key, hashes.SHA256(), default_backend()
@ -500,14 +458,6 @@ def get_certificate_primitives(certificate):
certificate via `create`.
"""
start, end = calculate_reissue_range(certificate.not_before, certificate.not_after)
names = [{'name_type': 'DNSName', 'value': x.name} for x in certificate.domains]
# TODO pull additional extensions
extensions = {
'sub_alt_names': {
'names': names
}
}
return dict(
authority=certificate.authority,
@ -517,7 +467,7 @@ def get_certificate_primitives(certificate):
validity_end=end,
destinations=certificate.destinations,
roles=certificate.roles,
extensions=extensions,
extensions=certificate.extensions,
owner=certificate.owner,
organization=certificate.organization,
organizational_unit=certificate.organizational_unit,

View File

@ -3,6 +3,9 @@ import warnings
from datetime import datetime as dt
from marshmallow.fields import Field
from marshmallow import utils
from cryptography import x509
from marshmallow.exceptions import ValidationError
import ipaddress
class ArrowDateTime(Field):
@ -91,3 +94,240 @@ class ArrowDateTime(Field):
warnings.warn('It is recommended that you install python-dateutil '
'for improved datetime deserialization.')
raise self.fail('invalid')
class KeyUsageExtension(Field):
"""An x509.KeyUsage ExtensionType object
Dict of KeyUsage names/values are deserialized into an x509.KeyUsage object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
return {
'useDigitalSignature': value.digital_signature,
'useNonRepudiation': value.content_commitment,
'useKeyEncipherment': value.key_encipherment,
'useDataEncipherment': value.data_encipherment,
'useKeyAgreement': value.key_agreement,
'useKeyCertSign': value.key_cert_sign,
'useCrlSign': value.crl_sign,
'useEncipherOnly': value._encipher_only,
'useDecipherOnly': value._decipher_only
}
def _deserialize(self, value, attr, data):
keyusages = {
'digital_signature': False,
'content_commitment': False,
'key_encipherment': False,
'data_encipherment': False,
'key_agreement': False,
'key_cert_sign': False,
'crl_sign': False,
'encipher_only': False,
'decipher_only': False
}
for k, v in value.items():
if k == 'useDigitalSignature':
keyusages['digital_signature'] = v
if k == 'useNonRepudiation':
keyusages['content_commitment'] = v
if k == 'useKeyEncipherment':
keyusages['key_encipherment'] = v
if k == 'useDataEncipherment':
keyusages['data_encipherment'] = v
if k == 'useKeyCertSign':
keyusages['key_cert_sign'] = v
if k == 'useCrlSign':
keyusages['crl_sign'] = v
if k == 'useEncipherOnly' and v:
keyusages['encipher_only'] = True
keyusages['key_agreement'] = True
if k == 'useDecipherOnly' and v:
keyusages['decipher_only'] = True
keyusages['key_agreement'] = True
if keyusages['encipher_only'] and keyusages['decipher_only']:
raise ValidationError('A certificate cannot have both Encipher Only and Decipher Only Extended Key Usages.')
return x509.KeyUsage(
digital_signature=keyusages['digital_signature'],
content_commitment=keyusages['content_commitment'],
key_encipherment=keyusages['key_encipherment'],
data_encipherment=keyusages['data_encipherment'],
key_agreement=keyusages['key_agreement'],
key_cert_sign=keyusages['key_cert_sign'],
crl_sign=keyusages['crl_sign'],
encipher_only=keyusages['encipher_only'],
decipher_only=keyusages['decipher_only']
)
class ExtendedKeyUsageExtension(Field):
"""An x509.ExtendedKeyUsage ExtensionType object
Dict of ExtendedKeyUsage names/values are deserialized into an x509.ExtendedKeyUsage object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
usages = value._usages
usage_list = {}
for usage in usages:
if usage.dotted_string == x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH:
usage_list["useClientAuthentication"] = True
if usage.dotted_string == x509.oid.ExtendedKeyUsageOID.SERVER_AUTH:
usage_list["useServerAuthentication"] = True
if usage.dotted_string == x509.oid.ExtendedKeyUsageOID.CODE_SIGNING:
usage_list["useCodeSigning"] = True
if usage.dotted_string == x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION:
usage_list["useEmailProtection"] = True
if usage.dotted_string == x509.oid.ExtendedKeyUsageOID.TIME_STAMPING:
usage_list["useTimestamping"] = True
if usage.dotted_string == x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING:
usage_list["useOCSPSigning"] = True
if usage.dotted_string == "1.3.6.1.5.5.7.3.14":
usage_list["useEapOverLAN"] = True
if usage.dotted_string == "1.3.6.1.5.5.7.3.13":
usage_list["useEapOverPPP"] = True
if usage.dotted_string == "1.3.6.1.4.1.311.20.2.2":
usage_list["useSmartCardLogon"] = True
return usage_list
def _deserialize(self, value, attr, data):
usage_oids = []
for k, v in value.items():
if k == 'useClientAuthentication' and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH)
if k == 'useServerAuthentication' and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.SERVER_AUTH)
if k == 'useCodeSigning' and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.CODE_SIGNING)
if k == 'useEmailProtection' and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION)
if k == 'useTimestamping' and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.TIME_STAMPING)
if k == 'useOCSPSigning' and v:
usage_oids.append(x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING)
if k == 'useEapOverLAN' and v:
usage_oids.append(x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.3.14"))
if k == 'useEapOverPPP' and v:
usage_oids.append(x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.3.13"))
if k == 'useSmartCardLogon' and v:
usage_oids.append(x509.oid.ObjectIdentifier("1.3.6.1.4.1.311.20.2.2"))
return x509.ExtendedKeyUsage(usage_oids)
class BasicConstraintsExtension(Field):
"""An x509.BasicConstraints ExtensionType object
Dict of CA boolean and a path_length integer names/values are deserialized into an x509.BasicConstraints object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
return {'ca': value.ca(), 'path_length': value.path_length()}
def _deserialize(self, value, attr, data):
ca = value.get('ca', False)
path_length = value.get('path_length', None)
if ca:
if not isinstance(path_length, (type(None), int)):
raise ValidationError('A CA certificate path_length (for BasicConstraints) must be None or an integer.')
return x509.BasicConstraints(ca=True, path_length=path_length)
else:
return x509.BasicConstraints(ca=False, path_length=None)
class SubjectAlternativeNameExtension(Field):
"""An x509.SubjectAlternativeName ExtensionType object
Dict of CA boolean and a path_length integer names/values are deserialized into an x509.BasicConstraints object
and back.
:param kwargs: The same keyword arguments that :class:`Field` receives.
"""
def _serialize(self, value, attr, obj):
general_names = []
for name in value._general_names:
value = name.value()
if isinstance(name, x509.DNSName):
name_type = 'DNSName'
if isinstance(name, x509.IPAddress):
name_type = 'IPAddress'
value = str(value)
if isinstance(name, x509.UniformResourceIdentifier):
name_type = 'uniformResourceIdentifier'
if isinstance(name, x509.DirectoryName):
name_type = 'directoryName'
if isinstance(name, x509.RFC822Name):
name_type = 'rfc822Name'
if isinstance(name, x509.RegisteredID):
name_type = 'registeredID'
value = value.dotted_string
general_names.append({'nameType': name_type, 'value': value})
return general_names
def _deserialize(self, value, attr, data):
general_names = []
for name in value.get('names', []):
if name['nameType'] == 'DNSName':
general_names.append(x509.DNSName(name['value']))
if name['nameType'] == 'IPAddress':
general_names.append(x509.IPAddress(ipaddress.ip_address(name['value'])))
if name['nameType'] == 'IPNetwork':
general_names.append(x509.IPAddress(ipaddress.ip_network(name['value'])))
if name['nameType'] == 'uniformResourceIdentifier':
general_names.append(x509.UniformResourceIdentifier(name['value']))
if name['nameType'] == 'directoryName':
# FIXME: Need to parse a string in name['value'] like:
# 'CN=Common Name, O=Org Name, OU=OrgUnit Name, C=US, ST=ST, L=City/emailAddress=person@example.com'
# or
# 'CN=Common Name/O=Org Name/OU=OrgUnit Name/C=US/ST=NH/L=City/emailAddress=person@example.com'
# and turn it into something like:
# x509.Name([
# x509.NameAttribute(x509.OID_COMMON_NAME, "Common Name"),
# x509.NameAttribute(x509.OID_ORGANIZATION_NAME, "Org Name"),
# x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, "OrgUnit Name"),
# x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
# x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, "NH"),
# x509.NameAttribute(x509.OID_LOCALITY_NAME, "City"),
# x509.NameAttribute(x509.OID_EMAIL_ADDRESS, "person@example.com")
# ]
# general_names.append(x509.DirectoryName(x509.Name(BLAH))))
pass
if name['nameType'] == 'rfc822Name':
general_names.append(x509.RFC822Name(name['value']))
if name['nameType'] == 'registeredID':
general_names.append(x509.RegisteredID(x509.ObjectIdentifier(name['value'])))
if name['nameType'] == 'otherName':
# This has two inputs (type and value), so it doesn't fit the mold of the rest of these GeneralName entities.
# general_names.append(x509.OtherName(name['type'], bytes(name['value']), 'utf-8'))
pass
if name['nameType'] == 'x400Address':
# The Python Cryptography library doesn't support x400Address types (yet?)
pass
if name['nameType'] == 'EDIPartyName':
# The Python Cryptography library doesn't support EDIPartyName types (yet?)
pass
if general_names:
return x509.SubjectAlternativeName(general_names)
else:
return None

View File

@ -9,11 +9,12 @@
"""
from sqlalchemy.orm.exc import NoResultFound
from marshmallow import fields, post_load, pre_load, post_dump, validates_schema
from marshmallow import fields, post_load, pre_load, post_dump
from marshmallow.exceptions import ValidationError
from lemur.common import validators
from lemur.common.schema import LemurSchema, LemurInputSchema, LemurOutputSchema
from lemur.common.fields import KeyUsageExtension, ExtendedKeyUsageExtension, BasicConstraintsExtension, SubjectAlternativeNameExtension
from lemur.plugins import plugins
from lemur.roles.models import Role
@ -166,10 +167,6 @@ class BaseExtensionSchema(LemurSchema):
return data
class BasicConstraintsSchema(BaseExtensionSchema):
pass
class AuthorityKeyIdentifierSchema(BaseExtensionSchema):
use_key_identifier = fields.Boolean()
use_authority_cert = fields.Boolean()
@ -183,30 +180,6 @@ class CertificateInfoAccessSchema(BaseExtensionSchema):
return {'includeAIA': data['include_aia']}
class KeyUsageSchema(BaseExtensionSchema):
use_crl_sign = fields.Boolean()
use_data_encipherment = fields.Boolean()
use_decipher_only = fields.Boolean()
use_encipher_only = fields.Boolean()
use_key_encipherment = fields.Boolean()
use_digital_signature = fields.Boolean()
use_non_repudiation = fields.Boolean()
use_key_agreement = fields.Boolean()
use_key_cert_sign = fields.Boolean()
class ExtendedKeyUsageSchema(BaseExtensionSchema):
use_server_authentication = fields.Boolean()
use_client_authentication = fields.Boolean()
use_eap_over_lan = fields.Boolean()
use_eap_over_ppp = fields.Boolean()
use_ocsp_signing = fields.Boolean()
use_smart_card_logon = fields.Boolean()
use_timestamping = fields.Boolean()
use_code_signing = fields.Boolean()
use_email_protection = fields.Boolean()
class SubjectKeyIdentifierSchema(BaseExtensionSchema):
include_ski = fields.Boolean()
@ -215,20 +188,6 @@ class SubjectKeyIdentifierSchema(BaseExtensionSchema):
return {'includeSKI': data['include_ski']}
class SubAltNameSchema(BaseExtensionSchema):
name_type = fields.String(validate=validators.sub_alt_type)
value = fields.String()
@validates_schema
def check_sensitive(self, data):
if data.get('name_type') == 'DNSName':
validators.sensitive_domain(data['value'])
class SubAltNamesSchema(BaseExtensionSchema):
names = fields.Nested(SubAltNameSchema, many=True)
class CustomOIDSchema(BaseExtensionSchema):
oid = fields.String()
encoding = fields.String(validate=validators.encoding)
@ -237,13 +196,15 @@ class CustomOIDSchema(BaseExtensionSchema):
class ExtensionSchema(BaseExtensionSchema):
basic_constraints = fields.Nested(BasicConstraintsSchema)
key_usage = fields.Nested(KeyUsageSchema)
extended_key_usage = fields.Nested(ExtendedKeyUsageSchema)
basic_constraints = BasicConstraintsExtension()
key_usage = KeyUsageExtension()
extended_key_usage = ExtendedKeyUsageExtension()
subject_key_identifier = fields.Nested(SubjectKeyIdentifierSchema)
sub_alt_names = fields.Nested(SubAltNamesSchema)
sub_alt_names = SubjectAlternativeNameExtension()
authority_key_identifier = fields.Nested(AuthorityKeyIdentifierSchema)
certificate_info_access = fields.Nested(CertificateInfoAccessSchema)
# FIXME: Convert custom OIDs to a custom field in fields.py like other Extensions
# FIXME: Remove support in UI for Critical custom extensions https://github.com/Netflix/lemur/issues/665
custom = fields.List(fields.Nested(CustomOIDSchema))

View File

@ -4,7 +4,7 @@
Subject Alternate Names
</label>
<div class="col-sm-3">
<select class="form-control" ng-model="authority.subAltType" ng-init="null" ng-options="item for item in ['DNSName', 'IPAddress', 'uniformResourceIdentifier', 'directoryName','rfc822Name', 'registeredID', 'otherName', 'x400Address', 'EDIPartyName']"></select>
<select class="form-control" ng-model="authority.subAltType" ng-init="null" ng-options="item for item in ['DNSName', 'IPAddress', 'IPNetwork', 'uniformResourceIdentifier', 'directoryName','rfc822Name', 'registeredID']"></select>
</div>
<div class="col-sm-5">
<div class="input-group">
@ -185,6 +185,16 @@
<select class="form-control" ng-model="authority.extensions.cRLDistributionPoints.includeCRLDP" ng-options="item for item in ['yes', 'no', 'default']"></select>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2">
Basic Constraints
</label>
<div class="col-sm-8">
<label tooltip-trigger="mouseenter" tooltip-placement="top" uib-tooltip="Maximum number of non-self-issued intermediate certificates that may follow this certificate in a valid certification path." >
Path Length: <select class="form-control" ng-model="authority.extensions.basicConstraints.path_length" ng-options="item for item in ['None', 0, 1, 2, 3, 4, 5]"></select>
</label>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2">
Custom

View File

@ -94,6 +94,15 @@ angular.module('lemur')
AuthorityService.create = function (authority) {
authority.attachSubAltName();
authority.attachCustom();
if (authority.extensions.basicConstraints === undefined) {
authority.extensions.basicConstraints = { 'path_length': null};
}
authority.extensions.basicConstraints.ca = true;
if (authority.extensions.basicConstraints.path_length === 'None') {
authority.extensions.basicConstraints.path_length = null;
}
if (authority.validityYears === '') { // if a user de-selects validity years we ignore it
delete authority.validityYears;
}

View File

@ -32,7 +32,7 @@
</label>
<div class="col-sm-3">
<select class="form-control" ng-model="certificate.subAltType"
ng-options="item for item in ['DNSName', 'IPAddress', 'uniformResourceIdentifier', 'directoryName','rfc822Name', 'registeredID', 'otherName', 'x400Address', 'EDIPartyName']"></select>
ng-options="item for item in ['DNSName', 'IPAddress', 'IPNetwork', 'uniformResourceIdentifier', 'directoryName','rfc822Name', 'registeredID']"></select>
</div>
<div class="col-sm-6">
<div class="input-group">

View File

@ -6,6 +6,7 @@ import datetime
import arrow
from freezegun import freeze_time
from cryptography import x509
from lemur.certificates.views import * # noqa
@ -29,7 +30,7 @@ def test_get_or_increase_name(session, certificate):
def test_get_certificate_primitives(certificate):
from lemur.certificates.service import get_certificate_primitives
names = [{'name_type': 'DNSName', 'value': x.name} for x in certificate.domains]
names = [x509.DNSName(x.name) for x in certificate.domains]
data = {
'common_name': certificate.cn,
@ -37,9 +38,7 @@ def test_get_certificate_primitives(certificate):
'authority': certificate.authority,
'description': certificate.description,
'extensions': {
'sub_alt_names': {
'names': names
}
'sub_alt_names': x509.SubjectAlternativeName(names)
},
'destinations': [],
'roles': [],
@ -171,10 +170,10 @@ def test_certificate_input_with_extensions(client, authority):
'description': 'testtestest',
'extensions': {
'keyUsage': {
'useKeyEncipherment': True,
'useDigitalSignature': True
'digital_signature': True
},
'extendedKeyUsage': {
'useClientAuthentication': True,
'useServerAuthentication': True
},
'subjectKeyIdentifier': {
@ -245,90 +244,6 @@ def test_certificate_valid_dates(client, authority):
assert not errors
def test_sub_alt_name_schema(session):
from lemur.schemas import SubAltNameSchema # SubAltNamesSchema
input_data = {'nameType': 'DNSName', 'value': 'test.example.com'}
data, errors = SubAltNameSchema().load(input_data)
assert not errors
assert data == {'name_type': 'DNSName', 'value': 'test.example.com'}
data, errors = SubAltNameSchema().dumps(data)
assert not errors
input_datas = {'names': [input_data]}
# data, errors = SubAltNamesSchema().load(input_datas)
# assert not errors
# assert data == {'names': [{'name_type': 'DNSName', 'value': 'test.example.com'}]}
# data, errors = SubAltNamesSchema().dumps(data)
# assert data == json.dumps(input_datas)
# assert not errors
input_data = {'nameType': 'CNAME', 'value': 'test.example.com'}
data, errors = SubAltNameSchema().load(input_data)
assert errors
def test_key_usage_schema():
from lemur.schemas import KeyUsageSchema
input_data = {
'useCRLSign': True,
'useDataEncipherment': True,
'useDecipherOnly': True,
'useEncipherOnly': True,
'useKeyEncipherment': True,
'useDigitalSignature': True,
'useNonRepudiation': True
}
data, errors = KeyUsageSchema().load(input_data)
assert not errors
assert data == {
'use_crl_sign': True,
'use_data_encipherment': True,
'use_decipher_only': True,
'use_encipher_only': True,
'use_key_encipherment': True,
'use_digital_signature': True,
'use_non_repudiation': True
}
def test_extended_key_usage_schema():
from lemur.schemas import ExtendedKeyUsageSchema
input_data = {
'useServerAuthentication': True,
'useClientAuthentication': True,
'useEapOverLAN': True,
'useEapOverPPP': True,
'useOCSPSigning': True,
'useSmartCardLogon': True,
'useTimestamping': True,
'useCodeSigning': True,
'useEmailProtection': True
}
data, errors = ExtendedKeyUsageSchema().load(input_data)
assert not errors
assert data == {
'use_server_authentication': True,
'use_client_authentication': True,
'use_eap_over_lan': True,
'use_eap_over_ppp': True,
'use_ocsp_signing': True,
'use_smart_card_logon': True,
'use_timestamping': True,
'use_code_signing': True,
'use_email_protection': True
}
def test_create_basic_csr(client):
from cryptography import x509
from cryptography.hazmat.backends import default_backend
@ -342,7 +257,7 @@ def test_create_basic_csr(client):
location='A place',
owner='joe@example.com',
key_type='RSA2048',
extensions=dict(names=dict(sub_alt_names=['test.example.com', 'test2.example.com']))
extensions=dict(names=dict(sub_alt_names=x509.SubjectAlternativeName([x509.DNSName('test.example.com'), x509.DNSName('test2.example.com')])))
)
csr, pem = create_csr(**csr_config)
@ -395,7 +310,7 @@ def test_create_csr():
assert csr
assert private_key
extensions = {'sub_alt_names': {'names': [{'name_type': 'DNSName', 'value': 'AnotherCommonName'}]}}
extensions = {'sub_alt_names': x509.SubjectAlternativeName([x509.DNSName('AnotherCommonName')])}
csr, private_key = create_csr(owner='joe@example.com', common_name='ACommonName', organization='test', organizational_unit='Meters', country='US',
state='CA', location='Here', extensions=extensions, key_type='RSA2048')
assert csr