x509: Make certificates represented as objects

https://pagure.io/freeipa/issue/4985

Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
Stanislav Laznicka
2017-06-16 10:18:07 +02:00
committed by Pavel Vomacka
parent 4375ef860f
commit b5732efda6
33 changed files with 537 additions and 477 deletions

View File

@@ -320,7 +320,7 @@ def install_step_1(standalone, replica_config, options):
subject_base=subject_base)
dsdb = certs.CertDB(
realm_name, nssdir=dirname, subject_base=subject_base)
cacert = cadb.get_cert_from_db('caSigningCert cert-pki-ca', pem=False)
cacert = cadb.get_cert_from_db('caSigningCert cert-pki-ca')
nickname = certdb.get_ca_nickname(realm_name)
trust_flags = certdb.IPA_CA_TRUST_FLAGS
dsdb.add_cert(cacert, nickname, trust_flags)

View File

@@ -791,24 +791,19 @@ class CAInstance(DogtagInstance):
data = base64.b64decode(chain)
# Get list of PEM certificates
certlist = x509.pkcs7_to_pems(data, x509.DER)
certlist = x509.pkcs7_to_certs(data, x509.DER)
# We need to append the certs to the existing file, so start by
# reading the file
if ipautil.file_exists(paths.IPA_CA_CRT):
ca_certs = x509.load_certificate_list_from_file(paths.IPA_CA_CRT)
ca_certs = [cert.public_bytes(serialization.Encoding.PEM)
for cert in ca_certs]
certlist.extend(ca_certs)
# We have all the certificates in certlist, write them to a PEM file
for path in [paths.IPA_CA_CRT,
paths.KDC_CA_BUNDLE_PEM,
paths.CA_BUNDLE_PEM]:
with open(path, 'w') as ipaca_pem:
for cert in certlist:
ipaca_pem.write(cert)
ipaca_pem.write('\n')
x509.write_certificate_list(certlist, path)
def __request_ra_certificate(self):
# create a temp file storing the pwd

View File

@@ -58,9 +58,7 @@ def get_cert_nickname(cert):
representation of the first RDN in the subject and subject_dn
is a DN object.
"""
cert_obj = x509.load_pem_x509_certificate(cert)
dn = DN(cert_obj.subject)
dn = DN(cert.subject)
return (str(dn[0]), dn)
@@ -323,30 +321,20 @@ class CertDB(object):
nick = get_ca_nickname(self.realm)
else:
nick = str(subject_dn)
self.nssdb.add_cert(cert, nick, trust_flags, pem=True)
self.nssdb.add_cert(cert, nick, trust_flags)
except RuntimeError:
break
def get_cert_from_db(self, nickname, pem=True):
def get_cert_from_db(self, nickname):
"""
Retrieve a certificate from the current NSS database for nickname.
pem controls whether the value returned PEM or DER-encoded. The
default is the data straight from certutil -a.
"""
try:
args = ["-L", "-n", nickname, "-a"]
result = self.run_certutil(args, capture_output=True)
cert = result.output
if pem:
return cert
else:
cert, _start = find_cert_from_txt(cert, start=0)
cert = x509.strip_header(cert)
dercert = base64.b64decode(cert)
return dercert
return x509.load_pem_x509_certificate(result.raw_output)
except ipautil.CalledProcessError:
return ''
return None
def track_server_cert(self, nickname, principal, password_file=None, command=None):
"""
@@ -362,8 +350,7 @@ class CertDB(object):
return
cert = self.get_cert_from_db(nickname)
cert_obj = x509.load_pem_x509_certificate(cert)
subject = str(DN(cert_obj.subject))
subject = str(DN(cert.subject))
certmonger.add_principal(request_id, principal)
certmonger.add_subject(request_id, subject)
@@ -392,8 +379,10 @@ class CertDB(object):
try:
self.issue_server_cert(self.certreq_fname, self.certder_fname)
self.import_cert(self.certder_fname, nickname)
with open(self.certder_fname, "r") as f:
dercert = f.read()
return x509.load_der_x509_certificate(dercert)
finally:
for fname in (self.certreq_fname, self.certder_fname):
try:
@@ -401,8 +390,6 @@ class CertDB(object):
except OSError:
pass
return dercert
def request_cert(
self, subject, certtype="rsa", keysize="2048",
san_dnsnames=None):
@@ -519,8 +506,8 @@ class CertDB(object):
with open(cert_fname, "w") as f:
f.write(cert)
def add_cert(self, cert, nick, flags, pem=False):
self.nssdb.add_cert(cert, nick, flags, pem)
def add_cert(self, cert, nick, flags):
self.nssdb.add_cert(cert, nick, flags)
def import_cert(self, cert_fname, nickname):
"""
@@ -594,8 +581,6 @@ class CertDB(object):
newca, _st = find_cert_from_txt(newca)
cacert = self.get_cert_from_db(self.cacert_name)
if cacert != '':
cacert, _st = find_cert_from_txt(cacert)
if newca == cacert:
return
@@ -649,7 +634,7 @@ class CertDB(object):
cert, st = find_cert_from_txt(certs, st)
except RuntimeError:
break
self.add_cert(cert, 'CA %s' % num, EMPTY_TRUST_FLAGS, pem=True)
self.add_cert(cert, 'CA %s' % num, EMPTY_TRUST_FLAGS)
num += 1
# We only handle one server cert

View File

@@ -29,7 +29,7 @@ import dbus
from pki.client import PKIConnection
import pki.system
from ipalib import api, errors
from ipalib import api, errors, x509
from ipalib.install import certmonger
from ipaplatform import services
from ipaplatform.constants import constants
@@ -342,7 +342,7 @@ class DogtagInstance(service.Service):
needs to get the new certificate as well.
``directive`` is the directive to update in CS.cfg
cert is a DER-encoded certificate.
cert is IPACertificate.
cs_cfg is the path to the CS.cfg file
"""
@@ -350,7 +350,8 @@ class DogtagInstance(service.Service):
installutils.set_directive(
cs_cfg,
directive,
base64.b64encode(cert),
# the cert must be only the base64 string without headers
base64.b64encode(cert.public_bytes(x509.Encoding.DER)),
quotes=False,
separator='=')

