URL: https://github.com/freeipa/freeipa/pull/899
Author: Rezney
 Title: #899: test_caless: introduce new python makepki + fix SKI extension 
issue
Action: opened

PR body:
"""
Change makepki.sh for new makepki.py which should be more
readable, maintainable and extendable than the old script.
In this test we use it as a module and import create_pki().

The new makepki adds SKI and AKI extensions for correct
cert validation.

Other minor changes needed as we do not use NSS to store our
certificates on the test controller.

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

Signed-off-by: Michal Reznik <mrez...@redhat.com>
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/899/head:pr899
git checkout pr899
From 5189ddc6660a28e490e7f61c75beb15e82d64218 Mon Sep 17 00:00:00 2001
From: Michal Reznik <mrez...@redhat.com>
Date: Wed, 28 Jun 2017 10:28:48 +0200
Subject: [PATCH] test_caless: introduce new python makepki + fix SKI extension
 issue

Change makepki.sh for new makepki.py which should be more
readable, maintainable and extendable than the old script.
In this test we use it as a module and import create_pki().

The new makepki adds SKI and AKI extensions for correct
cert validation.

Other minor changes needed as we do not use NSS to store our
certificates on the test controller.

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

Signed-off-by: Michal Reznik <mrez...@redhat.com>
---
 ipatests/test_integration/create_caless_pki.py     | 420 +++++++++++++++++++++
 .../test_integration/scripts/caless-create-pki     | 188 ---------
 ipatests/test_integration/test_caless.py           | 388 ++++++++++---------
 3 files changed, 609 insertions(+), 387 deletions(-)
 create mode 100644 ipatests/test_integration/create_caless_pki.py
 delete mode 100644 ipatests/test_integration/scripts/caless-create-pki

diff --git a/ipatests/test_integration/create_caless_pki.py b/ipatests/test_integration/create_caless_pki.py
new file mode 100644
index 0000000000..3b6a62cc31
--- /dev/null
+++ b/ipatests/test_integration/create_caless_pki.py
@@ -0,0 +1,420 @@
+# Copyright (c) 2015-2017, Jan Cholasta <jchol...@redhat.com>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, cert_dirECT, INcert_dirECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+import collections
+import datetime
+import itertools
+import os
+import os.path
+import sys
+
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.x509.oid import NameOID
+from pyasn1.type import univ, char, namedtype, tag
+from pyasn1.codec.der import encoder as der_encoder
+from pyasn1.codec.native import decoder as native_decoder
+
+
+DAY = datetime.timedelta(days=1)
+YEAR = 365 * DAY
+
+# we set the variables in create_pki()
+domain = None
+realm = None
+server1 = None
+server2 =  None
+client = None
+password = None
+cert_dir = None
+
+CertInfo = collections.namedtuple('CertInfo', 'nick key cert counter')
+
+
+# RFC 4120
+class PrincipalName(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType(
+            'name-type',
+            univ.Integer().subtype(
+                explicitTag=tag.Tag(
+                    tag.tagClassContext,
+                    tag.tagFormatSimple,
+                    0,
+                ),
+            ),
+        ),
+        namedtype.NamedType(
+            'name-string',
+            univ.SequenceOf(char.GeneralString()).subtype(
+                explicitTag=tag.Tag(
+                    tag.tagClassContext,
+                    tag.tagFormatSimple,
+                    1,
+                ),
+            ),
+        ),
+    )
+
+
+# RFC 4556
+class KRB5PrincipalName(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType(
+            'realm',
+            char.GeneralString().subtype(
+                explicitTag=tag.Tag(
+                    tag.tagClassContext,
+                    tag.tagFormatSimple,
+                    0,
+                ),
+            ),
+        ),
+        namedtype.NamedType(
+            'principalName',
+            PrincipalName().subtype(
+                explicitTag=tag.Tag(
+                    tag.tagClassContext,
+                    tag.tagFormatSimple,
+                    1,
+                ),
+            ),
+        ),
+    )
+
+
+def profile_ca(builder, ca_nick, ca):
+    now = datetime.datetime.utcnow()
+
+    builder = builder.not_valid_before(now)
+    builder = builder.not_valid_after(now + 10 * YEAR)
+
+    crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick))
+
+    builder = builder.add_extension(
+        x509.KeyUsage(
+            digital_signature=True,
+            content_commitment=True,
+            key_encipherment=False,
+            data_encipherment=False,
+            key_agreement=False,
+            key_cert_sign=True,
+            crl_sign=True,
+            encipher_only=False,
+            decipher_only=False,
+        ),
+        critical=True,
+    )
+    builder = builder.add_extension(
+        x509.BasicConstraints(ca=True, path_length=None),
+        critical=True,
+    )
+    builder = builder.add_extension(
+        x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=[x509.UniformResourceIdentifier(crl_uri)],
+                    relative_name=None,
+                    crl_issuer=None,
+                    reasons=None,
+                ),
+        ]),
+        critical=False,
+    )
+
+    public_key = builder._public_key
+
+    builder = builder.add_extension(
+        x509.SubjectKeyIdentifier.from_public_key(public_key),
+        critical=False,
+    )
+    # here we get "ca" only for "ca1/subca" CA
+    if not ca:
+        builder = builder.add_extension(
+            x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key),
+            critical=False,
+        )
+    else:
+        ski = ca.cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
+        builder = builder.add_extension(
+            x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ski),
+            critical=False,
+        )
+    return builder
+
+
+def profile_server(builder, ca_nick, ca,
+                   warp=datetime.timedelta(days=0), dns_name=None,
+                   badusage=False):
+    now = datetime.datetime.utcnow() + warp
+
+    builder = builder.not_valid_before(now)
+    builder = builder.not_valid_after(now + YEAR)
+
+    crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick))
+
+    builder = builder.add_extension(
+        x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=[x509.UniformResourceIdentifier(crl_uri)],
+                    relative_name=None,
+                    crl_issuer=None,
+                    reasons=None,
+                ),
+        ]),
+        critical=False,
+    )
+
+    if dns_name is not None:
+        builder = builder.add_extension(
+            x509.SubjectAlternativeName([x509.DNSName(dns_name)]),
+            critical=False,
+        )
+
+    if badusage:
+        builder = builder.add_extension(
+            x509.KeyUsage(
+                digital_signature=False,
+                content_commitment=False,
+                key_encipherment=False,
+                data_encipherment=True,
+                key_agreement=True,
+                key_cert_sign=False,
+                crl_sign=False,
+                encipher_only=False,
+                decipher_only=False
+            ),
+            critical=False
+        )
+
+    return builder
+
+
+def profile_kdc(builder, ca_nick, ca,
+                warp=datetime.timedelta(days=0), dns_name=None,
+                badusage=False):
+    now = datetime.datetime.utcnow() + warp
+
+    builder = builder.not_valid_before(now)
+    builder = builder.not_valid_after(now + YEAR)
+
+    crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick))
+
+    builder = builder.add_extension(
+        x509.ExtendedKeyUsage([x509.ObjectIdentifier('1.3.6.1.5.2.3.5')]),
+        critical=False,
+    )
+
+    name = {
+        'realm': realm,
+        'principalName': {
+            'name-type': 2,
+            'name-string': ['krbtgt', realm],
+        },
+    }
+    name = native_decoder.decode(name, asn1Spec=KRB5PrincipalName())
+    name = der_encoder.encode(name)
+
+    names = [x509.OtherName(x509.ObjectIdentifier('1.3.6.1.5.2.2'), name)]
+    if dns_name is not None:
+        names += [x509.DNSName(dns_name)]
+
+    builder = builder.add_extension(
+        x509.SubjectAlternativeName(names),
+        critical=False,
+    )
+
+    builder = builder.add_extension(
+        x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=[x509.UniformResourceIdentifier(crl_uri)],
+                    relative_name=None,
+                    crl_issuer=None,
+                    reasons=None,
+                ),
+        ]),
+        critical=False,
+    )
+
+    if badusage:
+        builder = builder.add_extension(
+            x509.KeyUsage(
+                digital_signature=False,
+                content_commitment=False,
+                key_encipherment=False,
+                data_encipherment=True,
+                key_agreement=True,
+                key_cert_sign=False,
+                crl_sign=False,
+                encipher_only=False,
+                decipher_only=False
+            ),
+            critical=False
+        )
+
+    return builder
+
+
+def gen_cert(profile, nick_base, subject, ca=None, **kwargs):
+    key = rsa.generate_private_key(
+        public_exponent=65537,
+        key_size=2048,
+        backend=default_backend(),
+    )
+    public_key = key.public_key()
+
+    counter = itertools.count(1)
+
+    if ca is not None:
+        ca_nick, ca_key, ca_cert, ca_counter = ca
+        nick = os.path.join(ca_nick, nick_base)
+        issuer = ca_cert.subject
+    else:
+        nick = ca_nick = nick_base
+        ca_key = key
+        ca_counter = counter
+        issuer = subject
+
+    serial = next(ca_counter)
+
+    builder = x509.CertificateBuilder()
+    builder = builder.serial_number(serial)
+    builder = builder.issuer_name(issuer)
+    builder = builder.subject_name(subject)
+    builder = builder.public_key(public_key)
+    builder = profile(builder, ca_nick, ca, **kwargs)
+
+    cert = builder.sign(
+        private_key=ca_key,
+        algorithm=hashes.SHA256(),
+        backend=default_backend(),
+    )
+
+    key_pem = key.private_bytes(
+        serialization.Encoding.PEM,
+        serialization.PrivateFormat.PKCS8,
+        serialization.BestAvailableEncryption(password.encode()),
+    )
+    cert_pem = cert.public_bytes(serialization.Encoding.PEM)
+    try:
+        os.makedirs(os.path.dirname(os.path.join(cert_dir, nick)))
+    except OSError:
+        pass
+    with open(os.path.join(cert_dir, nick + '.key'), 'wb') as f:
+        f.write(key_pem)
+    with open(os.path.join(cert_dir, nick + '.crt'), 'wb') as f:
+        f.write(cert_pem)
+
+    return CertInfo(nick, key, cert, counter)
+
+
+def revoke_cert(ca, serial):
+    now = datetime.datetime.utcnow()
+
+    crl_builder = x509.CertificateRevocationListBuilder()
+    crl_builder = crl_builder.issuer_name(ca.cert.subject)
+    crl_builder = crl_builder.last_update(now)
+    crl_builder = crl_builder.next_update(now + DAY)
+
+    crl_filename = os.path.join(cert_dir, ca.nick + '.crl')
+
+    try:
+        f = open(crl_filename, 'rb')
+    except IOError:
+        pass
+    else:
+        with f:
+            crl_pem = f.read()
+
+        crl = x509.load_pem_x509_crl(crl_pem, default_backend())
+
+        for revoked_cert in crl:
+            crl_builder = crl_builder.add_revoked_certificate(revoked_cert)
+
+    builder = x509.RevokedCertificateBuilder()
+    builder = builder.serial_number(serial)
+    builder = builder.revocation_date(now)
+
+    revoked_cert = builder.build(default_backend())
+
+    crl_builder = crl_builder.add_revoked_certificate(revoked_cert)
+
+    crl = crl_builder.sign(
+        private_key=ca.key,
+        algorithm=hashes.SHA256(),
+        backend=default_backend(),
+    )
+
+    crl_pem = crl.public_bytes(serialization.Encoding.PEM)
+
+    with open(crl_filename, 'wb') as f:
+        f.write(crl_pem)
+
+
+def gen_server_certs(nick_base, hostname, org, ca=None):
+    gen_cert(profile_server, nick_base, x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca)
+    gen_cert(profile_server, nick_base + u'-badname', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.COMMON_NAME, u'not-' + hostname)]), ca)
+    gen_cert(profile_server, nick_base + u'-altname', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.COMMON_NAME, u'alt-' + hostname)]), ca, dns_name=hostname)
+    gen_cert(profile_server, nick_base + u'-expired', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'Expired'), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca, warp=-2 * YEAR)
+    gen_cert(profile_server, nick_base + u'-badusage', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'Bad Usage'), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca, badusage=True)
+    revoked = gen_cert(profile_server, nick_base + u'-revoked', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'Revoked'), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca)
+    revoke_cert(ca, revoked.cert.serial_number)
+
+
+def gen_kdc_certs(nick_base, hostname, org, ca=None):
+    gen_cert(profile_kdc, nick_base + u'-kdc', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca)
+    gen_cert(profile_kdc, nick_base + u'-kdc-badname', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'), x509.NameAttribute(NameOID.COMMON_NAME, u'not-' + hostname)]), ca)
+    gen_cert(profile_kdc, nick_base + u'-kdc-altname', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'), x509.NameAttribute(NameOID.COMMON_NAME, u'alt-' + hostname)]), ca, dns_name=hostname)
+    gen_cert(profile_kdc, nick_base + u'-kdc-expired', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'Expired KDC'), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca, warp=-2 * YEAR)
+    gen_cert(profile_kdc, nick_base + u'-kdc-badusage', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'Bad Usage KDC'), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca, badusage=True)
+    revoked = gen_cert(profile_kdc, nick_base + u'-kdc-revoked', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'Revoked KDC'), x509.NameAttribute(NameOID.COMMON_NAME, hostname)]), ca)
+    revoke_cert(ca, revoked.cert.serial_number)
+
+
+def gen_subtree(nick_base, org, ca=None):
+    subca = gen_cert(profile_ca, nick_base, x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.COMMON_NAME, u'CA')]), ca)
+    gen_cert(profile_server, u'wildcard', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.COMMON_NAME, u'*.' + domain)]), subca)
+    gen_server_certs(u'server', server1, org, subca)
+    gen_server_certs(u'replica', server2, org, subca)
+    gen_server_certs(u'client', client, org, subca)
+    gen_cert(profile_kdc, u'kdcwildcard', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, org), x509.NameAttribute(NameOID.COMMON_NAME, u'*.' + domain)]), subca)
+    gen_kdc_certs(u'server', server1, org, subca)
+    gen_kdc_certs(u'replica', server2, org, subca)
+    gen_kdc_certs(u'client', client, org, subca)
+    return subca
+
+
+def create_pki(pki_vars):
+
+    # as original "makepki.py" from freeipa-tools uses mostly constant global
+    # variables in the code here we want to set what we get from ca_less test
+    # suite as global variables too
+
+    module = sys.modules[__name__]
+    for name, value in pki_vars.items():
+        setattr(module, name, unicode(value))
+
+    gen_cert(profile_server, u'server-selfsign', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'), x509.NameAttribute(NameOID.COMMON_NAME, server1)]))
+    gen_cert(profile_server, u'replica-selfsign', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'), x509.NameAttribute(NameOID.COMMON_NAME, server2)]))
+    gen_cert(profile_server, u'noca', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'No-CA'), x509.NameAttribute(NameOID.COMMON_NAME, server1)]))
+    gen_cert(profile_kdc, u'server-kdc-selfsign', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'), x509.NameAttribute(NameOID.COMMON_NAME, server1)]))
+    gen_cert(profile_kdc, u'replica-kdc-selfsign', x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Self-signed'), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'KDC'), x509.NameAttribute(NameOID.COMMON_NAME, server2)]))
+    ca1 = gen_subtree(u'ca1', u'Example Organization')
+    gen_subtree(u'subca', u'Subsidiary Example Organization', ca1)
+    gen_subtree(u'ca2', u'Other Example Organization')
+    ca3 = gen_subtree(u'ca3', u'Unknown Organization')
+    os.unlink(os.path.join(cert_dir, ca3.nick + '.key'))
+    os.unlink(os.path.join(cert_dir, ca3.nick + '.crt'))
diff --git a/ipatests/test_integration/scripts/caless-create-pki b/ipatests/test_integration/scripts/caless-create-pki
deleted file mode 100644
index dbcdb3e602..0000000000
--- a/ipatests/test_integration/scripts/caless-create-pki
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/bin/bash -e
-#
-# Copyright (C) 2017  FreeIPA Contributors see COPYING for license
-#
-
-profile_ca_request_options=(-1 -2 -4)
-profile_ca_request_input="\$'0\n1\n5\n6\n9\ny\ny\n\ny\n1\n7\nfile://'\$(readlink -f \$dbdir)/\$ca.crl\$'\n-1\n-1\n-1\nn\nn\n'"
-profile_ca_create_options=(-v 120)
-profile_ca_add_options=(-t CT,C,C)
-profile_server_request_options=(-4)
-profile_server_request_input="\$'1\n7\nfile://'\$(readlink -f \$dbdir)/\$ca.crl\$'\n-1\n-1\n-1\nn\nn\n'"
-profile_server_create_options=(-v 12)
-profile_server_add_options=(-t ,,)
-
-write_chain() {
-    local nick="$1"
-
-    chain=`certutil -O -d $dbdir -n "$nick" |
-             sed -e '/^\s*$/d' -e "s/\s*\"\(.*\)\" \[.*/\1/g"`
-
-    while read -r name; do
-        # OpenSSL requires a reverse order to what we get from NSS
-        echo -e "`certutil -L -d "$dbdir" -n "$name" -a`\n`cat $dbdir/$nick.pem`
-        " > "$dbdir/$nick.pem"
-    done <<< "$chain"
-}
-
-gen_cert() {
-    local profile="$1" nick="$2" subject="$3" ca request_options request_input create_options serial add_options pwfile noise csr crt
-    shift 3
-
-    echo "gen_cert(profile=$profile nick=$nick subject=$subject)"
-
-    ca="$(dirname $nick)"
-    if [ "$ca" = "." ]; then
-        ca="$nick"
-    fi
-
-    eval "request_options=(\"\${profile_${profile}_request_options[@]}\")"
-    eval "eval request_input=\"\${profile_${profile}_request_input}\""
-
-    eval "create_options=(\"\${profile_${profile}_create_options[@]}\")"
-    if [ "$ca" = "$nick" ]; then
-        create_options=("${create_options[@]}" -x -m 1)
-    else
-        eval "serial_${ca//\//_}=\$((\${serial_${ca//\//_}:-1}+1))"
-        eval "serial=\$serial_${ca//\//_}"
-        create_options=("${create_options[@]}" -c "$ca" -m "$serial")
-    fi
-
-    eval "add_options=(\"\${profile_${profile}_add_options[@]}\")"
-
-    pwfile="$(mktemp)"
-    echo "$dbpassword" >"$pwfile"
-
-    noise="$(mktemp)"
-    head -c 20 /dev/urandom >"$noise"
-
-    if [ ! -d "$dbdir" ]; then
-        mkdir "$dbdir"
-        certutil -N -d "$dbdir" -f "$pwfile"
-    fi
-
-    csr="$(mktemp)"
-    crt="$(mktemp)"
-
-    certutil -R -d "$dbdir" -s "$subject" -f "$pwfile" -z "$noise" -o "$csr" "${request_options[@]}" >/dev/null <<<"$request_input"
-    certutil -C -d "$dbdir" -f "$pwfile" -i "$csr" -o "$crt" "${create_options[@]}" "$@"
-    certutil -A -d "$dbdir" -n "$nick" -f "$pwfile" -i "$crt" "${add_options[@]}"
-
-    mkdir -p "$(dirname $dbdir/$nick.pem)"
-    write_chain "$nick"
-    pk12util -o "$dbdir/$nick.p12" -n "$nick" -d "$dbdir" -k "$pwfile" -w "$pwfile"
-
-    rm -f "$pwfile" "$noise" "$csr" "$crt"
-}
-
-revoke_cert() {
-    local nick="$1" ca pwfile serial
-    shift 1
-
-    echo "revoke_cert(nick=$nick)"
-
-    ca="$(dirname $nick)"
-    if [ "$ca" = "." ]; then
-        ca="$nick"
-    fi
-
-    pwfile="$(mktemp)"
-    echo "$dbpassword" >"$pwfile"
-
-    if ! crlutil -L -d "$dbdir" -n "$ca" &>/dev/null; then
-        crlutil -G -d "$dbdir" -n "$ca" -c /dev/null -f "$pwfile"
-    fi
-
-    sleep 1
-
-    mkdir -p "$(dirname $dbdir/$ca.crl)"
-    serial=$(certutil -L -d "$dbdir" -n "$nick" | awk '/^\s+Serial Number: / { print $3 }')
-    crlutil -M -d "$dbdir" -n "$ca" -c /dev/stdin -f "$pwfile" -o "$dbdir/$ca.crl" <<EOF
-addcert $serial $(date -u +%Y%m%d%H%M%SZ)
-EOF
-
-    rm -f "$pwfile"
-}
-
-gen_server_certs() {
-    local nick="$1" hostname="$2" org="$3"
-    shift 3
-
-    echo "gen_server_certs(nick=$nick hostname=$hostname org=$org)"
-
-    gen_cert server "$nick" "CN=$hostname,O=$org" "$@"
-    gen_cert server "$nick-badname" "CN=not-$hostname,O=$org" "$@"
-    gen_cert server "$nick-altname" "CN=alt-$hostname,O=$org" -8 "$hostname" "$@"
-    gen_cert server "$nick-expired" "CN=$hostname,OU=Expired,O=$org" -w -24 "$@"
-    gen_cert server "$nick-badusage" "CN=$hostname,OU=Bad Usage,O=$org" --keyUsage dataEncipherment,keyAgreement "$@"
-    gen_cert server "$nick-revoked" "CN=$hostname,OU=Revoked,O=$org" "$@"
-    revoke_cert "$nick-revoked"
-}
-
-gen_pkinit_extensions() {
-   echo "[kdc_cert]
-basicConstraints=CA:FALSE
-keyUsage=nonRepudiation,digitalSignature,keyEncipherment,keyAgreement
-extendedKeyUsage=TLS Web Server Authentication, 1.3.6.1.5.2.3.5
-subjectKeyIdentifier=hash
-authorityKeyIdentifier=keyid,issuer
-issuerAltName=issuer:copy
-subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name
-[kdc_princ_name]
-realm=EXP:0,GeneralString:${realm}
-principal_name=EXP:1,SEQUENCE:kdc_principal_seq
-[kdc_principal_seq]
-name_type=EXP:0,INTEGER:1
-name_string=EXP:1,SEQUENCE:kdc_principals
-[kdc_principals]
-princ1=GeneralString:krbtgt
-princ2=GeneralString:${realm}" > "$dbdir/ext.kdc"
-}
-
-gen_pkinit_cert() {
-    local nick="$1" subj="$2" outname="$3"
-    shift 3
-
-    openssl genrsa -out "$dbdir/$nick/kdc.key" 2048 > /dev/null
-    openssl req -new -out "$dbdir/$nick/kdc.req" -key "$dbdir/$nick/kdc.key" \
-    -subj "$subj"
-
-    openssl pkcs12 -in "$dbdir/$nick.p12" -passin "pass:$dbpassword" \
-    -nodes -nocerts -out "$dbdir/$nick.key" > /dev/null
-
-    openssl x509 -req -in "$dbdir/$nick/kdc.req" \
-    -CAkey "$dbdir/$nick.key" -CA "$dbdir/$nick.pem" \
-    -out "$dbdir/$nick/kdc.crt" -days 365 \
-    -extfile "$dbdir/ext.kdc" -extensions kdc_cert -CAcreateserial > /dev/null
-
-    rm "$dbdir/$nick/kdc.req"
-
-    openssl pkcs12 -export -in "$dbdir/$nick/kdc.crt" \
-    -inkey "$dbdir/$nick/kdc.key" -password "pass:$dbpassword" \
-    -out "$dbdir/$nick/$outname.p12" -chain -CAfile "$dbdir/$nick.pem"
-}
-
-gen_subtree() {
-    local nick="$1" org="$2"
-    shift 2
-
-    echo "gen_subtree(nick=$nick org=$org)"
-
-    gen_cert ca "$nick" "CN=CA,O=$org" "$@"
-    gen_cert server "$nick/wildcard" "CN=*.$domain,O=$org"
-    gen_pkinit_cert "$nick" "/O=$realm/CN=$server1" "pkinit-server"
-    gen_pkinit_cert "$nick" "/O=$realm/CN=$server2" "pkinit-replica"
-    gen_server_certs "$nick/server" "$server1" "$org"
-    gen_server_certs "$nick/replica" "$server2" "$org"
-    gen_server_certs "$nick/client" "$client" "$org"
-}
-
-gen_cert server server-selfsign "CN=$server1,O=Self-signed"
-gen_cert server replica-selfsign "CN=$server2,O=Self-signed"
-gen_pkinit_extensions
-gen_cert server noca "CN=$server1,O=No-CA"
-gen_subtree ca1 'Example Organization'
-gen_subtree ca1/subca 'Subsidiary Example Organization'
-gen_subtree ca2 'Other Example Organization'
-gen_subtree ca3 'Unknown Organization'
-certutil -D -d "$dbdir" -n ca3
diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py
index 4b412f6619..6462631960 100644
--- a/ipatests/test_integration/test_caless.py
+++ b/ipatests/test_integration/test_caless.py
@@ -31,6 +31,7 @@
 from ipaplatform.paths import paths
 from ipapython.dn import DN
 from ipatests.test_integration.base import IntegrationTest
+from ipatests.test_integration.create_caless_pki import create_pki
 from ipatests.pytest_plugins.integration import tasks
 from ipalib.constants import DOMAIN_LEVEL_0
 
@@ -111,9 +112,6 @@ class CALessBase(IntegrationTest):
     def install(cls, mh):
         cls.cert_dir = tempfile.mkdtemp(prefix="ipatest-")
         cls.pem_filename = os.path.join(cls.cert_dir, 'root.pem')
-        scriptfile = os.path.join(os.path.dirname(__file__),
-                                  'scripts',
-                                  'caless-create-pki')
         cls.cert_password = cls.master.config.admin_password
 
         cls.crl_path = os.path.join(cls.master.config.test_dir, 'crl')
@@ -126,26 +124,25 @@ def install(cls, mh):
             client_hostname = cls.clients[0].hostname
         else:
             client_hostname = 'unused-client.test'
-        cls.env = {
+        cls.pki_vars = {
             'domain': cls.master.domain.name,
             'realm': cls.master.domain.name.upper(),
             'server1': cls.master.hostname,
             'server2': replica_hostname,
             'client': client_hostname,
-            'dbdir': 'nssdb',
-            'dbpassword': cls.cert_password,
-            'crl_path': cls.crl_path,
-            'dirman_password': cls.master.config.dirman_password,
+            'password': cls.master.config.dirman_password,
+            'cert_dir': cls.cert_dir,
         }
-        ipautil.run(['bash', '-ex', scriptfile], cwd=cls.cert_dir, env=cls.env)
+        # here we generate our certificates (not yet converted to .p12)
+        cls.log.info('Generating certificates to %s', cls.cert_dir)
+        create_pki(cls.pki_vars)
 
         for host in cls.get_all_hosts():
             tasks.apply_common_fixes(host)
 
             # Copy CRLs over
-            base = os.path.join(cls.cert_dir, 'nssdb')
             host.transport.mkdir_recursive(cls.crl_path)
-            for source in glob.glob(os.path.join(base, '*.crl')):
+            for source in glob.glob(os.path.join(cls.crl_path, '*.crl')):
                 dest = os.path.join(cls.crl_path, os.path.basename(source))
                 host.transport.put_file(source, dest)
 
@@ -163,7 +160,7 @@ def install_server(cls, host=None,
                        http_pkcs12_exists=True, dirsrv_pkcs12_exists=True,
                        http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, pkinit_pin=None,
                        root_ca_file='root.pem', pkinit_pkcs12_exists=False,
-                       pkinit_pkcs12='pkinit-server.p12', unattended=True,
+                       pkinit_pkcs12='server-kdc.p12', unattended=True,
                        stdin_text=None):
         """Install a CA-less server
 
