diff options
-rwxr-xr-x | client/ipa-client-install | 27 | ||||
-rwxr-xr-x | install/tools/ipa-replica-conncheck | 15 | ||||
-rw-r--r-- | ipalib/certstore.py | 12 | ||||
-rw-r--r-- | ipalib/x509.py | 327 | ||||
-rw-r--r-- | ipapython/certdb.py | 17 | ||||
-rw-r--r-- | ipaserver/install/ca.py | 2 | ||||
-rw-r--r-- | ipaserver/install/cainstance.py | 25 | ||||
-rw-r--r-- | ipaserver/install/certs.py | 9 | ||||
-rw-r--r-- | ipaserver/install/installutils.py | 14 | ||||
-rw-r--r-- | ipaserver/install/ipa_cacert_manage.py | 103 | ||||
-rw-r--r-- | ipaserver/install/krainstance.py | 2 | ||||
-rw-r--r-- | ipaserver/plugins/cert.py | 115 | ||||
-rw-r--r-- | ipaserver/plugins/service.py | 20 | ||||
-rw-r--r-- | ipatests/test_ipalib/test_x509.py | 66 | ||||
-rw-r--r-- | ipatests/test_ipaserver/test_ldap.py | 8 | ||||
-rw-r--r-- | ipatests/test_ipaserver/test_otptoken_import.py | 4 |
16 files changed, 369 insertions, 397 deletions
diff --git a/client/ipa-client-install b/client/ipa-client-install index 639810b62..c228ea3ce 100755 --- a/client/ipa-client-install +++ b/client/ipa-client-install @@ -35,9 +35,9 @@ try: import gssapi import netifaces - import nss.nss as nss import SSSDConfig from six.moves.urllib.parse import urlparse, urlunparse + from cryptography.hazmat.primitives import serialization from ipapython.ipa_log_manager import standard_logging_setup, root_logger from ipaclient import ipadiscovery @@ -92,15 +92,10 @@ def parse_options(): if not os.path.isabs(value): raise OptionValueError("%s option '%s' is not an absolute file path" % (opt, value)) - initialized = nss.nss_is_initialized() try: - cert = x509.load_certificate_from_file(value) + x509.load_certificate_from_file(value) except Exception: raise OptionValueError("%s option '%s' is not a valid certificate file" % (opt, value)) - else: - del(cert) - if not initialized: - nss.nss_shutdown() parser.values.ca_cert_file = value @@ -300,10 +295,10 @@ def cert_summary(msg, certs, indent=' '): else: s = '' for cert in certs: - s += '%sSubject: %s\n' % (indent, cert.subject) - s += '%sIssuer: %s\n' % (indent, cert.issuer) - s += '%sValid From: %s\n' % (indent, cert.valid_not_before_str) - s += '%sValid Until: %s\n' % (indent, cert.valid_not_after_str) + s += '%sSubject: %s\n' % (indent, DN(cert.subject)) + s += '%sIssuer: %s\n' % (indent, DN(cert.issuer)) + s += '%sValid From: %s\n' % (indent, cert.not_valid_before) + s += '%sValid Until: %s\n' % (indent, cert.not_valid_after) s += '\n' s = s[:-1] @@ -2148,7 +2143,10 @@ def get_ca_certs(fstore, options, server, basedn, realm): if ca_certs is not None: try: - ca_certs = [cert.der_data for cert in ca_certs] + ca_certs = [ + cert.public_bytes(serialization.Encoding.DER) + for cert in ca_certs + ] x509.write_certificate_list(ca_certs, ca_file) except Exception as e: if os.path.exists(ca_file): @@ -2815,7 +2813,10 @@ def install(options, env, fstore, statestore): # Add CA certs to a temporary NSS database ca_certs = x509.load_certificate_list_from_file(CACERT) - ca_certs = [cert.der_data for cert in ca_certs] + ca_certs = [ + cert.public_bytes(serialization.Encoding.DER) + for cert in ca_certs + ] try: pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password()) tmp_db.create_db(pwd_file.name) diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck index 067afb7b0..4045e41df 100755 --- a/install/tools/ipa-replica-conncheck +++ b/install/tools/ipa-replica-conncheck @@ -21,6 +21,7 @@ from __future__ import print_function from ipapython.config import IPAOptionParser +from ipapython.dn import DN from ipapython import version from ipapython import ipautil, certdb from ipalib import api, errors, x509 @@ -40,7 +41,7 @@ from socket import SOCK_STREAM, SOCK_DGRAM import distutils.spawn from ipaplatform.paths import paths import gssapi -from nss import nss +from cryptography.hazmat.primitives import serialization CONNECT_TIMEOUT = 5 RESPONDERS = [ ] @@ -121,16 +122,12 @@ def parse_options(): raise OptionValueError( "%s option '%s' is not an absolute file path" % (opt, value)) - initialized = nss.nss_is_initialized() try: x509.load_certificate_list_from_file(value) except Exception: raise OptionValueError( "%s option '%s' is not a valid certificate file" % (opt, value)) - finally: - if not initialized: - nss.nss_shutdown() parser.values.ca_cert_file = value @@ -472,12 +469,12 @@ def main(): nss_db.create_db(password_file.name) ca_certs = x509.load_certificate_list_from_file( - options.ca_cert_file, dbdir=nss_db.secdir) + options.ca_cert_file) for ca_cert in ca_certs: + data = ca_cert.public_bytes( + serialization.Encoding.DER) nss_db.add_cert( - ca_cert.der_data, str(ca_cert.subject), 'C,,') - del ca_cert - del ca_certs + data, str(DN(ca_cert.subject)), 'C,,') else: nss_dir = None diff --git a/ipalib/certstore.py b/ipalib/certstore.py index d17cb2baa..70ae94210 100644 --- a/ipalib/certstore.py +++ b/ipalib/certstore.py @@ -22,7 +22,6 @@ LDAP shared certificate store. """ -from nss.error import NSPRError from pyasn1.error import PyAsn1Error from ipapython.dn import DN @@ -31,11 +30,12 @@ from ipalib import errors, x509 def _parse_cert(dercert): try: - subject = x509.get_subject(dercert, x509.DER) - issuer = x509.get_issuer(dercert, x509.DER) - serial_number = x509.get_serial_number(dercert, x509.DER) + cert = x509.load_certificate(dercert, x509.DER) + subject = DN(cert.subject) + issuer = DN(cert.issuer) + serial_number = cert.serial public_key_info = x509.get_der_public_key_info(dercert, x509.DER) - except (NSPRError, PyAsn1Error) as e: + except (ValueError, PyAsn1Error) as e: raise ValueError("failed to decode certificate: %s" % e) subject = str(subject).replace('\\;', '\\3b') @@ -54,7 +54,7 @@ def init_ca_entry(entry, dercert, nickname, trusted, ext_key_usage): if ext_key_usage is not None: try: cert_eku = x509.get_ext_key_usage(dercert, x509.DER) - except NSPRError as e: + except ValueError as e: raise ValueError("failed to decode certificate: %s" % e) if cert_eku is not None: cert_eku -= {x509.EKU_SERVER_AUTH, x509.EKU_CLIENT_AUTH, diff --git a/ipalib/x509.py b/ipalib/x509.py index a807d1270..7f7a89c31 100644 --- a/ipalib/x509.py +++ b/ipalib/x509.py @@ -28,31 +28,27 @@ # # cert: the certificate is a PEM-encoded certificate # dercert: the certificate is DER-encoded -# nsscert: the certificate is an NSS Certificate object # rawcert: the cert is in an unknown format from __future__ import print_function import binascii -import collections -import os +import datetime +import ipaddress import sys import base64 import re +from cryptography.hazmat.backends import default_backend import cryptography.x509 -import nss.nss as nss -from nss.error import NSPRError from pyasn1.type import univ, char, namedtype, tag from pyasn1.codec.der import decoder, encoder from pyasn1_modules import rfc2459 import six from ipalib import api -from ipalib import _ from ipalib import util from ipalib import errors -from ipaplatform.paths import paths from ipapython.dn import DN if six.PY3: @@ -95,32 +91,16 @@ def strip_header(pem): return pem -def initialize_nss_database(dbdir=None): - """ - Initializes NSS database, if not initialized yet. Uses a proper database - directory (.ipa/alias or HTTPD_ALIAS_DIR), depending on the value of - api.env.in_tree. - """ - if not nss.nss_is_initialized(): - if dbdir is None: - if 'in_tree' in api.env: - if api.env.in_tree: - dbdir = api.env.dot_ipa + os.sep + 'alias' - else: - dbdir = paths.HTTPD_ALIAS_DIR - nss.nss_init(dbdir) - else: - nss.nss_init_nodb() - else: - nss.nss_init(dbdir) - -def load_certificate(data, datatype=PEM, dbdir=None): +def load_certificate(data, datatype=PEM): """ - Given a base64-encoded certificate, with or without the - header/footer, return a request object. + Load an X.509 certificate. + + :param datatype: PEM for base64-encoded data (with or without header), + or DER + :return: a python-cryptography ``CertificateSigningRequest`` object. + :raises: ``ValueError`` if unable to load the certificate. - Returns a nss.Certificate type """ if type(data) in (tuple, list): data = data[0] @@ -129,82 +109,50 @@ def load_certificate(data, datatype=PEM, dbdir=None): data = strip_header(data) data = base64.b64decode(data) - initialize_nss_database(dbdir=dbdir) + return cryptography.x509.load_der_x509_certificate(data, default_backend()) - if six.PY2: - return nss.Certificate(buffer(data)) # pylint: disable=buffer-builtin - else: - # In python 3 , `bytes` has the buffer interface - return nss.Certificate(data) def load_certificate_from_file(filename, dbdir=None): """ Load a certificate from a PEM file. - Returns a nss.Certificate type - """ - fd = open(filename, 'r') - data = fd.read() - fd.close() - - return load_certificate(data, PEM, dbdir) - -def load_certificate_list(data, dbdir=None): - certs = PEM_REGEX.findall(data) - certs = [load_certificate(cert, PEM, dbdir) for cert in certs] - return certs + Returns a python-cryptography ``Certificate`` object. -def load_certificate_list_from_file(filename, dbdir=None): """ - Load a certificate list from a PEM file. + with open(filename, mode='rb') as f: + return load_certificate(f.read(), PEM) + - Returns a list of nss.Certificate objects. +def load_certificate_list(data): """ - fd = open(filename, 'r') - data = fd.read() - fd.close() + Load a certificate list from a sequence of concatenated PEMs. - return load_certificate_list(data, dbdir) + Return a list of python-cryptography ``Certificate`` objects. -def get_subject(certificate, datatype=PEM, dbdir=None): - """ - Load an X509.3 certificate and get the subject. """ + certs = PEM_REGEX.findall(data) + certs = [load_certificate(cert, PEM) for cert in certs] + return certs - nsscert = load_certificate(certificate, datatype, dbdir) - subject = nsscert.subject - del(nsscert) - return subject -def get_issuer(certificate, datatype=PEM, dbdir=None): - """ - Load an X509.3 certificate and get the issuer. +def load_certificate_list_from_file(filename): """ + Load a certificate list from a PEM file. - nsscert = load_certificate(certificate, datatype, dbdir) - issuer = nsscert.issuer - del(nsscert) - return issuer + Return a list of python-cryptography ``Certificate`` objects. -def get_serial_number(certificate, datatype=PEM, dbdir=None): """ - Return the decimal value of the serial number. - """ - nsscert = load_certificate(certificate, datatype, dbdir) - serial_number = nsscert.serial_number - del(nsscert) - return serial_number + with open(filename) as f: + return load_certificate_list(f.read()) + -def is_self_signed(certificate, datatype=PEM, dbdir=None): - nsscert = load_certificate(certificate, datatype, dbdir) - self_signed = (nsscert.issuer == nsscert.subject) - del nsscert - return self_signed +def is_self_signed(certificate, datatype=PEM): + cert = load_certificate(certificate, datatype) + return cert.issuer == cert.subject def _get_der_field(cert, datatype, dbdir, field): - cert = load_certificate(cert, datatype, dbdir) - cert = cert.der_data + cert = normalize_certificate(cert) cert = decoder.decode(cert, rfc2459.Certificate())[0] field = cert['tbsCertificate'][field] field = encoder.encode(field) @@ -222,20 +170,17 @@ def get_der_serial_number(cert, datatype=PEM, dbdir=None): def get_der_public_key_info(cert, datatype=PEM, dbdir=None): return _get_der_field(cert, datatype, dbdir, 'subjectPublicKeyInfo') -def get_ext_key_usage(certificate, datatype=PEM, dbdir=None): - nsscert = load_certificate(certificate, datatype, dbdir) - if not nsscert.extensions: - return None - for ext in nsscert.extensions: - if ext.oid_tag == nss.SEC_OID_X509_EXT_KEY_USAGE: - break - else: +def get_ext_key_usage(certificate, datatype=PEM): + cert = load_certificate(certificate, datatype) + try: + eku = cert.extensions.get_extension_for_oid( + cryptography.x509.oid.ExtensionOID.EXTENDED_KEY_USAGE).value + except cryptography.x509.ExtensionNotFound: return None - eku = nss.x509_ext_key_usage(ext.value, nss.AsDottedDecimal) - eku = set(o[4:] for o in eku) - return eku + return set(oid.dotted_string for oid in eku) + def make_pem(data): """ @@ -270,27 +215,21 @@ def normalize_certificate(rawcert): else: dercert = rawcert - # At this point we should have a certificate, either because the data - # was base64-encoded and now its not or it came in as DER format. - # Let's decode it and see. Fetching the serial number will pass the - # certificate through the NSS DER parser. + # At this point we should have a DER certificate. + # Attempt to decode it. validate_certificate(dercert, datatype=DER) return dercert -def validate_certificate(cert, datatype=PEM, dbdir=None): +def validate_certificate(cert, datatype=PEM): """ - Perform certificate validation by trying to load it into NSS database + Perform cert validation by trying to load it via python-cryptography. """ try: - load_certificate(cert, datatype=datatype, dbdir=dbdir) - except NSPRError as nsprerr: - if nsprerr.errno == -8183: # SEC_ERROR_BAD_DER - raise errors.CertificateFormatError( - error=_('improperly formatted DER-encoded certificate')) - else: - raise errors.CertificateFormatError(error=str(nsprerr)) + load_certificate(cert, datatype=datatype) + except ValueError as e: + raise errors.CertificateFormatError(error=str(e)) def write_certificate(rawcert, filename): @@ -379,56 +318,6 @@ def _decode_krb5principalname(data): return name -GeneralNameInfo = collections.namedtuple( - 'GeneralNameInfo', ('type', 'desc', 'value', 'der_value')) - - -def decode_generalnames(secitem): - """ - Decode a GeneralNames object (this the data for the Subject - Alt Name and Issuer Alt Name extensions, among others). - - ``secitem`` - The input is the DER-encoded extension data, without the - OCTET STRING header, as an nss SecItem object. - - Return a list of ``GeneralNameInfo`` namedtuples. The - ``der_value`` field is set for otherNames, otherwise it is - ``None``. - - """ - nss_names = nss.x509_alt_name(secitem, repr_kind=nss.AsObject) - asn1_names = decoder.decode( - secitem.data, asn1Spec=rfc2459.SubjectAltName())[0] - names = [] - for nss_name, asn1_name in zip(nss_names, asn1_names): - # NOTE: we use the NSS enum to identify the name type. - # (For otherName we also tuple it up with the type-id OID). - # The enum does not correspond exactly to the ASN.1 tags. - # If we ever want to switch to using the true tag numbers, - # the expression to get the tag is: - # - # asn1_name.getComponent().getTagSet()[0].asTuple()[2] - # - if nss_name.type_enum == nss.certOtherName: - oid = str(asn1_name['otherName']['type-id']) - nametype = (nss_name.type_enum, oid) - der_value = asn1_name['otherName']['value'].asOctets() - else: - nametype = nss_name.type_enum - der_value = None - - if nametype == (nss.certOtherName, SAN_KRB5PRINCIPALNAME): - name = _decode_krb5principalname(asn1_name['otherName']['value']) - else: - name = nss_name.name - - gni = GeneralNameInfo(nametype, nss_name.type_string, name, der_value) - names.append(gni) - - return names - - class KRB5PrincipalName(cryptography.x509.general_name.OtherName): def __init__(self, type_id, value): super(KRB5PrincipalName, self).__init__(type_id, value) @@ -464,6 +353,100 @@ def process_othernames(gns): yield gn +def get_san_general_names(cert): + """ + Return SAN general names from a python-cryptography + certificate object. If the SAN extension is not present, + return an empty sequence. + + Because python-cryptography does not yet provide a way to + handle unrecognised critical extensions (which may occur), + we must parse the certificate and extract the General Names. + For uniformity with other code, we manually construct values + of python-crytography GeneralName subtypes. + + python-cryptography does not yet provide types for + ediPartyName or x400Address, so we drop these name types. + + otherNames are NOT instantiated to more specific types where + the type is known. Use ``process_othernames`` to do that. + + When python-cryptography can handle certs with unrecognised + critical extensions and implements ediPartyName and + x400Address, this function (and helpers) will be redundant + and should go away. + + """ + tbs = decoder.decode( + cert.tbs_certificate_bytes, + asn1Spec=rfc2459.TBSCertificate() + )[0] + OID_SAN = univ.ObjectIdentifier('2.5.29.17') + gns = [] + for ext in tbs['extensions']: + if ext['extnID'] == OID_SAN: + der = decoder.decode( + ext['extnValue'], asn1Spec=univ.OctetString())[0] + gns = decoder.decode(der, asn1Spec=rfc2459.SubjectAltName())[0] + break + + GENERAL_NAME_CONSTRUCTORS = { + 'rfc822Name': lambda x: cryptography.x509.RFC822Name(unicode(x)), + 'dNSName': lambda x: cryptography.x509.DNSName(unicode(x)), + 'directoryName': _pyasn1_to_cryptography_directoryname, + 'registeredID': _pyasn1_to_cryptography_registeredid, + 'iPAddress': _pyasn1_to_cryptography_ipaddress, + 'uniformResourceIdentifier': + lambda x: cryptography.x509.UniformResourceIdentifier(unicode(x)), + 'otherName': _pyasn1_to_cryptography_othername, + } + + result = [] + + for gn in gns: + gn_type = gn.getName() + if gn_type in GENERAL_NAME_CONSTRUCTORS: + result.append( + GENERAL_NAME_CONSTRUCTORS[gn_type](gn.getComponent())) + + return result + + +def _pyasn1_to_cryptography_directoryname(dn): + attrs = [] + + # Name is CHOICE { RDNSequence } (only one possibility) + for rdn in dn.getComponent(): + for ava in rdn: + attr = cryptography.x509.NameAttribute( + _pyasn1_to_cryptography_oid(ava['type']), + unicode(decoder.decode(ava['value'])[0]) + ) + attrs.append(attr) + + return cryptography.x509.DirectoryName(cryptography.x509.Name(attrs)) + + +def _pyasn1_to_cryptography_registeredid(oid): + return cryptography.x509.RegisteredID(_pyasn1_to_cryptography_oid(oid)) + + +def _pyasn1_to_cryptography_ipaddress(octet_string): + return cryptography.x509.IPAddress( + ipaddress.ip_address(bytes(octet_string))) + + +def _pyasn1_to_cryptography_othername(on): + return cryptography.x509.OtherName( + _pyasn1_to_cryptography_oid(on['type-id']), + bytes(on['value']) + ) + + +def _pyasn1_to_cryptography_oid(oid): + return cryptography.x509.ObjectIdentifier(str(oid)) + + def chunk(size, s): """Yield chunks of the specified size from the given string. @@ -486,20 +469,34 @@ def to_hex_with_colons(bs): return add_colons(binascii.hexlify(bs).decode('utf-8')) +class UTC(datetime.tzinfo): + ZERO = datetime.timedelta(0) + + def tzname(self, dt): + return "UTC" + + def utcoffset(self, dt): + return self.ZERO + + def dst(self, dt): + return self.ZERO + + +def format_datetime(t): + if t.tzinfo is None: + t = t.replace(tzinfo=UTC()) + return unicode(t.strftime("%a %b %d %H:%M:%S %Y %Z")) + + if __name__ == '__main__': # this can be run with: # python ipalib/x509.py < /etc/ipa/ca.crt - api.bootstrap() - api.finalize() - - nss.nss_init_nodb() - - # Read PEM certs from stdin and print out its components + # Read PEM cert from stdin and print out its components certlines = sys.stdin.readlines() cert = ''.join(certlines) - nsscert = load_certificate(cert) + cert = load_certificate(cert) - print(nsscert) + print(cert) diff --git a/ipapython/certdb.py b/ipapython/certdb.py index 06666c022..c2fe599a2 100644 --- a/ipapython/certdb.py +++ b/ipapython/certdb.py @@ -22,10 +22,12 @@ import re import tempfile import shutil import base64 +from cryptography.hazmat.primitives import serialization from nss import nss from nss.error import NSPRError from ipaplatform.paths import paths +from ipapython.dn import DN from ipapython.ipa_log_manager import root_logger from ipapython import ipautil from ipalib import x509 @@ -258,7 +260,7 @@ class NSSDatabase(object): 'X.509 CERTIFICATE'): try: x509.load_certificate(match.group(2)) - except NSPRError as e: + except ValueError as e: if label != 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", @@ -334,7 +336,7 @@ class NSSDatabase(object): # Try to load the file as DER certificate try: x509.load_certificate(data, x509.DER) - except NSPRError: + except ValueError: pass else: data = x509.make_pem(base64.b64encode(data)) @@ -379,12 +381,11 @@ class NSSDatabase(object): raise RuntimeError( "No server certificates found in %s" % (', '.join(files))) - nss_certs = x509.load_certificate_list(extracted_certs) - nss_cert = None - for nss_cert in nss_certs: - nickname = str(nss_cert.subject) - self.add_cert(nss_cert.der_data, nickname, ',,') - del nss_certs, nss_cert + certs = x509.load_certificate_list(extracted_certs) + for cert in certs: + nickname = str(DN(cert.subject)) + data = cert.public_bytes(serialization.Encoding.DER) + self.add_cert(data, nickname, ',,') if extracted_key: in_file = ipautil.write_tmp_file(extracted_certs + extracted_key) diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py index 88ec6277f..921e49495 100644 --- a/ipaserver/install/ca.py +++ b/ipaserver/install/ca.py @@ -102,7 +102,7 @@ def install_check(standalone, replica_config, options): cert = db.get_cert_from_db(nickname) if not cert: continue - subject = DN(str(x509.get_subject(cert))) + subject = DN(x509.load_certificate(cert).subject) if subject in (DN('CN=Certificate Authority', subject_base), DN('CN=IPA RA', subject_base)): raise ScriptError( diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 1b7acef70..7b26e749e 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -554,9 +554,9 @@ class CAInstance(DogtagInstance): config.set("CA", "pki_req_ext_data", "1E0A00530075006200430041") elif self.external == 2: - cert = x509.load_certificate_from_file(self.cert_file) cert_file = tempfile.NamedTemporaryFile() - x509.write_certificate(cert.der_data, cert_file.name) + with open(self.cert_file) as f: + x509.write_certificate(f.read(), cert_file.name) cert_file.flush() result = ipautil.run( @@ -778,7 +778,7 @@ class CAInstance(DogtagInstance): userstate=["1"], userCertificate=[cert_data], description=['2;%s;%s;%s' % ( - cert.serial_number, + cert.serial, DN(('CN', 'Certificate Authority'), self.subject_base), DN(('CN', 'IPA RA'), self.subject_base))]) conn.add_entry(entry) @@ -1674,8 +1674,9 @@ def update_people_entry(dercert): is needed when a certificate is renewed. """ def make_filter(dercert): - subject = x509.get_subject(dercert, datatype=x509.DER) - issuer = x509.get_issuer(dercert, datatype=x509.DER) + cert = x509.load_certificate(dercert, datatype=x509.DER) + subject = DN(cert.subject) + issuer = DN(cert.issuer) return ldap2.ldap2.combine_filters( [ ldap2.ldap2.make_filter({'objectClass': 'inetOrgPerson'}), @@ -1686,9 +1687,10 @@ def update_people_entry(dercert): ldap2.ldap2.MATCH_ALL) def make_entry(dercert, entry): - serial_number = x509.get_serial_number(dercert, datatype=x509.DER) - subject = x509.get_subject(dercert, datatype=x509.DER) - issuer = x509.get_issuer(dercert, datatype=x509.DER) + cert = x509.load_certificate(dercert, datatype=x509.DER) + serial_number = cert.serial + subject = DN(cert.subject) + issuer = DN(cert.issuer) entry['usercertificate'].append(dercert) entry['description'] = '2;%d;%s;%s' % (serial_number, issuer, subject) return entry @@ -1702,15 +1704,16 @@ def update_authority_entry(dercert): serial number to match the given cert. """ def make_filter(dercert): - subject = x509.get_subject(dercert, datatype=x509.DER) + cert = x509.load_certificate(dercert, datatype=x509.DER) + subject = str(DN(cert.subject)) return ldap2.ldap2.make_filter( dict(objectclass='authority', authoritydn=subject), rules=ldap2.ldap2.MATCH_ALL, ) def make_entry(dercert, entry): - serial_number = x509.get_serial_number(dercert, datatype=x509.DER) - entry['authoritySerial'] = serial_number + cert = x509.load_certificate(dercert, datatype=x509.DER) + entry['authoritySerial'] = cert.serial return entry return __update_entry_from_cert(make_filter, make_entry, dercert) diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index 31fd36cc3..a73025099 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -60,9 +60,8 @@ def get_cert_nickname(cert): representation of the first RDN in the subject and subject_dn is a DN object. """ - nsscert = x509.load_certificate(cert) - subject = str(nsscert.subject) - dn = DN(subject) + cert_obj = x509.load_certificate(cert) + dn = DN(cert_obj.subject) return (str(dn[0]), dn) @@ -304,8 +303,8 @@ class CertDB(object): return cert = self.get_cert_from_db(nickname) - nsscert = x509.load_certificate(cert, dbdir=self.secdir) - subject = str(nsscert.subject) + cert_obj = x509.load_certificate(cert) + subject = str(DN(cert_obj.subject)) certmonger.add_principal(request_id, principal) certmonger.add_subject(request_id, subject) diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index fb9579a07..bee501a6e 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -921,10 +921,9 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files, if ca_cert is None: ca_cert = cert - nss_cert = x509.load_certificate(cert, x509.DER) - subject = DN(str(nss_cert.subject)) - issuer = DN(str(nss_cert.issuer)) - del nss_cert + cert_obj = x509.load_certificate(cert, x509.DER) + subject = DN(cert_obj.subject) + issuer = DN(cert_obj.issuer) if subject == issuer: break @@ -1046,10 +1045,9 @@ def load_external_cert(files, subject_base): for nickname, _trust_flags in nssdb.list_certs(): cert = nssdb.get_cert(nickname, pem=True) - nss_cert = x509.load_certificate(cert) - subject = DN(str(nss_cert.subject)) - issuer = DN(str(nss_cert.issuer)) - del nss_cert + cert_obj = x509.load_certificate(cert) + subject = DN(cert_obj.subject) + issuer = DN(cert_obj.issuer) cache[nickname] = (cert, subject, issuer) if subject == ca_subject: diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py index af08ba68c..0dcb70fea 100644 --- a/ipaserver/install/ipa_cacert_manage.py +++ b/ipaserver/install/ipa_cacert_manage.py @@ -21,8 +21,7 @@ from __future__ import print_function import os from optparse import OptionGroup -from nss import nss -from nss.error import NSPRError +from cryptography.hazmat.primitives import serialization import gssapi from ipapython import admintool, certmonger, ipautil @@ -187,7 +186,7 @@ class CACertManage(admintool.AdminTool): "--external-cert-file=/path/to/signed_certificate " "--external-cert-file=/path/to/external_ca_certificate") - def renew_external_step_2(self, ca, old_cert): + def renew_external_step_2(self, ca, old_cert_der): print("Importing the renewed CA certificate, please wait") options = self.options @@ -195,55 +194,54 @@ class CACertManage(admintool.AdminTool): cert_file, ca_file = installutils.load_external_cert( options.external_cert_files, x509.subject_base()) - nss_cert = None - nss.nss_init(paths.PKI_TOMCAT_ALIAS_DIR) - try: - nss_cert = x509.load_certificate(old_cert, x509.DER) - subject = nss_cert.subject - der_subject = x509.get_der_subject(old_cert, x509.DER) - #pylint: disable=E1101 - pkinfo = nss_cert.subject_public_key_info.format() - #pylint: enable=E1101 - - nss_cert = x509.load_certificate_from_file(cert_file.name) - cert = nss_cert.der_data - if nss_cert.subject != subject: - raise admintool.ScriptError( - "Subject name mismatch (visit " - "http://www.freeipa.org/page/Troubleshooting for " - "troubleshooting guide)") - if x509.get_der_subject(cert, x509.DER) != der_subject: - raise admintool.ScriptError( - "Subject name encoding mismatch (visit " - "http://www.freeipa.org/page/Troubleshooting for " - "troubleshooting guide)") - #pylint: disable=E1101 - if nss_cert.subject_public_key_info.format() != pkinfo: - raise admintool.ScriptError( - "Subject public key info mismatch (visit " - "http://www.freeipa.org/page/Troubleshooting for " - "troubleshooting guide)") - #pylint: enable=E1101 - finally: - del nss_cert - nss.nss_shutdown() + old_cert_obj = x509.load_certificate(old_cert_der, x509.DER) + old_der_subject = x509.get_der_subject(old_cert_der, x509.DER) + old_spki = old_cert_obj.public_key().public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.SubjectPublicKeyInfo + ) + + with open(cert_file.name) as f: + new_cert_data = f.read() + new_cert_der = x509.normalize_certificate(new_cert_data) + new_cert_obj = x509.load_certificate(new_cert_der, x509.DER) + new_der_subject = x509.get_der_subject(new_cert_der, x509.DER) + new_spki = new_cert_obj.public_key().public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.SubjectPublicKeyInfo + ) + + if new_cert_obj.subject != old_cert_obj.subject: + raise admintool.ScriptError( + "Subject name mismatch (visit " + "http://www.freeipa.org/page/Troubleshooting for " + "troubleshooting guide)") + if new_der_subject != old_der_subject: + raise admintool.ScriptError( + "Subject name encoding mismatch (visit " + "http://www.freeipa.org/page/Troubleshooting for " + "troubleshooting guide)") + if new_spki != old_spki: + raise admintool.ScriptError( + "Subject public key info mismatch (visit " + "http://www.freeipa.org/page/Troubleshooting for " + "troubleshooting guide)") with certs.NSSDatabase() as tmpdb: pw = ipautil.write_tmp_file(ipautil.ipa_generate_password()) tmpdb.create_db(pw.name) - tmpdb.add_cert(old_cert, 'IPA CA', 'C,,') + tmpdb.add_cert(old_cert_der, 'IPA CA', 'C,,') try: - tmpdb.add_cert(cert, 'IPA CA', 'C,,') + tmpdb.add_cert(new_cert_der, 'IPA CA', 'C,,') except ipautil.CalledProcessError as e: raise admintool.ScriptError( "Not compatible with the current CA certificate: %s" % e) ca_certs = x509.load_certificate_list_from_file(ca_file.name) for ca_cert in ca_certs: - tmpdb.add_cert(ca_cert.der_data, str(ca_cert.subject), 'C,,') - del ca_certs - del ca_cert + data = ca_cert.public_bytes(serialization.Encoding.DER) + tmpdb.add_cert(data, str(DN(ca_cert.subject)), 'C,,') try: tmpdb.verify_ca_cert_validity('IPA CA') @@ -266,14 +264,14 @@ class CACertManage(admintool.AdminTool): ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn) try: entry = conn.get_entry(dn, ['usercertificate']) - entry['usercertificate'] = [cert] + entry['usercertificate'] = [new_cert_der] conn.update_entry(entry) except errors.NotFound: entry = conn.make_entry( dn, objectclass=['top', 'pkiuser', 'nscontainer'], cn=[self.cert_nickname], - usercertificate=[cert]) + usercertificate=[new_cert_der]) conn.add_entry(entry) except errors.EmptyModlist: pass @@ -313,21 +311,16 @@ class CACertManage(admintool.AdminTool): options = self.options cert_filename = self.args[1] - nss_cert = None try: - try: - nss_cert = x509.load_certificate_from_file(cert_filename) - except IOError as e: - raise admintool.ScriptError( - "Can't open \"%s\": %s" % (cert_filename, e)) - except (TypeError, NSPRError, ValueError) as e: - raise admintool.ScriptError("Not a valid certificate: %s" % e) - subject = nss_cert.subject - cert = nss_cert.der_data - finally: - del nss_cert + cert_obj = x509.load_certificate_from_file(cert_filename) + except IOError as e: + raise admintool.ScriptError( + "Can't open \"%s\": %s" % (cert_filename, e)) + except (TypeError, ValueError) as e: + raise admintool.ScriptError("Not a valid certificate: %s" % e) + cert = cert_obj.public_bytes(serialization.Encoding.DER) - nickname = options.nickname or str(subject) + nickname = options.nickname or str(DN(cert_obj.subject)) ca_certs = certstore.get_ca_certs_nss(api.Backend.ldap2, api.env.basedn, diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index 315057808..77f23c1c3 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -297,7 +297,7 @@ class KRAInstance(DogtagInstance): usertype=["undefined"], userCertificate=[cert_data], description=['2;%s;%s;%s' % ( - cert.serial_number, + cert.serial, DN(('CN', 'Certificate Authority'), self.subject_base), DN(('CN', 'IPA RA'), self.subject_base))]) conn.add_entry(entry) diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py index a534c4d26..4362d8268 100644 --- a/ipaserver/plugins/cert.py +++ b/ipaserver/plugins/cert.py @@ -22,11 +22,11 @@ import base64 import collections import datetime +from operator import attrgetter import os import cryptography.x509 -from nss import nss -from nss.error import NSPRError +from cryptography.hazmat.primitives import hashes import six from ipalib import Command, Str, Int, Flag @@ -224,7 +224,7 @@ def bind_principal_can_manage_cert(cert): """Check that the bind principal can manage the given cert. ``cert`` - An NSS certificate object. + A python-cryptography ``Certificate`` object. """ bind_principal = kerberos.Principal(getattr(context, 'principal')) @@ -233,9 +233,14 @@ def bind_principal_can_manage_cert(cert): hostname = bind_principal.hostname - # If we have a hostname we want to verify that the subject - # of the certificate matches it. - return hostname == cert.subject.common_name #pylint: disable=E1101 + # Verify that hostname matches subject of cert. + # We check the "most-specific" CN value. + cns = cert.subject.get_attributes_for_oid( + cryptography.x509.oid.NameOID.COMMON_NAME) + if len(cns) == 0: + return False # no CN in subject + else: + return hostname == cns[-1].value class BaseCertObject(Object): @@ -370,30 +375,27 @@ class BaseCertObject(Object): attribute. """ - cert = obj.get('certificate') - if cert is not None: - cert = x509.load_certificate(cert) - obj['subject'] = DN(unicode(cert.subject)) - obj['issuer'] = DN(unicode(cert.issuer)) - obj['serial_number'] = cert.serial_number - obj['valid_not_before'] = unicode(cert.valid_not_before_str) - obj['valid_not_after'] = unicode(cert.valid_not_after_str) + if 'certificate' in obj: + cert = x509.load_certificate(obj['certificate']) + obj['subject'] = DN(cert.subject) + obj['issuer'] = DN(cert.issuer) + obj['serial_number'] = cert.serial + obj['valid_not_before'] = x509.format_datetime( + cert.not_valid_before) + obj['valid_not_after'] = x509.format_datetime( + cert.not_valid_after) if full: obj['md5_fingerprint'] = x509.to_hex_with_colons( - nss.md5_digest(cert.der_data)) + cert.fingerprint(hashes.MD5())) obj['sha1_fingerprint'] = x509.to_hex_with_colons( - nss.sha1_digest(cert.der_data)) + cert.fingerprint(hashes.SHA1())) - try: - ext_san = cert.get_extension(nss.SEC_OID_X509_SUBJECT_ALT_NAME) - general_names = x509.decode_generalnames(ext_san.value) - except KeyError: - general_names = [] + general_names = x509.process_othernames( + x509.get_san_general_names(cert)) - for name_type, _desc, name, der_name in general_names: + for gn in general_names: try: - self._add_san_attribute( - obj, full, name_type, name, der_name) + self._add_san_attribute(obj, full, gn) except Exception: # Invalid GeneralName (i.e. not a valid X.509 cert); # don't fail but log something about it @@ -404,45 +406,52 @@ class BaseCertObject(Object): if serial_number is not None: obj['serial_number_hex'] = u'0x%X' % serial_number - - def _add_san_attribute( - self, obj, full, name_type, name, der_name): + def _add_san_attribute(self, obj, full, gn): name_type_map = { - nss.certRFC822Name: 'san_rfc822name', - nss.certDNSName: 'san_dnsname', - nss.certX400Address: 'san_x400address', - nss.certDirectoryName: 'san_directoryname', - nss.certEDIPartyName: 'san_edipartyname', - nss.certURI: 'san_uri', - nss.certIPAddress: 'san_ipaddress', - nss.certRegisterID: 'san_oid', - (nss.certOtherName, x509.SAN_UPN): 'san_other_upn', - (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME): 'san_other_kpn', + cryptography.x509.RFC822Name: + ('san_rfc822name', attrgetter('value')), + cryptography.x509.DNSName: ('san_dnsname', attrgetter('value')), + # cryptography.x509.???: 'san_x400address', + cryptography.x509.DirectoryName: + ('san_directoryname', lambda x: DN(x.value)), + # cryptography.x509.???: 'san_edipartyname', + cryptography.x509.UniformResourceIdentifier: + ('san_uri', attrgetter('value')), + cryptography.x509.IPAddress: + ('san_ipaddress', attrgetter('value')), + cryptography.x509.RegisteredID: + ('san_oid', attrgetter('value.dotted_string')), + cryptography.x509.OtherName: ('san_other', _format_othername), + x509.UPN: ('san_other_upn', attrgetter('name')), + x509.KRB5PrincipalName: ('san_other_kpn', attrgetter('name')), } default_attrs = { 'san_rfc822name', 'san_dnsname', 'san_other_upn', 'san_other_kpn', } - attr_name = name_type_map.get(name_type, 'san_other') + if type(gn) not in name_type_map: + return + + attr_name, format_name = name_type_map[type(gn)] if full or attr_name in default_attrs: - if attr_name != 'san_other': - name_formatted = name - else: - # display as "OID : b64(DER)" - name_formatted = u'{}:{}'.format( - name_type[1], base64.b64encode(der_name)) - attr_value = self.params[attr_name].type(name_formatted) + attr_value = self.params[attr_name].type(format_name(gn)) obj.setdefault(attr_name, []).append(attr_value) if full and attr_name.startswith('san_other_'): # also include known otherName in generic otherName attribute - name_formatted = u'{}:{}'.format( - name_type[1], base64.b64encode(der_name)) - attr_value = self.params['san_other'].type(name_formatted) + attr_value = self.params['san_other'].type(_format_othername(gn)) obj.setdefault('san_other', []).append(attr_value) +def _format_othername(on): + """Format a python-cryptography OtherName for display.""" + return u'{}:{}'.format( + on.type_id.dotted_string, + base64.b64encode(on.value) + ) + + class BaseCertMethod(Method): def get_options(self): yield self.obj.params['cacn'].clone(query=True) @@ -909,7 +918,7 @@ class cert_show(Retrieve, CertMethod, VirtualCommand): raise acierr # pylint: disable=E0702 ca_obj = api.Command.ca_show(options['cacn'])['result'] - if DN(unicode(cert.issuer)) != DN(ca_obj['ipacasubjectdn'][0]): + if DN(cert.issuer) != DN(ca_obj['ipacasubjectdn'][0]): # DN of cert differs from what we requested raise errors.NotFound( reason=_("Certificate with serial number %(serial)s " @@ -1132,16 +1141,16 @@ class cert_find(Search, CertMethod): def _get_cert_key(self, cert): try: - nss_cert = x509.load_certificate(cert, x509.DER) - except NSPRError as e: + cert_obj = x509.load_certificate(cert, x509.DER) + except ValueError as e: message = messages.SearchResultTruncated( reason=_("failed to load certificate: %s") % e, ) self.add_message(message) - raise ValueError("failed to load certificate") + raise - return (DN(unicode(nss_cert.issuer)), nss_cert.serial_number) + return (DN(cert_obj.issuer), cert_obj.serial) def _get_cert_obj(self, cert, all, raw, pkey_only): obj = {'certificate': unicode(base64.b64encode(cert))} diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py index a39ba3249..ddae37fec 100644 --- a/ipaserver/plugins/service.py +++ b/ipaserver/plugins/service.py @@ -19,6 +19,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +from cryptography.hazmat.primitives import hashes import six from ipalib import api, errors, messages @@ -49,8 +50,6 @@ from ipalib import output from ipapython import kerberos from ipapython.dn import DN -import nss.nss as nss - if six.PY3: unicode = str @@ -268,16 +267,17 @@ def set_certificate_attrs(entry_attrs): cert = entry_attrs['usercertificate'] cert = x509.normalize_certificate(cert) cert = x509.load_certificate(cert, datatype=x509.DER) - entry_attrs['subject'] = unicode(cert.subject) - entry_attrs['serial_number'] = unicode(cert.serial_number) - entry_attrs['serial_number_hex'] = u'0x%X' % cert.serial_number - entry_attrs['issuer'] = unicode(cert.issuer) - entry_attrs['valid_not_before'] = unicode(cert.valid_not_before_str) - entry_attrs['valid_not_after'] = unicode(cert.valid_not_after_str) + entry_attrs['subject'] = unicode(DN(cert.subject)) + entry_attrs['serial_number'] = unicode(cert.serial) + entry_attrs['serial_number_hex'] = u'0x%X' % cert.serial + entry_attrs['issuer'] = unicode(DN(cert.issuer)) + entry_attrs['valid_not_before'] = x509.format_datetime( + cert.not_valid_before) + entry_attrs['valid_not_after'] = x509.format_datetime(cert.not_valid_after) entry_attrs['md5_fingerprint'] = x509.to_hex_with_colons( - nss.md5_digest(cert.der_data)) + cert.fingerprint(hashes.MD5())) entry_attrs['sha1_fingerprint'] = x509.to_hex_with_colons( - nss.sha1_digest(cert.der_data)) + cert.fingerprint(hashes.SHA1())) def check_required_principal(ldap, principal): """ diff --git a/ipatests/test_ipalib/test_x509.py b/ipatests/test_ipalib/test_x509.py index f765bc964..750e086e4 100644 --- a/ipatests/test_ipalib/test_x509.py +++ b/ipatests/test_ipalib/test_x509.py @@ -22,9 +22,9 @@ Test the `ipalib.x509` module. """ import base64 +import datetime import pytest -from nss.error import NSPRError from ipalib import x509 from ipapython.dn import DN @@ -57,17 +57,25 @@ class test_x509(object): # Load a good cert x509.load_certificate(goodcert) + # Should handle list/tuple + x509.load_certificate((goodcert,)) + x509.load_certificate([goodcert]) + # Load a good cert with headers newcert = '-----BEGIN CERTIFICATE-----' + goodcert + '-----END CERTIFICATE-----' x509.load_certificate(newcert) + # Should handle list/tuple + x509.load_certificate((newcert,)) + x509.load_certificate([newcert]) + # Load a good cert with bad headers newcert = '-----BEGIN CERTIFICATE-----' + goodcert with pytest.raises((TypeError, ValueError)): x509.load_certificate(newcert) # Load a bad cert - with pytest.raises(NSPRError): + with pytest.raises(ValueError): x509.load_certificate(badcert) def test_1_load_der_cert(self): @@ -80,53 +88,23 @@ class test_x509(object): # Load a good cert x509.load_certificate(der, x509.DER) - def test_2_get_subject(self): - """ - Test retrieving the subject - """ - subject = x509.get_subject(goodcert) - assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA')) - - der = base64.b64decode(goodcert) - subject = x509.get_subject(der, x509.DER) - assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA')) - - # We should be able to pass in a tuple/list of certs too - subject = x509.get_subject((goodcert)) - assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA')) - - subject = x509.get_subject([goodcert]) - assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA')) - - def test_2_get_serial_number(self): - """ - Test retrieving the serial number - """ - serial = x509.get_serial_number(goodcert) - assert serial == 1093 - - der = base64.b64decode(goodcert) - serial = x509.get_serial_number(der, x509.DER) - assert serial == 1093 - - # We should be able to pass in a tuple/list of certs too - serial = x509.get_serial_number((goodcert)) - assert serial == 1093 - - serial = x509.get_serial_number([goodcert]) - assert serial == 1093 + # Should handle list/tuple + x509.load_certificate((der,), x509.DER) + x509.load_certificate([der], x509.DER) def test_3_cert_contents(self): """ Test the contents of a certificate """ - # Verify certificate contents. This exercises python-nss more than - # anything but confirms our usage of it. + # Verify certificate contents. This exercises python-cryptography + # more than anything but confirms our usage of it. + not_before = datetime.datetime(2010, 6, 25, 13, 0, 42) + not_after = datetime.datetime(2015, 6, 25, 13, 0, 42) cert = x509.load_certificate(goodcert) - assert DN(str(cert.subject)) == DN(('CN','ipa.example.com'),('O','IPA')) - assert DN(str(cert.issuer)) == DN(('CN','IPA Test Certificate Authority')) - assert cert.serial_number == 1093 - assert cert.valid_not_before_str == 'Fri Jun 25 13:00:42 2010 UTC' - assert cert.valid_not_after_str == 'Thu Jun 25 13:00:42 2015 UTC' + assert DN(cert.subject) == DN(('CN', 'ipa.example.com'), ('O', 'IPA')) + assert DN(cert.issuer) == DN(('CN', 'IPA Test Certificate Authority')) + assert cert.serial == 1093 + assert cert.not_valid_before == not_before + assert cert.not_valid_after == not_after diff --git a/ipatests/test_ipaserver/test_ldap.py b/ipatests/test_ipaserver/test_ldap.py index 904c8415c..1ea995999 100644 --- a/ipatests/test_ipaserver/test_ldap.py +++ b/ipatests/test_ipaserver/test_ldap.py @@ -80,7 +80,7 @@ class test_ldap(object): entry_attrs = self.conn.get_entry(self.dn, ['usercertificate']) cert = entry_attrs.get('usercertificate') cert = cert[0] - serial = unicode(x509.get_serial_number(cert, x509.DER)) + serial = x509.load_certificate(cert, x509.DER).serial assert serial is not None def test_simple(self): @@ -99,7 +99,7 @@ class test_ldap(object): entry_attrs = self.conn.get_entry(self.dn, ['usercertificate']) cert = entry_attrs.get('usercertificate') cert = cert[0] - serial = unicode(x509.get_serial_number(cert, x509.DER)) + serial = x509.load_certificate(cert, x509.DER).serial assert serial is not None def test_Backend(self): @@ -127,7 +127,7 @@ class test_ldap(object): entry_attrs = result['result'] cert = entry_attrs.get('usercertificate') cert = cert[0] - serial = unicode(x509.get_serial_number(cert, x509.DER)) + serial = x509.load_certificate(cert, x509.DER).serial assert serial is not None def test_autobind(self): @@ -143,7 +143,7 @@ class test_ldap(object): entry_attrs = self.conn.get_entry(self.dn, ['usercertificate']) cert = entry_attrs.get('usercertificate') cert = cert[0] - serial = unicode(x509.get_serial_number(cert, x509.DER)) + serial = x509.load_certificate(cert, x509.DER).serial assert serial is not None diff --git a/ipatests/test_ipaserver/test_otptoken_import.py b/ipatests/test_ipaserver/test_otptoken_import.py index f1b4331df..b885cefe0 100644 --- a/ipatests/test_ipaserver/test_otptoken_import.py +++ b/ipatests/test_ipaserver/test_otptoken_import.py @@ -20,7 +20,6 @@ import os import pytest from nss import nss -from ipalib.x509 import initialize_nss_database from ipaserver.install.ipa_otptoken_import import PSKCDocument, ValidationError @@ -30,9 +29,6 @@ basename = os.path.join(os.path.dirname(__file__), "data") @pytest.mark.tier1 class test_otptoken_import(object): - def teardown(self): - initialize_nss_database() - def test_figure3(self): doc = PSKCDocument(os.path.join(basename, "pskc-figure3.xml")) assert doc.keyname is None |