View File

@@ -234,7 +234,7 @@ class DsInstance(service.Service):
self.pkcs12_info = None
self.cacert_name = None
self.ca_is_configured = True
self.dercert = None
self.cert = None
self.idstart = None
self.idmax = None
self.ca_subject = None
@@ -791,7 +791,7 @@ class DsInstance(service.Service):
# We only handle one server cert
self.nickname = server_certs[0][0]
self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)
self.cert = dsdb.get_cert_from_db(self.nickname)
if self.ca_is_configured:
dsdb.track_server_cert(
@@ -834,7 +834,7 @@ class DsInstance(service.Service):
api.Backend.ldap2.disconnect()
api.Backend.ldap2.connect()
self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)
self.cert = dsdb.get_cert_from_db(self.nickname)
if prev_helper is not None:
self.add_cert_to_service()
@@ -888,12 +888,12 @@ class DsInstance(service.Service):
nicknames = dsdb.find_root_cert(self.cacert_name)[:-1]
for nickname in nicknames:
cert = dsdb.get_cert_from_db(nickname, pem=False)
cert = dsdb.get_cert_from_db(nickname)
certstore.put_ca_cert_nss(conn, self.suffix, cert, nickname,
trust_flags[nickname])
nickname = self.cacert_name
cert = dsdb.get_cert_from_db(nickname, pem=False)
cert = dsdb.get_cert_from_db(nickname)
cacert_flags = trust_flags[nickname]
if self.setup_pkinit:
cacert_flags = TrustFlags(

View File

@@ -138,7 +138,7 @@ class HTTPInstance(service.Service):
self.dm_password = dm_password
self.suffix = ipautil.realm_to_suffix(self.realm)
self.pkcs12_info = pkcs12_info
self.dercert = None
self.cert = None
self.subject_base = subject_base
self.sub_dict = dict(
REALM=realm,
@@ -406,7 +406,7 @@ class HTTPInstance(service.Service):
nickname = server_certs[0][0]
if nickname == 'ipaCert':
nickname = server_certs[1][0]
self.dercert = db.get_cert_from_db(nickname, pem=False)
self.cert = db.get_cert_from_db(nickname)
if self.ca_is_configured:
db.track_server_cert(nickname, self.principal, db.passwd_fname, 'restart_httpd')
@@ -443,7 +443,7 @@ class HTTPInstance(service.Service):
if prev_helper is not None:
certmonger.modify_ca_helper('IPA', prev_helper)
self.dercert = db.get_cert_from_db(self.cert_nickname, pem=False)
self.cert = db.get_cert_from_db(self.cert_nickname)
if prev_helper is not None:
self.add_cert_to_service()

View File

@@ -1054,9 +1054,8 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files,
if ca_cert is None:
ca_cert = cert
cert_obj = x509.load_der_x509_certificate(cert)
subject = DN(cert_obj.subject)
issuer = DN(cert_obj.issuer)
subject = DN(cert.subject)
issuer = DN(cert.issuer)
if subject == issuer:
break
@@ -1183,11 +1182,9 @@ def load_external_cert(files, ca_subject):
ca_nickname = None
cache = {}
for nickname, _trust_flags in nssdb.list_certs():
cert = nssdb.get_cert(nickname, pem=True)
cert_obj = x509.load_pem_x509_certificate(cert)
subject = DN(cert_obj.subject)
issuer = DN(cert_obj.issuer)
cert = nssdb.get_cert(nickname)
subject = DN(cert.subject)
issuer = DN(cert.issuer)
cache[nickname] = (cert, subject, issuer)
if subject == ca_subject:
@@ -1220,11 +1217,11 @@ def load_external_cert(files, ca_subject):
(subject, ", ".join(files), e))
cert_file = tempfile.NamedTemporaryFile()
cert_file.write(ca_cert_chain[0] + '\n')
cert_file.write(ca_cert_chain[0].public_bytes(x509.Encoding.PEM) + b'\n')
cert_file.flush()
ca_file = tempfile.NamedTemporaryFile()
ca_file.write('\n'.join(ca_cert_chain[1:]) + '\n')
x509.write_certificate_list(ca_cert_chain[1:], ca_file.name)
ca_file.flush()
return cert_file, ca_file

View File

@@ -22,7 +22,6 @@ from __future__ import print_function
import logging
import os
from optparse import OptionGroup # pylint: disable=deprecated-module
from cryptography.hazmat.primitives import serialization
import gssapi
from ipalib.install import certmonger, certstore
@@ -161,7 +160,7 @@ class CACertManage(admintool.AdminTool):
"Found certmonger request id %r", self.request_id)
db = certs.CertDB(api.env.realm, nssdir=paths.PKI_TOMCAT_ALIAS_DIR)
cert = db.get_cert_from_db(self.cert_nickname, pem=False)
cert = db.get_cert_from_db(self.cert_nickname)
options = self.options
if options.external_cert_files:
@@ -170,7 +169,7 @@ class CACertManage(admintool.AdminTool):
if options.self_signed is not None:
self_signed = options.self_signed
else:
self_signed = x509.is_self_signed(cert, x509.DER)
self_signed = cert.is_self_signed()
if self_signed:
return self.renew_self_signed(ca)
@@ -205,38 +204,28 @@ 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_der):
def renew_external_step_2(self, ca, old_cert):
print("Importing the renewed CA certificate, please wait")
options = self.options
conn = api.Backend.ldap2
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
)
old_spki = old_cert.public_key_info_bytes
cert_file, ca_file = installutils.load_external_cert(
options.external_cert_files, DN(old_cert_obj.subject))
options.external_cert_files, DN(old_cert.subject))
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
)
new_cert = x509.load_pem_x509_certificate(new_cert_data)
new_spki = new_cert.public_key_info_bytes
if new_cert_obj.subject != old_cert_obj.subject:
if new_cert.subject != old_cert.subject:
raise admintool.ScriptError(
"Subject name mismatch (visit "
"http://www.freeipa.org/page/Troubleshooting for "
"troubleshooting guide)")
if new_der_subject != old_der_subject:
if new_cert.subject_bytes != old_cert.subject_bytes:
raise admintool.ScriptError(
"Subject name encoding mismatch (visit "
"http://www.freeipa.org/page/Troubleshooting for "
@@ -249,19 +238,18 @@ class CACertManage(admintool.AdminTool):
with certs.NSSDatabase() as tmpdb:
tmpdb.create_db()
tmpdb.add_cert(old_cert_der, 'IPA CA', EXTERNAL_CA_TRUST_FLAGS)
tmpdb.add_cert(old_cert, 'IPA CA', EXTERNAL_CA_TRUST_FLAGS)
try:
tmpdb.add_cert(new_cert_der, 'IPA CA', EXTERNAL_CA_TRUST_FLAGS)
tmpdb.add_cert(new_cert, 'IPA CA', EXTERNAL_CA_TRUST_FLAGS)
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:
data = ca_cert.public_bytes(serialization.Encoding.DER)
tmpdb.add_cert(
data, str(DN(ca_cert.subject)), EXTERNAL_CA_TRUST_FLAGS)
ca_cert, str(DN(ca_cert.subject)), EXTERNAL_CA_TRUST_FLAGS)
try:
tmpdb.verify_ca_cert_validity('IPA CA')
@@ -286,6 +274,8 @@ class CACertManage(admintool.AdminTool):
dn = DN(('cn', self.cert_nickname), ('cn', 'ca_renewal'),
('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
new_cert_der = new_cert.public_bytes(x509.Encoding.DER)
try:
entry = conn.get_entry(dn, ['usercertificate'])
entry['usercertificate'] = [new_cert_der]
@@ -338,15 +328,14 @@ class CACertManage(admintool.AdminTool):
cert_filename = self.args[1]
try:
cert_obj = x509.load_certificate_from_file(cert_filename)
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, 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(DN(cert_obj.subject))
nickname = options.nickname or str(DN(cert.subject))
ca_certs = certstore.get_ca_certs_nss(api.Backend.ldap2,
api.env.basedn,

View File

@@ -212,8 +212,7 @@ class ServerCertInstall(admintool.AdminTool):
# Start tracking only if the cert was issued by IPA CA
# Retrieve IPA CA
ipa_ca_cert = cdb.get_cert_from_db(
get_ca_nickname(api.env.realm),
pem=False)
get_ca_nickname(api.env.realm))
# And compare with the CA which signed this certificate
if ca_cert == ipa_ca_cert:
certmonger.start_tracking(
@@ -289,8 +288,7 @@ class ServerCertInstall(admintool.AdminTool):
# Start tracking only if the cert was issued by IPA CA
# Retrieve IPA CA
ipa_ca_cert = cdb.get_cert_from_db(
get_ca_nickname(api.env.realm),
pem=False)
get_ca_nickname(api.env.realm))
# And compare with the CA which signed this certificate
if ca_cert == ipa_ca_cert:
cdb.track_server_cert(server_cert,

View File

@@ -27,7 +27,6 @@ import six
# pylint: disable=import-error
from six.moves.configparser import RawConfigParser
# pylint: enable=import-error
from cryptography.hazmat.primitives import serialization
from ipalib import api
from ipalib import x509
@@ -304,7 +303,7 @@ class KRAInstance(DogtagInstance):
# get RA agent certificate
cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
cert_data = cert.public_bytes(serialization.Encoding.DER)
cert_data = cert.public_bytes(x509.Encoding.DER)
# connect to KRA database
conn = ldap2.ldap2(api)

View File

@@ -22,7 +22,7 @@ import logging
from ipalib.install import certstore
from ipaplatform.paths import paths
from ipaserver.install import certs
from ipalib import Registry, errors
from ipalib import Registry, errors, x509
from ipalib import Updater
from ipapython import certdb
from ipapython.dn import DN
@@ -60,7 +60,7 @@ class update_upload_cacrt(Updater):
continue
if nickname == ca_nickname and ca_enabled:
trust_flags = certdb.IPA_CA_TRUST_FLAGS
cert = db.get_cert_from_db(nickname, pem=False)
cert = db.get_cert_from_db(nickname)
trust, _ca, eku = certstore.trust_flags_to_key_policy(trust_flags)
dn = DN(('cn', nickname), ('cn', 'certificates'), ('cn', 'ipa'),
@@ -90,6 +90,7 @@ class update_upload_cacrt(Updater):
pass
if ca_cert:
dercert = ca_cert.public_bytes(x509.Encoding.DER)
dn = DN(('cn', 'CACert'), ('cn', 'ipa'), ('cn','etc'),
self.api.env.basedn)
try:
@@ -98,11 +99,11 @@ class update_upload_cacrt(Updater):
entry = ldap.make_entry(dn)
entry['objectclass'] = ['nsContainer', 'pkiCA']
entry.single_value['cn'] = 'CAcert'
entry.single_value['cACertificate;binary'] = ca_cert
entry.single_value['cACertificate;binary'] = dercert
ldap.add_entry(entry)
else:
if b'' in entry['cACertificate;binary']:
entry.single_value['cACertificate;binary'] = ca_cert
entry.single_value['cACertificate;binary'] = dercert
ldap.update_entry(entry)
return False, []

View File

@@ -1602,7 +1602,7 @@ def disable_httpd_system_trust(http):
db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR)
for nickname, trust_flags in db.list_certs():
if not trust_flags.has_key:
cert = db.get_cert_from_db(nickname, pem=False)
cert = db.get_cert_from_db(nickname)
if cert:
ca_certs.append((cert, nickname, trust_flags))

View File

@@ -32,7 +32,7 @@ from ipalib.install import certstore, sysrestore
from ipapython import ipautil
from ipapython.dn import DN
from ipapython import kerberos
from ipalib import api, errors
from ipalib import api, errors, x509
from ipaplatform import services
from ipaplatform.paths import paths
@@ -245,7 +245,7 @@ class Service(object):
self.suffix = DN()
self.service_prefix = service_prefix
self.keytab = keytab
self.dercert = None
self.cert = None
self.api = api
self.service_user = service_user
self.keytab_user = service_user
@@ -370,7 +370,8 @@ class Service(object):
dn = DN(('krbprincipalname', self.principal), ('cn', 'services'),
('cn', 'accounts'), self.suffix)
entry = api.Backend.ldap2.get_entry(dn)
entry.setdefault('userCertificate', []).append(self.dercert)
entry.setdefault('userCertificate', []).append(
self.cert.public_bytes(x509.Encoding.DER))
try:
api.Backend.ldap2.update_entry(entry)
except Exception as e: