mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
x509: use python-cryptography to process certs
Update x509.load_certificate and related functions to return python-cryptography ``Certificate`` objects. Update the call sites accordingly, including removal of NSS initialisation code. Also update GeneralName parsing code to return python-cryptography GeneralName values, for consistency with other code that processes GeneralNames. The new function, `get_san_general_names`, and associated helper functions, can be removed when python-cryptography provides a way to deal with unrecognised critical extensions. Part of: https://fedorahosted.org/freeipa/ticket/6398 Reviewed-By: Jan Cholasta <jcholast@redhat.com> Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
This commit is contained in:
committed by
David Kupka
parent
c57dc890b2
commit
db116f73fe
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
329
ipalib/x509.py
329
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
|
||||
Returns a python-cryptography ``Certificate`` object.
|
||||
|
||||
"""
|
||||
fd = open(filename, 'r')
|
||||
data = fd.read()
|
||||
fd.close()
|
||||
with open(filename, mode='rb') as f:
|
||||
return load_certificate(f.read(), PEM)
|
||||
|
||||
return load_certificate(data, PEM, dbdir)
|
||||
|
||||
def load_certificate_list(data, dbdir=None):
|
||||
def load_certificate_list(data):
|
||||
"""
|
||||
Load a certificate list from a sequence of concatenated PEMs.
|
||||
|
||||
Return a list of python-cryptography ``Certificate`` objects.
|
||||
|
||||
"""
|
||||
certs = PEM_REGEX.findall(data)
|
||||
certs = [load_certificate(cert, PEM, dbdir) for cert in certs]
|
||||
certs = [load_certificate(cert, PEM) for cert in certs]
|
||||
return certs
|
||||
|
||||
def load_certificate_list_from_file(filename, dbdir=None):
|
||||
|
||||
def load_certificate_list_from_file(filename):
|
||||
"""
|
||||
Load a certificate list from a PEM file.
|
||||
|
||||
Returns a list of nss.Certificate objects.
|
||||
"""
|
||||
fd = open(filename, 'r')
|
||||
data = fd.read()
|
||||
fd.close()
|
||||
Return a list of python-cryptography ``Certificate`` objects.
|
||||
|
||||
return load_certificate_list(data, dbdir)
|
||||
|
||||
def get_subject(certificate, datatype=PEM, dbdir=None):
|
||||
"""
|
||||
Load an X509.3 certificate and get the subject.
|
||||
"""
|
||||
with open(filename) as f:
|
||||
return load_certificate_list(f.read())
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
nsscert = load_certificate(certificate, datatype, dbdir)
|
||||
issuer = nsscert.issuer
|
||||
del(nsscert)
|
||||
return issuer
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
for ext in nsscert.extensions:
|
||||
if ext.oid_tag == nss.SEC_OID_X509_EXT_KEY_USAGE:
|
||||
break
|
||||
else:
|
||||
return None
|
||||
return set(oid.dotted_string for oid in eku)
|
||||
|
||||
eku = nss.x509_ext_key_usage(ext.value, nss.AsDottedDecimal)
|
||||
eku = set(o[4:] for o in eku)
|
||||
return 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
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
|
||||
)
|
||||
|
||||
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()
|
||||
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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))}
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user