@@ -189,8 +186,8 @@ def install_server(cls, host=None,
             files_to_copy.append(http_pkcs12)
         if dirsrv_pkcs12_exists:
             files_to_copy.append(dirsrv_pkcs12)
-
         if pkinit_pkcs12_exists:
+            files_to_copy.append(pkinit_pkcs12)
             extra_args.extend(['--pkinit-cert-file', pkinit_pkcs12])
         else:
             extra_args.append('--no-pkinit')
@@ -217,19 +214,13 @@ def install_server(cls, host=None,
     def copy_cert(cls, host, filename):
         host.transport.put_file(os.path.join(cls.cert_dir, filename),
                                 os.path.join(host.config.test_dir, filename))
-    @classmethod
-    def copy_pkinit_cert(cls, host, pkinit_nick):
-        filename = pkinit_nick.split('/')[-1]
-        host.transport.put_file(os.path.join(cls.cert_dir, 'nssdb', pkinit_nick),
-                                os.path.join(host.config.test_dir, filename))
-
 
     def prepare_replica(self, _replica_number=0, replica=None, master=None,
                         http_pkcs12='replica.p12', dirsrv_pkcs12='replica.p12',
                         http_pkcs12_exists=True, dirsrv_pkcs12_exists=True,
                         http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, pkinit_pin=None,
                         root_ca_file='root.pem', pkinit_pkcs12_exists=False,
-                        pkinit_pkcs12='pkinit-replica.p12', unattended=True,
+                        pkinit_pkcs12='replica-kdc.p12', unattended=True,
                         stdin_text=None, domain_level=None):
         """Prepare a CA-less replica
 
@@ -256,6 +247,8 @@ def prepare_replica(self, _replica_number=0, replica=None, master=None,
             files_to_copy.append(http_pkcs12)
         if dirsrv_pkcs12_exists:
             files_to_copy.append(dirsrv_pkcs12)
+        if pkinit_pkcs12_exists:
+            files_to_copy.append(pkinit_pkcs12)
         if domain_level == DOMAIN_LEVEL_0:
             destination_host = master
         else:
@@ -306,21 +299,53 @@ def get_replica_filename(self, replica):
                             'replica-info.gpg')
 
     @classmethod
-    def export_pkcs12(cls, nickname, filename='server.p12', password=None):
-        """Export a cert as PKCS#12 to the given file"""
+    def create_pkcs12(cls, nickname, filename='server.p12', password=None):
+        """Create a cert chain and generate pkcs12 cert"""
         if password is None:
             password = cls.cert_password
-        ipautil.run(['pk12util',
-                     '-o', filename,
-                     '-n', nickname,
-                     '-d', 'nssdb',
-                     '-K', cls.cert_password,
-                     '-W', password], cwd=cls.cert_dir)
+
+        fname_chain = []
+
+        key_fname = os.path.join(cls.cert_dir, nickname + '.key')
+        certchain_fname = os.path.join(cls.cert_dir, nickname + '.pem')
+
+        nick_chain = nickname.split('/')
+
+        # to construct whole chain e.g "ca1 - ca/sub - ca1/sub/server"
+        for step in range(len(nick_chain)):
+            cert_path = '/'.join(nick_chain[:step+1])
+            fname_chain.append(os.path.join(cls.cert_dir, cert_path + '.crt'))
+        # to simulate unknown CA we drop ca3 from the chain
+        if nickname.startswith('ca3/'):
+            fname_chain.pop(0)
+
+        with open(certchain_fname, 'w') as chain:
+                for cert_fname in fname_chain:
+                    with open(cert_fname) as cert:
+                        chain.write(cert.read())
+
+        ipautil.run(["openssl", "pkcs12", "-export", "-out", filename, "-inkey",
+                    key_fname, "-in", certchain_fname, "-passin",
+                    "pass:"+cls.cert_password, "-passout", "pass:"+password,
+                    '-name', nickname], cwd=cls.cert_dir)
+
+
+    @classmethod
+    def prepare_pem(cls, nickname):
+        """ Prepare pem file for root_ca_file/ca-cert-file option """
+        # makepki.py saves certificates with ".crt" extension by default
+        suffix = '.crt'
+        fname_from_nick = os.path.join(cls.cert_dir, nickname + suffix)
+        ipautil.run(['cp', fname_from_nick, cls.pem_filename],
+                    cwd=cls.cert_dir)
 
     @classmethod
     def get_pem(cls, nickname):
+        """ Return cert as base64 encoded ascii for TestIPACommands """
+        suffix = '.crt'
+        fname_from_nick = os.path.join(cls.cert_dir, nickname + suffix)
         result = ipautil.run(
-            [paths.CERTUTIL, '-L', '-d', 'nssdb', '-n', nickname, '-a'],
+            ['cat', fname_from_nick],
             cwd=cls.cert_dir, capture_output=True)
         return result.output
 
@@ -369,9 +394,8 @@ class TestServerInstall(CALessBase):
     def test_nonexistent_ca_pem_file(self):
         "IPA server install with non-existent CA PEM file "
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca2'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca2')
 
         result = self.install_server(root_ca_file='does_not_exist')
         assert_error(result,
@@ -382,9 +406,8 @@ def test_nonexistent_ca_pem_file(self):
     def test_unknown_ca(self):
         "IPA server install with CA PEM file with unknown CA certificate"
 
-        self.export_pkcs12('ca3/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca2'))
+        self.create_pkcs12('ca3/server')
+        self.prepare_pem('ca2')
 
         result = self.install_server()
         assert_error(result,
@@ -394,9 +417,8 @@ def test_unknown_ca(self):
     def test_ca_server_cert(self):
         "IPA server install with CA PEM file with server certificate"
 
-        self.export_pkcs12('noca')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('noca'))
+        self.create_pkcs12('noca')
+        self.prepare_pem('noca')
 
         result = self.install_server()
         assert_error(result,
@@ -407,10 +429,9 @@ def test_ca_server_cert(self):
     def test_ca_2_certs(self):
         "IPA server install with CA PEM file with 2 certificates"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
-            f.write(self.get_pem('ca2'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
+        self.prepare_pem('ca2')
 
         result = self.install_server()
         assert_error(result, 'root.pem contains more than one certificate')
@@ -419,9 +440,8 @@ def test_ca_2_certs(self):
     def test_nonexistent_http_pkcs12_file(self):
         "IPA server install with non-existent HTTP PKCS#12 file"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='does_not_exist',
                                      http_pkcs12_exists=False)
@@ -431,9 +451,8 @@ def test_nonexistent_http_pkcs12_file(self):
     def test_nonexistent_ds_pkcs12_file(self):
         "IPA server install with non-existent DS PKCS#12 file"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca2')
 
         result = self.install_server(dirsrv_pkcs12='does_not_exist',
                                      dirsrv_pkcs12_exists=False)
@@ -443,9 +462,8 @@ def test_nonexistent_ds_pkcs12_file(self):
     def test_missing_http_password(self):
         "IPA server install with missing HTTP PKCS#12 password (unattended)"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pin=None)
         assert_error(result,
@@ -456,9 +474,8 @@ def test_missing_http_password(self):
     def test_missing_ds_password(self):
         "IPA server install with missing DS PKCS#12 password (unattended)"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         result = self.install_server(dirsrv_pin=None)
         assert_error(result,
@@ -470,9 +487,8 @@ def test_missing_ds_password(self):
     def test_incorect_http_pin(self):
         "IPA server install with incorrect HTTP PKCS#12 password"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pin='bad<pin>')
         assert_error(result, 'incorrect password for pkcs#12 file server.p12')
@@ -482,9 +498,8 @@ def test_incorect_http_pin(self):
     def test_incorect_ds_pin(self):
         "IPA server install with incorrect DS PKCS#12 password"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         result = self.install_server(dirsrv_pin='bad<pin>')
         assert_error(result, 'incorrect password for pkcs#12 file server.p12')
@@ -493,10 +508,9 @@ def test_incorect_ds_pin(self):
     def test_invalid_http_cn(self):
         "IPA server install with HTTP certificate with invalid CN"
 
-        self.export_pkcs12('ca1/server-badname', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server-badname', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -508,9 +522,8 @@ def test_invalid_http_cn(self):
     def test_invalid_ds_cn(self):
         "IPA server install with DS certificate with invalid CN"
 
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -522,10 +535,9 @@ def test_invalid_ds_cn(self):
     def test_expired_http(self):
         "IPA server install with expired HTTP certificate"
 
-        self.export_pkcs12('ca1/server-expired', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server-expired', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -537,10 +549,9 @@ def test_expired_http(self):
     def test_expired_ds(self):
         "IPA server install with expired DS certificate"
 
-        self.export_pkcs12('ca1/server', filename='http.p12')
-        self.export_pkcs12('ca1/server-expired', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12')
+        self.create_pkcs12('ca1/server-expired', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -552,10 +563,9 @@ def test_expired_ds(self):
     def test_http_bad_usage(self):
         "IPA server install with HTTP certificate with invalid key usage"
 
-        self.export_pkcs12('ca1/server-badusage', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server-badusage', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -567,10 +577,9 @@ def test_http_bad_usage(self):
     def test_ds_bad_usage(self):
         "IPA server install with DS certificate with invalid key usage"
 
-        self.export_pkcs12('ca1/server', filename='http.p12')
-        self.export_pkcs12('ca1/server-badusage', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12')
+        self.create_pkcs12('ca1/server-badusage', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -582,10 +591,9 @@ def test_ds_bad_usage(self):
     def test_revoked_http(self):
         "IPA server install with revoked HTTP certificate"
 
-        self.export_pkcs12('ca1/server-revoked', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server-revoked', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -601,10 +609,9 @@ def test_revoked_http(self):
     def test_revoked_ds(self):
         "IPA server install with revoked DS certificate"
 
-        self.export_pkcs12('ca1/server', filename='http.p12')
-        self.export_pkcs12('ca1/server-revoked', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12')
+        self.create_pkcs12('ca1/server-revoked', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -620,10 +627,9 @@ def test_revoked_ds(self):
     def test_http_intermediate_ca(self):
         "IPA server install with HTTP certificate issued by intermediate CA"
 
-        self.export_pkcs12('ca1/subca/server', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/subca/server', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -635,10 +641,9 @@ def test_http_intermediate_ca(self):
     def test_ds_intermediate_ca(self):
         "IPA server install with DS certificate issued by intermediate CA"
 
-        self.export_pkcs12('ca1/server', filename='http.p12')
-        self.export_pkcs12('ca1/subca/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12')
+        self.create_pkcs12('ca1/subca/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -650,9 +655,8 @@ def test_ds_intermediate_ca(self):
     def test_ca_self_signed(self):
         "IPA server install with self-signed certificate"
 
-        self.export_pkcs12('server-selfsign')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('server-selfsign'))
+        self.create_pkcs12('server-selfsign')
+        self.prepare_pem('server-selfsign')
 
         result = self.install_server()
         assert result.returncode > 0
@@ -661,9 +665,8 @@ def test_ca_self_signed(self):
     def test_valid_certs(self):
         "IPA server install with valid certificates"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         result = self.install_server()
         assert result.returncode == 0
@@ -674,10 +677,9 @@ def test_valid_certs(self):
     def test_wildcard_http(self):
         "IPA server install with wildcard HTTP certificate"
 
-        self.export_pkcs12('ca1/wildcard', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/wildcard', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -689,10 +691,9 @@ def test_wildcard_http(self):
     def test_wildcard_ds(self):
         "IPA server install with wildcard DS certificate"
 
-        self.export_pkcs12('ca1/server', filename='http.p12')
-        self.export_pkcs12('ca1/wildcard', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12')
+        self.create_pkcs12('ca1/wildcard', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -703,10 +704,9 @@ def test_wildcard_ds(self):
     def test_http_san(self):
         "IPA server install with HTTP certificate with SAN"
 
-        self.export_pkcs12('ca1/server-altname', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server-altname', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -717,10 +717,9 @@ def test_http_san(self):
     def test_ds_san(self):
         "IPA server install with DS certificate with SAN"
 
-        self.export_pkcs12('ca1/server', filename='http.p12')
-        self.export_pkcs12('ca1/server-altname', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12')
+        self.create_pkcs12('ca1/server-altname', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12')
@@ -731,9 +730,8 @@ def test_ds_san(self):
     def test_interactive_missing_http_pkcs_password(self):
         "IPA server install with prompt for HTTP PKCS#12 password"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         stdin_text = get_install_stdin(cert_passwords=[self.cert_password])
 
@@ -748,9 +746,8 @@ def test_interactive_missing_http_pkcs_password(self):
     def test_interactive_missing_ds_pkcs_password(self):
         "IPA server install with prompt for DS PKCS#12 password"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         stdin_text = get_install_stdin(cert_passwords=[self.cert_password])
 
@@ -765,10 +762,9 @@ def test_interactive_missing_ds_pkcs_password(self):
     def test_no_http_password(self):
         "IPA server install with empty HTTP password"
 
-        self.export_pkcs12('ca1/server', filename='http.p12', password='')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12', password='')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12',
@@ -780,10 +776,9 @@ def test_no_http_password(self):
     def test_no_ds_password(self):
         "IPA server install with empty DS password"
 
-        self.export_pkcs12('ca1/server', filename='http.p12')
-        self.export_pkcs12('ca1/server', filename='dirsrv.p12', password='')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server', filename='http.p12')
+        self.create_pkcs12('ca1/server', filename='dirsrv.p12', password='')
+        self.prepare_pem('ca1')
 
         result = self.install_server(http_pkcs12='http.p12',
                                      dirsrv_pkcs12='dirsrv.p12',
@@ -798,9 +793,8 @@ class TestReplicaInstall(CALessBase):
     @classmethod
     def install(cls, mh):
         super(TestReplicaInstall, cls).install(mh)
-        cls.export_pkcs12('ca1/server')
-        with open(cls.pem_filename, 'w') as f:
-            f.write(cls.get_pem('ca1'))
+        cls.create_pkcs12('ca1/server')
+        cls.prepare_pem('ca1')
         result = cls.install_server()
         assert result.returncode == 0
 
@@ -818,7 +812,7 @@ def test_no_certs(self):
     def test_nonexistent_http_pkcs12_file(self):
         "IPA replica install with non-existent DS PKCS#12 file"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
 
         result = self.prepare_replica(dirsrv_pkcs12='does_not_exist',
                                       http_pkcs12='http.p12')
@@ -828,7 +822,7 @@ def test_nonexistent_http_pkcs12_file(self):
     def test_nonexistent_ds_pkcs12_file(self):
         "IPA replica install with non-existent HTTP PKCS#12 file"
 
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='does_not_exist',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -839,7 +833,7 @@ def test_nonexistent_ds_pkcs12_file(self):
     def test_incorect_http_pin(self):
         "IPA replica install with incorrect HTTP PKCS#12 password"
 
-        self.export_pkcs12('ca1/replica', filename='replica.p12')
+        self.create_pkcs12('ca1/replica', filename='replica.p12')
 
         result = self.prepare_replica(http_pin='bad<pin>')
         assert result.returncode > 0
@@ -850,7 +844,7 @@ def test_incorect_http_pin(self):
     def test_incorect_ds_pin(self):
         "IPA replica install with incorrect DS PKCS#12 password"
 
-        self.export_pkcs12('ca1/replica', filename='replica.p12')
+        self.create_pkcs12('ca1/replica', filename='replica.p12')
 
         result = self.prepare_replica(dirsrv_pin='bad<pin>')
         assert_error(result, 'incorrect password for pkcs#12 file replica.p12')
@@ -859,8 +853,8 @@ def test_incorect_ds_pin(self):
     def test_http_unknown_ca(self):
         "IPA replica install with HTTP certificate issued by unknown CA"
 
-        self.export_pkcs12('ca2/replica', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca2/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -872,8 +866,8 @@ def test_http_unknown_ca(self):
     def test_ds_unknown_ca(self):
         "IPA replica install with DS certificate issued by unknown CA"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
-        self.export_pkcs12('ca2/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca2/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -885,8 +879,8 @@ def test_ds_unknown_ca(self):
     def test_invalid_http_cn(self):
         "IPA replica install with HTTP certificate with invalid CN"
 
-        self.export_pkcs12('ca1/replica-badname', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica-badname', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -898,8 +892,8 @@ def test_invalid_http_cn(self):
     def test_invalid_ds_cn(self):
         "IPA replica install with DS certificate with invalid CN"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
-        self.export_pkcs12('ca1/replica-badname', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica-badname', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -911,8 +905,8 @@ def test_invalid_ds_cn(self):
     def test_expired_http(self):
         "IPA replica install with expired HTTP certificate"
 
-        self.export_pkcs12('ca1/replica-expired', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica-expired', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -924,8 +918,8 @@ def test_expired_http(self):
     def test_expired_ds(self):
         "IPA replica install with expired DS certificate"
 
-        self.export_pkcs12('ca1/replica-expired', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica-expired', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -937,8 +931,8 @@ def test_expired_ds(self):
     def test_http_bad_usage(self):
         "IPA replica install with HTTP certificate with invalid key usage"
 
-        self.export_pkcs12('ca1/replica-badusage', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica-badusage', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -950,8 +944,8 @@ def test_http_bad_usage(self):
     def test_ds_bad_usage(self):
         "IPA replica install with DS certificate with invalid key usage"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
-        self.export_pkcs12('ca1/replica-badusage', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica-badusage', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -963,8 +957,8 @@ def test_ds_bad_usage(self):
     def test_revoked_http(self):
         "IPA replica install with revoked HTTP certificate"
 
-        self.export_pkcs12('ca1/replica-revoked', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica-revoked', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -980,8 +974,8 @@ def test_revoked_http(self):
     def test_revoked_ds(self):
         "IPA replica install with revoked DS certificate"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
-        self.export_pkcs12('ca1/replica-revoked', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica-revoked', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -997,8 +991,8 @@ def test_revoked_ds(self):
     def test_http_intermediate_ca(self):
         "IPA replica install with HTTP certificate issued by intermediate CA"
 
-        self.export_pkcs12('ca1/subca/replica', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/subca/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -1010,8 +1004,8 @@ def test_http_intermediate_ca(self):
     def test_ds_intermediate_ca(self):
         "IPA replica install with DS certificate issued by intermediate CA"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
-        self.export_pkcs12('ca1/subca/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca1/subca/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -1023,7 +1017,7 @@ def test_ds_intermediate_ca(self):
     def test_valid_certs(self):
         "IPA replica install with valid certificates"
 
-        self.export_pkcs12('ca1/replica', filename='server.p12')
+        self.create_pkcs12('ca1/replica', filename='server.p12')
 
         result = self.prepare_replica(http_pkcs12='server.p12',
                                       dirsrv_pkcs12='server.p12')
@@ -1036,8 +1030,8 @@ def test_valid_certs(self):
     def test_wildcard_http(self):
         "IPA replica install with wildcard HTTP certificate"
 
-        self.export_pkcs12('ca1/wildcard', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/wildcard', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -1050,8 +1044,8 @@ def test_wildcard_http(self):
     def test_wildcard_ds(self):
         "IPA replica install with wildcard DS certificate"
 
-        self.export_pkcs12('ca1/wildcard', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/wildcard', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -1063,8 +1057,8 @@ def test_wildcard_ds(self):
     def test_http_san(self):
         "IPA replica install with HTTP certificate with SAN"
 
-        self.export_pkcs12('ca1/replica-altname', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica-altname', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -1076,8 +1070,8 @@ def test_http_san(self):
     def test_ds_san(self):
         "IPA replica install with DS certificate with SAN"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
-        self.export_pkcs12('ca1/replica-altname', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica-altname', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12')
@@ -1089,7 +1083,7 @@ def test_ds_san(self):
     def test_interactive_missing_http_pkcs_password(self):
         "IPA replica install with missing HTTP PKCS#12 password"
 
-        self.export_pkcs12('ca1/replica', filename='replica.p12')
+        self.create_pkcs12('ca1/replica', filename='replica.p12')
 
         stdin_text = get_replica_prepare_stdin(
             cert_passwords=[self.cert_password])
@@ -1104,7 +1098,7 @@ def test_interactive_missing_http_pkcs_password(self):
     def test_interactive_missing_ds_pkcs_password(self):
         "IPA replica install with missing DS PKCS#12 password"
 
-        self.export_pkcs12('ca1/replica', filename='replica.p12')
+        self.create_pkcs12('ca1/replica', filename='replica.p12')
 
         stdin_text = get_replica_prepare_stdin(
             cert_passwords=[self.cert_password])
@@ -1119,8 +1113,8 @@ def test_interactive_missing_ds_pkcs_password(self):
     def test_no_http_password(self):
         "IPA replica install with empty HTTP password"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12', password='')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+        self.create_pkcs12('ca1/replica', filename='http.p12', password='')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12',
@@ -1133,8 +1127,8 @@ def test_no_http_password(self):
     def test_no_ds_password(self):
         "IPA replica install with empty DS password"
 
-        self.export_pkcs12('ca1/replica', filename='http.p12')
-        self.export_pkcs12('ca1/replica', filename='dirsrv.p12', password='')
+        self.create_pkcs12('ca1/replica', filename='http.p12')
+        self.create_pkcs12('ca1/replica', filename='dirsrv.p12', password='')
 
         result = self.prepare_replica(http_pkcs12='http.p12',
                                       dirsrv_pkcs12='dirsrv.p12',
@@ -1150,9 +1144,8 @@ class TestClientInstall(CALessBase):
     def test_client_install(self):
         "IPA client install"
 
-        self.export_pkcs12('ca1/server')
-        with open(self.pem_filename, 'w') as f:
-            f.write(self.get_pem('ca1'))
+        self.create_pkcs12('ca1/server')
+        self.prepare_pem('ca1')
 
         result = self.install_server()
         assert result.returncode == 0
@@ -1172,9 +1165,8 @@ class TestIPACommands(CALessBase):
     def install(cls, mh):
         super(TestIPACommands, cls).install(mh)
 
-        cls.export_pkcs12('ca1/server')
-        with open(cls.pem_filename, 'w') as f:
-            f.write(cls.get_pem('ca1'))
+        cls.create_pkcs12('ca1/server')
+        cls.prepare_pem('ca1')
 
         result = cls.install_server()
         assert result.returncode == 0
@@ -1258,14 +1250,13 @@ def test_host_del_doesnt_revoke(self):
             self.master.run_command(['ipa', 'host-del', self.test_hostname])
 
 
-class TestCertinstall(CALessBase):
+class TestCertInstall(CALessBase):
     @classmethod
     def install(cls, mh):
-        super(TestCertinstall, cls).install(mh)
+        super(TestCertInstall, cls).install(mh)
 
-        cls.export_pkcs12('ca1/server')
-        with open(cls.pem_filename, 'w') as f:
-            f.write(cls.get_pem('ca1'))
+        cls.create_pkcs12('ca1/server')
+        cls.prepare_pem('ca1')
 
         result = cls.install_server()
         assert result.returncode == 0
@@ -1276,7 +1267,7 @@ def certinstall(self, mode, cert_nick=None, cert_exists=True,
                     filename='server.p12', pin=_DEFAULT, stdin_text=None,
                     p12_pin=None, args=None):
         if cert_nick:
-            self.export_pkcs12(cert_nick, password=p12_pin)
+            self.create_pkcs12(cert_nick, password=p12_pin)
         if pin is _DEFAULT:
             pin = self.cert_password
         if cert_exists:
@@ -1511,18 +1502,17 @@ class TestPKINIT(CALessBase):
     @classmethod
     def install(cls, mh):
         super(TestPKINIT, cls).install(mh)
-        cls.export_pkcs12('ca1/server')
-        cls.copy_pkinit_cert(cls.master, 'ca1/pkinit-server.p12')
-        with open(cls.pem_filename, 'w') as f:
-            f.write(cls.get_pem('ca1'))
+        cls.create_pkcs12('ca1/server')
+        cls.create_pkcs12('ca1/server-kdc', filename='server-kdc.p12')
+        cls.prepare_pem('ca1')
         result = cls.install_server(pkinit_pkcs12_exists=True,
                                     pkinit_pin=_DEFAULT)
         assert result.returncode == 0
 
     @replica_install_teardown
     def test_server_replica_install_pkinit(self):
-        self.export_pkcs12('ca1/replica', filename='replica.p12')
-        self.copy_pkinit_cert(self.replicas[0], 'ca1/pkinit-replica.p12')
+        self.create_pkcs12('ca1/replica', filename='replica.p12')
+        self.create_pkcs12('ca1/replica-kdc', filename='replica-kdc.p12')
         result = self.prepare_replica(pkinit_pkcs12_exists=True,
                                       pkinit_pin=_DEFAULT)
         assert result.returncode == 0
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org

Reply via email to