Dne 26.9.2014 v 19:40 Jan Cholasta napsal(a):
Dne 26.9.2014 v 17:37 Rob Crittenden napsal(a):
Petr Viktorin wrote:
On 09/24/2014 06:13 PM, Jan Cholasta wrote:
Hi,

the attached patches fix <https://fedorahosted.org/freeipa/ticket/4480>
and <https://fedorahosted.org/freeipa/ticket/4489>.

(Note that design page for this is TBD.)

Isn't this backwards then?


336:

Instead of
     len(data[:match.start() + 1].splitlines())
you can do
     data.count('\n', 0, match.start()) + 1

Unfortunately '\n' is not good enough, we have to check for '\r\n' and
'\r' as well, hence the use of splitlines.


337:
The --external_cert_file and --external_ca_file options for
ipa-ca-install are removed, do we really want to do that? Shouldn't they
be deprecated instead?

+1


Same for --external-ca-file in ipa-cacert-manage.

+1

IMO it's OK to just remove them, as they were added during 4.1
development and are not available in any released version of IPA.



I can't say I'm a fan of forcing users to concatenate cert files.

All the --*-cert-file options may be given multiple times.



338: Looks OK
339: Looks OK

Could you add some docstrings to the functions you add? Sometimes it's
harder than necessary to decipher what they do and what the
arguments/return values mean exactly.

Sure.


There is no user-visible documentation on what file types are
expected/supported. It would be good to add this to the man pages, or
the --help.

Added.


I also wonder if the detection code should be changed. It basically now
tries a slew of different mechanisms one at a time rather than trying to
identify the type of file and using that one. It may not be possible in
all cases but you could at least start by looking for ^----- to know it
is a text file and go from there, otherwise step through the binary
formats.

Rearranged the code so that text files are tried first.





In external CA, the error message when specifying a certificate but not
the CA could be improved:
$ ipa-server-install --external_cert_file
~/p/Certificate_Authority_8.cer
...
CA certificate CN=Certificate Authority,O=IDM.LAB.ENG.BRQ.REDHAT.COM in
/home/pviktori/p/Certificate_Authority_8.cer is not valid:
(SEC_ERROR_UNKNOWN_ISSUER) Peer's Certificate issuer is not recognized.

Fixed.




For CA-less, I used a combination of files with which server
installation went well, but replica-install failed halfway through:

Console:
...
   [16/36]: creating indices
   [17/36]: enabling referential integrity plugin
   [18/36]: configuring ssl for ds instance
   [error] RuntimeError: incorrect password for pkcs#12 file
/tmp/tmp2vEWX_ipa/realm_info/dscert.p12

Log tail:

2014-09-26T15:05:43Z DEBUG Starting external process
2014-09-26T15:05:43Z DEBUG args='/usr/bin/pk12util' '-d'
'/etc/dirsrv/slapd-IDM-LAB-ENG-BRQ-REDHAT-COM/' '-i'
'/tmp/tmp2vEWX_ipa/realm_info/dscert.p12' '-k'
'/etc/dirsrv/slapd-IDM-LAB-ENG-BRQ-REDHAT-COM//pwdfile.txt' '-v' '-w'
'/dev/stdin'
2014-09-26T15:05:43Z DEBUG Process finished, return code=17
2014-09-26T15:05:43Z DEBUG stdout=
2014-09-26T15:05:43Z DEBUG stderr=pk12util: PKCS12 decode not verified:
SEC_ERROR_BAD_PASSWORD: The security password entered is incorrect.

2014-09-26T15:05:43Z DEBUG Traceback (most recent call last):
   File "/usr/lib/python2.7/site-packages/ipaserver/install/service.py",
line 370, in start_creation
     run_step(full_msg, method)
   File "/usr/lib/python2.7/site-packages/ipaserver/install/service.py",
line 360, in run_step
     method()
   File
"/usr/lib/python2.7/site-packages/ipaserver/install/dsinstance.py", line
600, in __enable_ssl
     trust_flags=trust_flags)
   File "/usr/lib/python2.7/site-packages/ipaserver/install/certs.py",
line 1030, in create_from_pkcs12
     self.import_pkcs12(pkcs12_fname, pkcs12_passwd)
   File "/usr/lib/python2.7/site-packages/ipaserver/install/certs.py",
line 971, in import_pkcs12
     pkcs12_passwd=pkcs12_passwd)
   File "/usr/lib/python2.7/site-packages/ipaserver/install/certs.py",
line 191, in import_pkcs12
     pkcs12_filename)
RuntimeError: incorrect password for pkcs#12 file
/tmp/tmp2vEWX_ipa/realm_info/dscert.p12

2014-09-26T15:05:43Z DEBUG   [error] RuntimeError: incorrect password
for pkcs#12 file /tmp/tmp2vEWX_ipa/realm_info/dscert.p12
2014-09-26T15:05:43Z DEBUG   File
"/usr/lib/python2.7/site-packages/ipaserver/install/installutils.py",
line 644, in run_script
     return_value = main_function()

   File "/sbin/ipa-replica-install", line 677, in main
     ds = install_replica_ds(config)

   File "/sbin/ipa-replica-install", line 190, in install_replica_ds
     ca_file=config.dir + "/ca.crt",

   File
"/usr/lib/python2.7/site-packages/ipaserver/install/dsinstance.py", line
354, in create_replica
     self.start_creation(runtime=60)

   File "/usr/lib/python2.7/site-packages/ipaserver/install/service.py",
line 370, in start_creation
     run_step(full_msg, method)

   File "/usr/lib/python2.7/site-packages/ipaserver/install/service.py",
line 360, in run_step
     method()

   File
"/usr/lib/python2.7/site-packages/ipaserver/install/dsinstance.py", line
600, in __enable_ssl
     trust_flags=trust_flags)

   File "/usr/lib/python2.7/site-packages/ipaserver/install/certs.py",
line 1030, in create_from_pkcs12
     self.import_pkcs12(pkcs12_fname, pkcs12_passwd)

   File "/usr/lib/python2.7/site-packages/ipaserver/install/certs.py",
line 971, in import_pkcs12
     pkcs12_passwd=pkcs12_passwd)

   File "/usr/lib/python2.7/site-packages/ipaserver/install/certs.py",
line 191, in import_pkcs12
     pkcs12_filename)

2014-09-26T15:05:43Z DEBUG The ipa-replica-install command failed,
exception: RuntimeError: incorrect password for pkcs#12 file
/tmp/tmp2vEWX_ipa/realm_info/dscert.p12


I'll attach the files for reference; the options for ipa-server-install
and ipa-replica-prepare were:

--http-cert-file=~/STAR.idm.lab.eng.brq.redhat.com_3.p12-nocacerts.p12
--http-cert-file
~/STAR.idm.lab.eng.brq.redhat.com_3.p12-allcerts-x509.pem --http-pin
12345678 --dirsrv-cert-file
~/STAR.idm.lab.eng.brq.redhat.com_3.p12-cacerts-pkcs7.pem
--dirsrv-cert-file ~/STAR.idm.lab.eng.brq.redhat.com_3.p12-nocacerts.p12
--dirsrv-pin 12345678

Fixed.

Updated patches attached.

Added patch 341 for stricter CA certificate validation which fixes <https://fedorahosted.org/freeipa/ticket/4477>.

Updated patches attached.

--
Jan Cholasta
>From 63c184416139239b4590e8cb8e13488277f92c50 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 24 Sep 2014 16:22:32 +0200
Subject: [PATCH 1/5] Add NSSDatabase.import_files method for importing files
 in various formats

The files are accepted in PEM and DER certificate, PKCS#7 certificate chain,
PKCS#8 and raw private key and PKCS#12 formats.

https://fedorahosted.org/freeipa/ticket/4480
https://fedorahosted.org/freeipa/ticket/4489
---
 ipaserver/install/certs.py | 191 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 191 insertions(+)

diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 4d508cd..82d8290 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -195,6 +195,197 @@ class NSSDatabase(object):
                 raise RuntimeError("unknown error import pkcs#12 file %s" %
                     pkcs12_filename)
 
+    def import_files(self, files, db_password_filename, import_keys=False,
+                     key_password=None, key_nickname=None):
+        """
+        Import certificates and a single private key from multiple files
+
+        The files may be in PEM and DER certificate, PKCS#7 certificate chain,
+        PKCS#8 and raw private key and PKCS#12 formats.
+
+        :param files: Names of files to import
+        :param db_password_filename: Name of file containing the database
+            password
+        :param import_keys: Whether to import private keys
+        :param key_password: Password to decrypt private keys
+        :param key_nickname: Nickname of the private key to import from PKCS#12
+            files
+        """
+        key_file = None
+        extracted_key = None
+        extracted_certs = ''
+
+        for filename in files:
+            try:
+                with open(filename, 'rb') as f:
+                    data = f.read()
+            except IOError as e:
+                raise RuntimeError(
+                    "Failed to open %s: %s" % (filename, e.strerror))
+
+            # Try to parse the file as PEM file
+            matches = list(re.finditer(
+                r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL))
+            if matches:
+                loaded = False
+                for match in matches:
+                    body = match.group()
+                    label = match.group(1)
+                    line = len(data[:match.start() + 1].splitlines())
+
+                    if label in ('CERTIFICATE', 'X509 CERTIFICATE',
+                                 'X.509 CERTIFICATE'):
+                        try:
+                            x509.load_certificate(match.group(2))
+                        except NSPRError as e:
+                            if label != 'CERTIFICATE':
+                                root_logger.warning(
+                                    "Skipping certificate in %s at line %s: %s",
+                                    filename, line, e)
+                                continue
+                        else:
+                            extracted_certs += body + '\n'
+                            loaded = True
+                            continue
+
+                    if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'):
+                        args = [
+                            paths.OPENSSL, 'pkcs7',
+                            '-print_certs',
+                        ]
+                        try:
+                            stdout, stderr, rc = ipautil.run(args, stdin=body)
+                        except ipautil.CalledProcessError as e:
+                            if label == 'CERTIFICATE':
+                                root_logger.warning(
+                                    "Skipping certificate in %s at line %s: %s",
+                                    filename, line, e)
+                            else:
+                                root_logger.warning(
+                                    "Skipping PKCS#7 in %s at line %s: %s",
+                                    filename, line, e)
+                            continue
+                        else:
+                            extracted_certs += stdout + '\n'
+                            loaded = True
+                            continue
+
+                    if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY',
+                                 'RSA PRIVATE KEY', 'DSA PRIVATE KEY',
+                                 'EC PRIVATE KEY'):
+                        if not import_keys:
+                            continue
+
+                        if key_file:
+                            raise RuntimeError(
+                                "Can't load private key from both %s and %s" %
+                                (key_file, filename))
+
+                        args = [
+                            paths.OPENSSL, 'pkcs8',
+                            '-topk8',
+                            '-passout', 'file:' + db_password_filename,
+                        ]
+                        if ((label != 'PRIVATE KEY' and key_password) or
+                            label == 'ENCRYPTED PRIVATE KEY'):
+                            key_pwdfile = ipautil.write_tmp_file(key_password)
+                            args += [
+                                '-passin', 'file:' + key_pwdfile.name,
+                            ]
+                        try:
+                            stdout, stderr, rc = ipautil.run(args, stdin=body)
+                        except ipautil.CalledProcessError as e:
+                            root_logger.warning(
+                                "Skipping private key in %s at line %s: %s",
+                                filename, line, e)
+                            continue
+                        else:
+                            extracted_key = stdout
+                            key_file = filename
+                            loaded = True
+                            continue
+                if loaded:
+                    continue
+                raise RuntimeError("Failed to load %s" % filename)
+
+            # Try to load the file as DER certificate
+            try:
+                x509.load_certificate(data, x509.DER)
+            except NSPRError:
+                pass
+            else:
+                data = x509.make_pem(base64.b64encode(data))
+                extracted_certs += data + '\n'
+                continue
+
+            # Try to import the file as PKCS#12 file
+            if import_keys:
+                try:
+                    self.import_pkcs12(
+                        filename, db_password_filename, key_password)
+                except RuntimeError:
+                    pass
+                else:
+                    if key_file:
+                        raise RuntimeError(
+                            "Can't load private key from both %s and %s" %
+                            (key_file, filename))
+                    key_file = filename
+
+                    server_certs = self.find_server_certs()
+                    if key_nickname:
+                        for nickname, trust_flags in server_certs:
+                            if nickname == key_nickname:
+                                break
+                        else:
+                            raise RuntimeError(
+                                "Server certificate \"%s\" not found in %s" %
+                                (key_nickname, filename))
+                    else:
+                        if len(server_certs) > 1:
+                            raise RuntimeError(
+                                "%s server certificates found in %s, "
+                                "expecting only one" %
+                                (len(server_certs), filename))
+
+                    continue
+
+            raise RuntimeError("Failed to load %s" % filename)
+
+        if import_keys and not key_file:
+            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
+
+        if extracted_key:
+            in_file = ipautil.write_tmp_file(extracted_certs + extracted_key)
+            out_file = tempfile.NamedTemporaryFile()
+            out_password = ipautil.ipa_generate_password()
+            out_pwdfile = ipautil.write_tmp_file(out_password)
+            args = [
+                paths.OPENSSL, 'pkcs12',
+                '-export',
+                '-in', in_file.name,
+                '-out', out_file.name,
+                '-passin', 'file:' + db_password_filename,
+                '-passout', 'file:' + out_pwdfile.name,
+            ]
+            try:
+                ipautil.run(args)
+            except ipautil.CalledProcessError as e:
+                raise RuntimeError(
+                    "No matching certificate found for private key from %s" %
+                    key_file)
+
+            self.import_pkcs12(out_file.name, db_password_filename,
+                               out_password)
+
     def trust_root_cert(self, root_nickname, trust_flags=None):
         if root_nickname[:7] == "Builtin":
             root_logger.debug(
-- 
1.9.3

>From 452765ef95f9a263ebda32891e3073e44ce3cb21 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 24 Sep 2014 16:31:39 +0200
Subject: [PATCH 2/5] External CA installer options usability fixes

The --external_cert_file and --external_ca_file options of ipa-server-install
and ipa-ca-install have been replaced by --external-cert-file option which
accepts multiple files. The files are accepted in PEM and DER certificate and
PKCS#7 certificate chain formats.

https://fedorahosted.org/freeipa/ticket/4480
---
 install/tools/ipa-ca-install                  |  34 +++-----
 install/tools/ipa-server-install              |  68 ++++++++--------
 install/tools/man/ipa-ca-install.1            |   3 +
 install/tools/man/ipa-cacert-manage.1         |   5 +-
 install/tools/man/ipa-server-install.1        |  11 +--
 ipaserver/install/cainstance.py               |   4 +-
 ipaserver/install/installutils.py             | 109 ++++++++++++++++----------
 ipaserver/install/ipa_cacert_manage.py        |  34 +++-----
 ipatests/test_integration/test_external_ca.py |   4 +-
 9 files changed, 133 insertions(+), 139 deletions(-)

diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
index fc89412..df8e34b 100755
--- a/install/tools/ipa-ca-install
+++ b/install/tools/ipa-ca-install
@@ -30,7 +30,7 @@ from ipaserver.install import installutils, service
 from ipaserver.install import certs
 from ipaserver.install.installutils import (HostnameLocalhost, ReplicaConfig,
         expand_replica_info, read_replica_info, get_host_name, BadHostError,
-        private_ccache, read_replica_info_dogtag_port, validate_external_cert)
+        private_ccache, read_replica_info_dogtag_port, load_external_cert)
 from ipaserver.install import dsinstance, cainstance, bindinstance
 from ipaserver.install.replication import replica_conn_check
 from ipapython import version
@@ -68,10 +68,9 @@ def parse_options():
                       default=False, help="unattended installation never prompts the user")
     parser.add_option("--external-ca", dest="external_ca", action="store_true",
                       default=False, help="Generate a CSR to be signed by an external CA")
-    parser.add_option("--external_cert_file", dest="external_cert_file",
-                      help="PEM file containing a certificate signed by the external CA")
-    parser.add_option("--external_ca_file", dest="external_ca_file",
-                      help="PEM file containing the external CA chain")
+    parser.add_option("--external-cert-file", dest="external_cert_files",
+                      action="append", metavar="FILE",
+                      help="File containing the IPA CA certificate and the external CA certificate chain")
 
     options, args = parser.parse_args()
     safe_options = parser.get_safe_opts(options)
@@ -86,12 +85,9 @@ def parse_options():
         filename = None
 
         if options.external_ca:
-            if options.external_cert_file:
-                parser.error("You cannot specify --external_cert_file "
+            if options.external_cert_files:
+                parser.error("You cannot specify --external-cert-file "
                              "together with --external-ca")
-            if options.external_ca_file:
-                parser.error("You cannot specify --external_ca_file together "
-                             "with --external-ca")
 
     return safe_options, options, filename
 
@@ -264,23 +260,19 @@ def install_master(safe_options, options):
     if options.external_ca:
         if cainstance.is_step_one_done():
             print ("CA is already installed.\nRun the installer with "
-                   "--external-cert-file and --external-ca-file.")
+                   "--external-cert-file.")
             sys.exit(1)
-    elif options.external_cert_file:
+    elif options.external_cert_files:
         if not cainstance.is_step_one_done():
             print ("CA is not installed yet. To install with an external CA "
                    "is a two-stage process.\nFirst run the installer with "
                    "--external-ca.")
             sys.exit(1)
 
-        try:
-            validate_external_cert(options.external_cert_file,
-                                   options.external_ca_file, subject_base)
-        except ValueError, e:
-            print e
-            sys.exit(1)
+        external_cert_file, external_ca_file = load_external_cert(
+            options.external_cert_files, subject_base)
 
-    if options.external_cert_file:
+    if options.external_cert_files:
         external = 2
     elif options.external_ca:
         external = 1
@@ -329,8 +321,8 @@ def install_master(safe_options, options):
     else:
         ca.configure_instance(host_name, domain_name, dm_password,
                               dm_password,
-                              cert_file=options.external_cert_file,
-                              cert_chain_file=options.external_ca_file,
+                              cert_file=external_cert_file.name,
+                              cert_chain_file=external_ca_file.name,
                               subject_base=subject_base)
 
     ca.stop(ca.dogtag_constants.PKI_INSTANCE_NAME)
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 4ec430e..e996736 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -38,7 +38,7 @@ import nss.error
 import base64
 import pwd
 import textwrap
-from optparse import OptionGroup, OptionValueError
+from optparse import OptionGroup, OptionValueError, SUPPRESS_HELP
 
 try:
     from ipaserver.install import adtrustinstance
@@ -203,10 +203,15 @@ def parse_options():
     cert_group = OptionGroup(parser, "certificate system options")
     cert_group.add_option("", "--external-ca", dest="external_ca", action="store_true",
                       default=False, help="Generate a CSR for the IPA CA certificate to be signed by an external CA")
-    cert_group.add_option("", "--external_cert_file", dest="external_cert_file",
-                      help="File containing the IPA CA certificate signed by the external CA in PEM format")
-    cert_group.add_option("", "--external_ca_file", dest="external_ca_file",
-                      help="File containing the external CA certificate chain in PEM format")
+    cert_group.add_option("--external-cert-file", dest="external_cert_files",
+                      action="append", metavar="FILE",
+                      help="File containing the IPA CA certificate and the external CA certificate chain")
+    cert_group.add_option("--external_cert_file", dest="external_cert_files",
+                      action="append",
+                      help=SUPPRESS_HELP)
+    cert_group.add_option("--external_ca_file", dest="external_cert_files",
+                      action="append",
+                      help=SUPPRESS_HELP)
     cert_group.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
                       default=True, help="disables pkinit setup steps")
     cert_group.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12",
@@ -320,25 +325,19 @@ def parse_options():
         if options.pkinit_pkcs12 and options.pkinit_pin is None:
             parser.error("You must specify --pkinit_pin with --pkinit_pkcs12")
 
-    if (options.external_cert_file or options.external_ca_file) and options.dirsrv_pkcs12:
-        parser.error(
-            "PKCS#12 options cannot be used with the external CA options.")
+    if options.external_cert_files and options.dirsrv_pkcs12:
+        parser.error("Service certificate file options cannot be used with "
+                     "the external CA options.")
 
     if options.external_ca:
-        if options.external_cert_file:
-            parser.error("You cannot specify --external_cert_file together with --external-ca")
-        if options.external_ca_file:
-            parser.error("You cannot specify --external_ca_file together with --external-ca")
+        if options.external_cert_files:
+            parser.error("You cannot specify --external-cert-file "
+                         "together with --external-ca")
         if options.dirsrv_pkcs12:
             parser.error("You cannot specify PKCS#12 options together with --external-ca")
 
-    if ((options.external_cert_file and not options.external_ca_file) or
-       (not options.external_cert_file and options.external_ca_file)):
-        parser.error("if either external CA option is used, both are required.")
-
-    if (options.external_ca_file and not os.path.isabs(options.external_ca_file)):
-        parser.error("--external-ca-file must use an absolute path")
-    if (options.external_cert_file and not os.path.isabs(options.external_cert_file)):
+    if (options.external_cert_files and
+        any(not os.path.isabs(path) for path in options.external_cert_files)):
         parser.error("--external-cert-file must use an absolute path")
 
     if options.idmax == 0:
@@ -392,11 +391,10 @@ def read_cache(dm_password):
         shutil.rmtree(top_dir)
 
     # These are the only ones that may be overridden
-    for opt in ('external_ca_file', 'external_cert_file'):
-        try:
-            del optdict[opt]
-        except KeyError:
-            pass
+    try:
+        del optdict['external_cert_files']
+    except KeyError:
+        pass
 
     return optdict
 
@@ -626,7 +624,7 @@ def main():
     else:
         standard_logging_setup(paths.IPASERVER_INSTALL_LOG, debug=options.debug)
         print "\nThe log file for this installation can be found in /var/log/ipaserver-install.log"
-        if not options.external_ca and not options.external_cert_file and is_ipa_configured():
+        if not options.external_ca and not options.external_cert_files and is_ipa_configured():
             installation_cleanup = False
             sys.exit("IPA server is already configured on this system.\n" +
                 "If you want to reinstall the IPA server, please uninstall " +
@@ -719,14 +717,14 @@ def main():
     if options.external_ca:
         if cainstance.is_step_one_done():
             print ("CA is already installed.\nRun the installer with "
-                   "--external_cert_file and --external_ca_file.")
+                   "--external-cert-file.")
             sys.exit(1)
         if ipautil.file_exists(paths.ROOT_IPA_CSR):
             print ("CA CSR file %s already exists.\nIn order to continue "
                    "remove the file and run the installer again." %
                    paths.ROOT_IPA_CSR)
             sys.exit(1)
-    elif options.external_cert_file:
+    elif options.external_cert_files:
         if not cainstance.is_step_one_done():
             # This can happen if someone passes external_ca_file without
             # already having done the first stage of the CA install.
@@ -748,13 +746,9 @@ def main():
         except Exception, e:
             sys.exit("Cannot process the cache file: %s" % str(e))
 
-    if options.external_cert_file:
-        try:
-            validate_external_cert(options.external_cert_file,
-                                   options.external_ca_file, options.subject)
-        except ValueError, e:
-            print e
-            sys.exit(1)
+    if options.external_cert_files:
+        external_cert_file, external_ca_file = load_external_cert(
+            options.external_cert_files, options.subject)
 
     # We only set up the CA if the PKCS#12 options are not given.
     if options.dirsrv_pkcs12:
@@ -764,7 +758,7 @@ def main():
 
     # Figure out what external CA step we're in. See cainstance.py for more
     # info on the 3 states.
-    if options.external_cert_file:
+    if options.external_cert_files:
         external = 2
     elif options.external_ca:
         external = 1
@@ -1101,8 +1095,8 @@ def main():
             # stage 2 of external CA installation
             ca.configure_instance(host_name, domain_name, dm_password,
                                   dm_password,
-                                  cert_file=options.external_cert_file,
-                                  cert_chain_file=options.external_ca_file,
+                                  cert_file=external_cert_file.name,
+                                  cert_chain_file=external_ca_file.name,
                                   subject_base=options.subject,
                                   ca_signing_algorithm=options.ca_signing_algorithm)
 
diff --git a/install/tools/man/ipa-ca-install.1 b/install/tools/man/ipa-ca-install.1
index 2e0b079..8f7201c 100644
--- a/install/tools/man/ipa-ca-install.1
+++ b/install/tools/man/ipa-ca-install.1
@@ -37,6 +37,9 @@ Directory Manager (existing master) password
 \fB\-w\fR \fIADMIN_PASSWORD\fR, \fB\-\-admin\-password\fR=\fIADMIN_PASSWORD\fR
 Admin user Kerberos password used for connection check
 .TP
+\fB\-\-external\-cert\-file\fR=\fIFILE\fR
+File containing the IPA CA certificate and the external CA certificate chain. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times.
+.TP
 \fB\-\-no\-host\-dns\fR
 Do not use DNS for hostname lookup during installation
 .TP
diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1
index 3006be7..1f37788 100644
--- a/install/tools/man/ipa-cacert-manage.1
+++ b/install/tools/man/ipa-cacert-manage.1
@@ -56,10 +56,7 @@ Sign the renewed certificate by itself.
 Sign the renewed certificate by external CA.
 .TP
 \fB\-\-external\-cert\-file\fR=\fIFILE\fR
-PEM file containing a certificate signed by the external CA. Must be given with \-\-external\-ca\-file.
-.TP
-\fB\-\-external\-ca\-file\fR=\fIFILE\fR
-PEM file containing the external CA chain.
+File containing the IPA CA certificate and the external CA certificate chain. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times.
 .TP
 \fB\-n\fR \fINICKNAME\fR, \fB\-\-nickname\fR=\fINICKNAME\fR
 Nickname for the certificate.
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index ecea26d..92d9ec8 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -87,15 +87,8 @@ An unattended installation that will never prompt for user input
 \fB\-\-external\-ca\fR
 Generate a CSR for the IPA CA certificate to be signed by an external CA.
 .TP
-\fB\-\-external_cert_file\fR=\fIFILE\fR
-File containing the IPA CA certificate signed by the external CA in PEM format. Must be given with \-\-external_ca_file.
-.TP
-\fB\-\-external_ca_file\fR=\fIFILE\fR
-File containing the external CA certificate chain in PEM format. Must be given with \-\-external_cert_file.
-
-If the CA certificate chain is in PKCS#7 format you can convert it to PEM using:
-
-    openssl pkcs7 -in PKCS7_FILE -print_certs -out PEM_FILE
+\fB\-\-external\-cert\-file\fR=\fIFILE\fR
+File containing the IPA CA certificate and the external CA certificate chain. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times.
 .TP
 \fB\-\-no\-pkinit\fR
 Disables pkinit setup steps
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 06087fa..521f25d 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -654,7 +654,7 @@ class CAInstance(service.Service):
 
         if self.external == 1:
             print "The next step is to get %s signed by your CA and re-run %s as:" % (self.csr_file, sys.argv[0])
-            print "%s --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate" % sys.argv[0]
+            print "%s --external-cert-file=/path/to/signed_certificate --external-cert-file=/path/to/external_ca_certificate" % sys.argv[0]
             sys.exit(0)
         else:
             shutil.move(paths.CA_BACKUP_KEYS_P12, \
@@ -803,7 +803,7 @@ class CAInstance(service.Service):
 
         if self.external == 1:
             print "The next step is to get %s signed by your CA and re-run %s as:" % (self.csr_file, sys.argv[0])
-            print "%s --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate" % sys.argv[0]
+            print "%s --external-cert-file=/path/to/signed_certificate --external-cert-file=/path/to/external_ca_certificate" % sys.argv[0]
             sys.exit(0)
 
         # pkisilent makes a copy of the CA PKCS#12 file for us but gives
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index a14b689..7dd8ce7 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -877,52 +877,77 @@ def check_entropy():
         root_logger.debug("Invalid value in /proc/sys/kernel/random/entropy_avail %s" % \
             e)
 
-def validate_external_cert(cert_file, ca_file, subject_base):
-    extcert = None
-    try:
-        extcert = x509.load_certificate_from_file(cert_file)
-        certsubject = DN(str(extcert.subject))
-        certissuer = DN(str(extcert.issuer))
-    except IOError, e:
-        raise ValueError("Can't load the PEM certificate: %s." % e)
-    except (TypeError, NSPRError):
-        raise ValueError(
-            "'%s' is not a valid PEM-encoded certificate." % cert_file)
-    finally:
-        del extcert
+def load_external_cert(files, subject_base):
+    """
+    Load and verify external CA certificate chain from multiple files.
 
-    wantsubject = DN(('CN', 'Certificate Authority'), subject_base)
-    if certsubject != wantsubject:
-        raise ValueError(
-            "Subject of the external certificate is not correct (got %s, "
-            "expected %s)." % (certsubject, wantsubject))
+    The files are accepted in PEM and DER certificate and PKCS#7 certificate
+    chain formats.
 
-    extchain = None
-    try:
-        extchain = x509.load_certificate_list_from_file(ca_file)
-        certdict = dict((DN(str(cert.subject)), DN(str(cert.issuer)))
-                        for cert in extchain)
-    except IOError, e:
-        raise ValueError("Can't load the external CA chain: %s." % e)
-    except (TypeError, NSPRError):
-        raise ValueError(
-            "'%s' is not a valid PEM-encoded certificate chain." % ca_file)
-    finally:
-        del extchain
-
-    if certissuer not in certdict:
-        raise ValueError(
-            "The external certificate is not signed by the external CA "
-            "(unknown issuer %s)." % certissuer)
+    :param files: Names of files to import
+    :param subject_base: Subject name base for IPA certificates
+    :returns: Temporary file with the IPA CA certificate and temporary file
+        with the external CA certificate chain
+    """
+    with certs.NSSDatabase() as nssdb:
+        db_password = ipautil.ipa_generate_password()
+        db_pwdfile = ipautil.write_tmp_file(db_password)
+        nssdb.create_db(db_pwdfile.name)
 
-    while certsubject != certissuer:
-        certsubject = certissuer
         try:
-            certissuer = certdict[certsubject]
-        except KeyError:
-            raise ValueError(
-                "The external CA chain is incomplete (%s is missing from the "
-                "chain)." % certsubject)
+            nssdb.import_files(files, db_pwdfile.name)
+        except RuntimeError as e:
+            raise ScriptError(str(e))
+
+        ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
+        ca_nickname = None
+        cache = {}
+        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
+
+            cache[nickname] = (cert, subject, issuer)
+            if subject == ca_subject:
+                ca_nickname = nickname
+            nssdb.trust_root_cert(nickname)
+
+        if ca_nickname is None:
+            raise ScriptError(
+                "IPA CA certificate not found in %s" % (", ".join(files)))
+
+        trust_chain = reversed(nssdb.get_trust_chain(ca_nickname))
+        ca_cert_chain = []
+        for nickname in trust_chain:
+            cert, subject, issuer = cache[nickname]
+            ca_cert_chain.append(cert)
+            if subject == issuer:
+                break
+        else:
+            raise ScriptError(
+                "CA certificate chain in %s is incomplete" %
+                (", ".join(files)))
+
+        for nickname in trust_chain:
+            try:
+                nssdb.verify_ca_cert_validity(nickname)
+            except ValueError, e:
+                raise ScriptError(
+                    "CA certificate %s in %s is not valid: %s" %
+                    (subject, ", ".join(files), e))
+
+    cert_file = tempfile.NamedTemporaryFile()
+    cert_file.write(ca_cert_chain[0] + '\n')
+    cert_file.flush()
+
+    ca_file = tempfile.NamedTemporaryFile()
+    ca_file.write('\n'.join(ca_cert_chain[1:]) + '\n')
+    ca_file.flush()
+
+    return cert_file, ca_file
 
 
 def create_system_user(name, group, homedir, shell):
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index c681261..6a7fd05 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -60,11 +60,10 @@ class CACertManage(admintool.AdminTool):
             action='store_false',
             help="Sign the renewed certificate by external CA")
         renew_group.add_option(
-            "--external-cert-file", dest='external_cert_file',
-            help="PEM file containing a certificate signed by the external CA")
-        renew_group.add_option(
-            "--external-ca-file", dest='external_ca_file',
-            help="PEM file containing the external CA chain")
+            "--external-cert-file", dest="external_cert_files",
+            action="append", metavar="FILE",
+            help="File containing the IPA CA certificate and the external CA "
+                 "certificate chain")
         parser.add_option_group(renew_group)
 
         install_group = OptionGroup(parser, "Install options")
@@ -90,10 +89,7 @@ class CACertManage(admintool.AdminTool):
         options = self.options
 
         if command == 'renew':
-            if options.external_cert_file and not options.external_ca_file:
-                parser.error("--external-ca-file not specified")
-            elif not options.external_cert_file and options.external_ca_file:
-                parser.error("--external-cert-file not specified")
+            pass
         elif command == 'install':
             if len(self.args) < 2:
                 parser.error("certificate file name not provided")
@@ -107,7 +103,7 @@ class CACertManage(admintool.AdminTool):
         api.bootstrap(in_server=True)
         api.finalize()
 
-        if ((command == 'renew' and options.external_cert_file) or
+        if ((command == 'renew' and options.external_cert_files) or
             command == 'install'):
             self.conn = self.ldap_connect()
         else:
@@ -166,7 +162,7 @@ class CACertManage(admintool.AdminTool):
         cert = db.get_cert_from_db(self.cert_nickname, pem=False)
 
         options = self.options
-        if options.external_cert_file:
+        if options.external_cert_files:
             return self.renew_external_step_2(ca, cert)
 
         if options.self_signed is not None:
@@ -200,31 +196,25 @@ class CACertManage(admintool.AdminTool):
               "ipa-cacert-manage as:" % paths.IPA_CA_CSR)
         print("ipa-cacert-manage renew "
               "--external-cert-file=/path/to/signed_certificate "
-              "--external-ca-file=/path/to/external_ca_certificate")
+              "--external-cert-file=/path/to/external_ca_certificate")
 
     def renew_external_step_2(self, ca, old_cert):
         print "Importing the renewed CA certificate, please wait"
 
         options = self.options
-        cert_filename = options.external_cert_file
-        ca_filename = options.external_ca_file
+        cert_file, ca_file = installutils.load_external_cert(
+            options.external_cert_files, x509.subject_base())
 
         nss_cert = None
         nss.nss_init(ca.dogtag_constants.ALIAS_DIR)
         try:
-            try:
-                installutils.validate_external_cert(
-                    cert_filename, ca_filename, x509.subject_base())
-            except ValueError, e:
-                raise admintool.ScriptError(e)
-
             nss_cert = x509.load_certificate(old_cert, x509.DER)
             subject = nss_cert.subject
             #pylint: disable=E1101
             pkinfo = nss_cert.subject_public_key_info.format()
             #pylint: enable=E1101
 
-            nss_cert = x509.load_certificate_from_file(cert_filename)
+            nss_cert = x509.load_certificate_from_file(cert_file.name)
             if not nss_cert.is_ca_cert():
                 raise admintool.ScriptError("Not a CA certificate")
             if nss_cert.subject != subject:
@@ -249,7 +239,7 @@ class CACertManage(admintool.AdminTool):
                 raise admintool.ScriptError(
                     "Not compatible with the current CA certificate: %s", e)
 
-            ca_certs = x509.load_certificate_list_from_file(ca_filename)
+            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
diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py
index 747990c..fbffdf1 100644
--- a/ipatests/test_integration/test_external_ca.py
+++ b/ipatests/test_integration/test_external_ca.py
@@ -97,8 +97,8 @@ class TestExternalCA(IntegrationTest):
             'ipa-server-install',
             '-a', self.master.config.admin_password,
             '-p', self.master.config.dirman_password,
-            '--external_cert_file', external_cert_file,
-            '--external_ca_file', external_ca_file
+            '--external-cert-file', external_cert_file,
+            '--external-cert-file', external_ca_file
         ])
 
         # Make sure IPA server is working properly
-- 
1.9.3

>From a02d256a833e58c56a926d28702fb68f06ab4f94 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 24 Sep 2014 16:41:47 +0200
Subject: [PATCH 3/5] CA-less installer options usability fixes

The --*_pkcs12 options of ipa-server-install and ipa-replica-prepare have
been replaced by --*-cert-file options which accept multiple files.
ipa-server-certinstall now accepts multiple files as well. The files are
accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and
raw private key and PKCS#12 formats.

The --root-ca-file option of ipa-server-install has been replaced by
--ca-cert-file option which accepts multiple files. The files are
accepted in PEM and DER certificate and PKCS#7 certificate chain formats.

The --*_pin options of ipa-server-install and ipa-replica-prepare have been
renamed to --*-pin.

https://fedorahosted.org/freeipa/ticket/4489
---
 install/tools/ipa-server-install            | 165 ++++++++++++++++++----------
 install/tools/man/ipa-replica-prepare.1     |  24 ++--
 install/tools/man/ipa-server-certinstall.1  |   6 +-
 install/tools/man/ipa-server-install.1      |  28 ++---
 ipaserver/install/installutils.py           | 126 ++++++++++++---------
 ipaserver/install/ipa_replica_prepare.py    | 150 ++++++++++++++-----------
 ipaserver/install/ipa_server_certinstall.py |  26 ++---
 ipatests/test_integration/test_caless.py    |  33 +++---
 8 files changed, 326 insertions(+), 232 deletions(-)

diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index e996736..331b08d 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -214,20 +214,45 @@ def parse_options():
                       help=SUPPRESS_HELP)
     cert_group.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
                       default=True, help="disables pkinit setup steps")
-    cert_group.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12",
-                      help="PKCS#12 file containing the Directory Server SSL certificate")
-    cert_group.add_option("--http_pkcs12", dest="http_pkcs12",
-                      help="PKCS#12 file containing the Apache Server SSL certificate")
-    cert_group.add_option("--pkinit_pkcs12", dest="pkinit_pkcs12",
-                      help="PKCS#12 file containing the Kerberos KDC SSL certificate")
+    cert_group.add_option("--dirsrv-cert-file", dest="dirsrv_cert_files",
+                      action="append", metavar="FILE",
+                      help="File containing the Directory Server SSL certificate and private key")
+    cert_group.add_option("--dirsrv_pkcs12", dest="dirsrv_cert_files",
+                      action="append",
+                      help=SUPPRESS_HELP)
+    cert_group.add_option("--http-cert-file", dest="http_cert_files",
+                      action="append", metavar="FILE",
+                      help="File containing the Apache Server SSL certificate and private key")
+    cert_group.add_option("--http_pkcs12", dest="http_cert_files",
+                      action="append",
+                      help=SUPPRESS_HELP)
+    cert_group.add_option("--pkinit-cert-file", dest="pkinit_cert_files",
+                      action="append", metavar="FILE",
+                      help="File containing the Kerberos KDC SSL certificate and private key")
+    cert_group.add_option("--pkinit_pkcs12", dest="pkinit_cert_files",
+                      action="append",
+                      help=SUPPRESS_HELP)
+    cert_group.add_option("--dirsrv-pin", dest="dirsrv_pin", sensitive=True,
+                      metavar="PIN",
+                      help="The password to unlock the Directory Server private key")
     cert_group.add_option("--dirsrv_pin", dest="dirsrv_pin", sensitive=True,
-                      help="The password of the Directory Server PKCS#12 file")
+                      help=SUPPRESS_HELP)
+    cert_group.add_option("--http-pin", dest="http_pin", sensitive=True,
+                      metavar="PIN",
+                      help="The password to unlock the Apache Server private key")
     cert_group.add_option("--http_pin", dest="http_pin", sensitive=True,
-                      help="The password of the Apache Server PKCS#12 file")
-    cert_group.add_option("--pkinit_pin", dest="pkinit_pin",
-                      help="The password of the Kerberos KDC PKCS#12 file")
-    cert_group.add_option("--root-ca-file", dest="root_ca_file",
-                      help="PEM file containing the CA certificate for the PKCS#12 files")
+                      help=SUPPRESS_HELP)
+    cert_group.add_option("--pkinit-pin", dest="pkinit_pin", sensitive=True,
+                      metavar="PIN",
+                      help="The password to unlock the Kerberos KDC private key")
+    cert_group.add_option("--pkinit_pin", dest="pkinit_pin", sensitive=True,
+                      help=SUPPRESS_HELP)
+    cert_group.add_option("--ca-cert-file", dest="ca_cert_files",
+                      action="append", metavar="FILE",
+                      help="File containing CA certificates for the service certificate files")
+    cert_group.add_option("--root-ca-file", dest="ca_cert_files",
+                      action="append",
+                      help=SUPPRESS_HELP)
     cert_group.add_option("--subject", action="callback", callback=subject_callback,
                       type="string",
                       help="The certificate subject base (default O=<realm-name>)")
@@ -310,22 +335,25 @@ def parse_options():
             if not options.forwarders and not options.no_forwarders:
                 parser.error("You must specify at least one --forwarder option or --no-forwarders option")
 
-    # If any of the PKCS#12 options are selected, all are required.
-    pkcs12_req = (options.dirsrv_pkcs12, options.http_pkcs12)
-    pkcs12_opt = (options.pkinit_pkcs12,)
-    if any(pkcs12_req + pkcs12_opt) and not all(pkcs12_req):
-        parser.error("--dirsrv_pkcs12 and --http_pkcs12 are required if any "
-                     "PKCS#12 options are used.")
+    # If any of the key file options are selected, all are required.
+    cert_file_req = (options.dirsrv_cert_files, options.http_cert_files)
+    cert_file_opt = (options.pkinit_cert_files,)
+    if any(cert_file_req + cert_file_opt) and not all(cert_file_req):
+        parser.error("--dirsrv-cert-file and --http-cert-file are required if "
+                     "any key file options are used.")
 
     if options.unattended:
-        if options.dirsrv_pkcs12 and options.dirsrv_pin is None:
-            parser.error("You must specify --dirsrv_pin with --dirsrv_pkcs12")
-        if options.http_pkcs12 and options.http_pin is None:
-            parser.error("You must specify --http_pin with --http_pkcs12")
-        if options.pkinit_pkcs12 and options.pkinit_pin is None:
-            parser.error("You must specify --pkinit_pin with --pkinit_pkcs12")
-
-    if options.external_cert_files and options.dirsrv_pkcs12:
+        if options.dirsrv_cert_files and options.dirsrv_pin is None:
+            parser.error(
+                "You must specify --dirsrv-pin with --dirsrv-cert-file")
+        if options.http_cert_files and options.http_pin is None:
+            parser.error(
+                "You must specify --http-pin with --http-cert-file")
+        if options.pkinit_cert_files and options.pkinit_pin is None:
+            parser.error(
+                "You must specify --pkinit-pin with --pkinit-cert-file")
+
+    if options.external_cert_files and options.dirsrv_cert_files:
         parser.error("Service certificate file options cannot be used with "
                      "the external CA options.")
 
@@ -333,8 +361,9 @@ def parse_options():
         if options.external_cert_files:
             parser.error("You cannot specify --external-cert-file "
                          "together with --external-ca")
-        if options.dirsrv_pkcs12:
-            parser.error("You cannot specify PKCS#12 options together with --external-ca")
+        if options.dirsrv_cert_files:
+            parser.error("You cannot specify service certificate file options "
+                         "together with --external-ca")
 
     if (options.external_cert_files and
         any(not os.path.isabs(path) for path in options.external_cert_files)):
@@ -751,7 +780,7 @@ def main():
             options.external_cert_files, options.subject)
 
     # We only set up the CA if the PKCS#12 options are not given.
-    if options.dirsrv_pkcs12:
+    if options.dirsrv_cert_files:
         setup_ca = False
     else:
         setup_ca = True
@@ -886,43 +915,58 @@ def main():
     if not options.subject:
         options.subject = DN(('O', realm_name))
 
-    ca_file = options.root_ca_file
-
-    if options.http_pkcs12:
+    if options.http_cert_files:
         if options.http_pin is None:
             options.http_pin = installutils.read_password(
-                "Enter %s unlock" % options.http_pkcs12,
+                "Enter Apache Server private key unlock",
                 confirm=False, validate=False)
             if options.http_pin is None:
-                sys.exit("%s unlock password required" % options.http_pkcs12)
-        http_pkcs12_info = (options.http_pkcs12, options.http_pin)
-        http_ca_cert = installutils.check_pkcs12(
-            http_pkcs12_info, ca_file, host_name)
-
-    if options.dirsrv_pkcs12:
+                sys.exit(
+                    "Apache Server private key unlock password required")
+        http_pkcs12_file, http_pin, http_ca_cert = load_pkcs12(
+            cert_files=options.http_cert_files,
+            key_password=options.http_pin,
+            key_nickname=None,
+            ca_cert_files=options.ca_cert_files,
+            host_name=host_name)
+        http_pkcs12_info = (http_pkcs12_file.name, http_pin)
+
+    if options.dirsrv_cert_files:
         if options.dirsrv_pin is None:
-            options.dirsrv_pin = installutils.read_password(
-                "Enter %s unlock" % options.dirsrv_pkcs12,
+            options.dirsrv_pin = read_password(
+                "Enter Directory Server private key unlock",
                 confirm=False, validate=False)
             if options.dirsrv_pin is None:
-                sys.exit("%s unlock password required" % options.dirsrv_pkcs12)
-        dirsrv_pkcs12_info = (options.dirsrv_pkcs12, options.dirsrv_pin)
-        dirsrv_ca_cert = installutils.check_pkcs12(
-            dirsrv_pkcs12_info, ca_file, host_name)
-
-    if options.pkinit_pkcs12:
+                sys.exit(
+                    "Directory Server private key unlock password required")
+        dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = load_pkcs12(
+            cert_files=options.dirsrv_cert_files,
+            key_password=options.dirsrv_pin,
+            key_nickname=None,
+            ca_cert_files=options.ca_cert_files,
+            host_name=host_name)
+        dirsrv_pkcs12_info = (dirsrv_pkcs12_file.name, dirsrv_pin)
+
+    if options.pkinit_cert_files:
         if options.pkinit_pin is None:
-            options.pkinit_pin = installutils.read_password(
-                "Enter %s unlock" % options.pkinit_pkcs12,
+            options.pkinit_pin = read_password(
+                "Enter Kerberos KDC private key unlock",
                 confirm=False, validate=False)
             if options.pkinit_pin is None:
-                sys.exit("%s unlock password required" % options.pkinit_pkcs12)
-        pkinit_pkcs12_info = (options.pkinit_pkcs12, options.pkinit_pin)
-
-    if (options.http_pkcs12 and options.dirsrv_pkcs12 and
+                sys.exit(
+                    "Kerberos KDC private key unlock password required")
+        pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
+            cert_files=options.pkinit_cert_files,
+            key_password=options.pkinit_pin,
+            key_nickname=None,
+            ca_cert_files=options.ca_cert_files,
+            host_name=host_name)
+        pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)
+
+    if (options.http_cert_files and options.dirsrv_cert_files and
         http_ca_cert != dirsrv_ca_cert):
-        sys.exit("%s and %s are not signed by the same CA certificate" %
-                 (options.http_pkcs12, options.dirsrv_pkcs12))
+        sys.exit("Apache Server SSL certificate and Directory Server SSL "
+                 "certificate are not signed by the same CA certificate")
 
     if not options.dm_password:
         dm_password = read_dm_password()
@@ -1046,14 +1090,13 @@ def main():
             if not ntp.is_configured():
                 ntp.create_instance()
 
-        if options.dirsrv_pkcs12:
+        if options.dirsrv_cert_files:
             ds = dsinstance.DsInstance(fstore=fstore)
             ds.create_instance(realm_name, host_name, domain_name,
                             dm_password, dirsrv_pkcs12_info,
                             idstart=options.idstart, idmax=options.idmax,
                             subject_base=options.subject,
-                            hbac_allow=not options.hbac_allow,
-                            ca_file=ca_file)
+                            hbac_allow=not options.hbac_allow)
         else:
             ds = dsinstance.DsInstance(fstore=fstore)
             ds.create_instance(realm_name, host_name, domain_name,
@@ -1119,7 +1162,7 @@ def main():
         ca.enable_client_auth_to_db()
 
     krb = krbinstance.KrbInstance(fstore)
-    if options.pkinit_pkcs12:
+    if options.pkinit_cert_files:
         krb.create_instance(realm_name, host_name, domain_name,
                             dm_password, master_password,
                             setup_pkinit=options.setup_pkinit,
@@ -1145,11 +1188,11 @@ def main():
 
     # Create a HTTP instance
     http = httpinstance.HTTPInstance(fstore)
-    if options.http_pkcs12:
+    if options.http_cert_files:
         http.create_instance(
             realm_name, host_name, domain_name, dm_password,
             pkcs12_info=http_pkcs12_info, subject_base=options.subject,
-            auto_redirect=options.ui_redirect, ca_file=ca_file)
+            auto_redirect=options.ui_redirect)
     else:
         http.create_instance(
             realm_name, host_name, domain_name, dm_password,
diff --git a/install/tools/man/ipa-replica-prepare.1 b/install/tools/man/ipa-replica-prepare.1
index 8e1e60a..fc8bf83 100644
--- a/install/tools/man/ipa-replica-prepare.1
+++ b/install/tools/man/ipa-replica-prepare.1
@@ -35,23 +35,23 @@ Once the file has been created it will be named replica\-hostname. This file can
 A replica should only be installed on the same or higher version of IPA on the remote system.
 .SH "OPTIONS"
 .TP
-\fB\-\-dirsrv_pkcs12\fR=\fIFILE\fR
-PKCS#12 file containing the Directory Server SSL Certificate and Private Key
+\fB\-\-dirsrv\-cert\-file\fR=\fIFILE\fR
+File containing the Directory Server SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times.
 .TP
-\fB\-\-http_pkcs12\fR=\fIFILE\fR
-PKCS#12 file containing the Apache Server SSL Certificate and Private Key
+\fB\-\-http\-cert\-file\fR=\fIFILE\fR
+File containing the Apache Server SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times.
 .TP
-\fB\-\-pkinit_pkcs12\fR=\fIFILE\fR
-PKCS#12 file containing the Kerberos KDC Certificate and Private Key
+\fB\-\-pkinit\-cert\-file\fR=\fIFILE\fR
+File containing the Kerberos KDC SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times.
 .TP
-\fB\-\-dirsrv_pin\fR=\fIDIRSRV_PIN\fR
-The password of the Directory Server PKCS#12 file
+\fB\-\-dirsrv\-pin\fR=\fIPIN\fR
+The password to unlock the Directory Server private key
 .TP
-\fB\-\-http_pin\fR=\fIHTTP_PIN\fR
-The password of the Apache Server PKCS#12 file
+\fB\-\-http\-pin\fR=\fIPIN\fR
+The password to unlock the Apache Server private key
 .TP
-\fB\-\-pkinit_pin\fR=\fIPKINIT_PIN\fR
-The password of the Kerberos KDC PKCS#12 file
+\fB\-\-pkinit\-pin\fR=\fIPIN\fR
+The password to unlock the Kerberos KDC private key
 .TP
 \fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR
 Directory Manager (existing master) password
diff --git a/install/tools/man/ipa-server-certinstall.1 b/install/tools/man/ipa-server-certinstall.1
index f428402..a5cb4bb 100644
--- a/install/tools/man/ipa-server-certinstall.1
+++ b/install/tools/man/ipa-server-certinstall.1
@@ -20,9 +20,9 @@
 .SH "NAME"
 ipa\-server\-certinstall \- Install new SSL server certificates
 .SH "SYNOPSIS"
-ipa\-server\-certinstall [\fIOPTION\fR]... PKCS12_FILE
+ipa\-server\-certinstall [\fIOPTION\fR]... FILE...
 .SH "DESCRIPTION"
-Replace the current SSL Directory and/or Apache server certificate(s) with the certificate in the PKCS#12 file.
+Replace the current SSL Directory and/or Apache server certificate(s) with the certificate in the specified files. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats.
 
 PKCS#12 is a file format used to safely transport SSL certificates and public/private keypairs.
 
@@ -38,7 +38,7 @@ Install the certificate on the Directory Server
 Install the certificate in the Apache Web Server
 .TP
 \fB\-\-pin\fR=\fIPIN\fR
-The password of the PKCS#12 file
+The password to unlock the private key
 .TP
 \fB\-\-dirman\-password\fR=\fIDIRMAN_PASSWORD\fR
 Directory Manager password
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index 92d9ec8..0bd5968 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -93,26 +93,26 @@ File containing the IPA CA certificate and the external CA certificate chain. Th
 \fB\-\-no\-pkinit\fR
 Disables pkinit setup steps
 .TP
-\fB\-\-dirsrv_pkcs12\fR=\fIFILE\fR
-PKCS#12 file containing the Directory Server SSL Certificate
+\fB\-\-dirsrv\-cert\-file\fR=\fIFILE\fR
+File containing the Directory Server SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times.
 .TP
-\fB\-\-http_pkcs12\fR=\fIFILE\fR
-PKCS#12 file containing the Apache Server SSL Certificate
+\fB\-\-http\-cert\-file\fR=\fIFILE\fR
+File containing the Apache Server SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times.
 .TP
-\fB\-\-pkinit_pkcs12\fR=\fIFILE\fR
-PKCS#12 file containing the Kerberos KDC SSL certificate
+\fB\-\-pkinit\-cert\-file\fR=\fIFILE\fR
+File containing the Kerberos KDC SSL certificate and private key. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. This option may be used multiple times.
 .TP
-\fB\-\-dirsrv_pin\fR=\fIDIRSRV_PIN\fR
-The password of the Directory Server PKCS#12 file
+\fB\-\-dirsrv\-pin\fR=\fIPIN\fR
+The password to unlock the Directory Server private key
 .TP
-\fB\-\-http_pin\fR=\fIHTTP_PIN\fR
-The password of the Apache Server PKCS#12 file
+\fB\-\-http\-pin\fR=\fIPIN\fR
+The password to unlock the Apache Server private key
 .TP
-\fB\-\-pkinit_pin\fR=\fIPKINIT_PIN\fR
-The password of the Kerberos KDC PKCS#12 file
+\fB\-\-pkinit\-pin\fR=\fIPIN\fR
+The password to unlock the Kerberos KDC private key
 .TP
-\fB\-\-root\-ca\-file\fR=\fIFILE\fR
-PEM file containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC SSL certificates. Use this option if the CA certificate is not present in the PKCS#12 files.
+\fB\-\-ca\-cert\-file\fR=\fIFILE\fR
+File containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC certificates. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times. Use this option if the CA certificate is not present in the certificate files.
 .TP
 \fB\-\-subject\fR=\fISUBJECT\fR
 The certificate subject base (default O=REALM.NAME)
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 7dd8ce7..1e010ed 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -736,72 +736,98 @@ def handle_error(error, log_file_name=None):
     return message, 1
 
 
-def check_pkcs12(pkcs12_info, ca_file, hostname):
-    """Check the given PKCS#12 with server cert and return the cert nickname
-
-    This is used for files given to --*_pkcs12 to ipa-server-install and
-    ipa-replica-prepare.
+def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files,
+                host_name):
+    """
+    Load and verify server certificate and private key from multiple files
+
+    The files are accepted in PEM and DER certificate, PKCS#7 certificate
+    chain, PKCS#8 and raw private key and PKCS#12 formats.
+
+    :param cert_files: Names of server certificate and private key files to
+        import
+    :param key_password: Password to decrypt private keys
+    :param key_nickname: Nickname of the private key to import from PKCS#12
+        files
+    :param ca_cert_files: Names of CA certificate files to import
+    :param host_name: Host name of the server
+    :returns: Temporary PKCS#12 file with the server certificate, private key
+        and CA certificate chain, password to unlock the PKCS#12 file and
+        the CA certificate of the CA that issued the server certificate
     """
-    pkcs12_filename, pkcs12_passwd = pkcs12_info
-    root_logger.debug('Checking PKCS#12 certificate %s', pkcs12_filename)
-    db_pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password())
     with certs.NSSDatabase() as nssdb:
-        nssdb.create_db(db_pwd_file.name)
-
-        # Import the CA cert first so it has a known nickname
-        # (if it's present in the PKCS#12 it won't be overwritten)
-        ca_cert_name = 'The Root CA'
-        if ca_file:
-            try:
-                nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
-            except (ValueError, RuntimeError) as e:
-                raise ScriptError(str(e))
+        db_password = ipautil.ipa_generate_password()
+        db_pwdfile = ipautil.write_tmp_file(db_password)
+        nssdb.create_db(db_pwdfile.name)
 
-        # Import everything in the PKCS#12
         try:
-            nssdb.import_pkcs12(
-                pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
+            nssdb.import_files(cert_files, db_pwdfile.name,
+                               True, key_password, key_nickname)
         except RuntimeError as e:
             raise ScriptError(str(e))
 
-        # Check we have exactly one server cert (one with a private key)
-        server_certs = nssdb.find_server_certs()
-        if not server_certs:
-            raise ScriptError(
-                'no server certificate found in %s' % pkcs12_filename)
-        if len(server_certs) > 1:
-            raise ScriptError(
-                '%s server certificates found in %s, expecting only one' %
-                (len(server_certs), pkcs12_filename))
-        [(server_cert_name, server_cert_trust)] = server_certs
+        if ca_cert_files:
+            try:
+                nssdb.import_files(ca_cert_files, db_pwdfile.name)
+            except RuntimeError as e:
+                raise ScriptError(str(e))
+
+        for nickname, trust_flags in nssdb.list_certs():
+            if 'u' in trust_flags:
+                key_nickname = nickname
+                continue
+            nssdb.trust_root_cert(nickname)
 
         # Check we have the whole cert chain & the CA is in it
-        trust_chain = nssdb.get_trust_chain(server_cert_name)
-        if len(trust_chain) < 2:
-            if ca_file:
-                raise ScriptError(
-                    '%s is not signed by %s, or the full certificate chain is '
-                    'not present in the PKCS#12 file' %
-                    (pkcs12_filename, ca_file))
-            else:
-                raise ScriptError(
-                    'The full certificate chain is not present in %s' %
-                    pkcs12_filename)
-        if ca_file and trust_chain[-2] != ca_cert_name:
+        trust_chain = list(reversed(nssdb.get_trust_chain(key_nickname)))
+        ca_cert = None
+        for nickname in trust_chain[1:]:
+            cert = nssdb.get_cert(nickname)
+            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
+
+            if subject == issuer:
+                break
+        else:
             raise ScriptError(
-                '%s is not signed by %s' % (pkcs12_filename, ca_file))
-        ca_cert_name = trust_chain[-2]
+                "The full certificate chain is not present in %s" %
+                (", ".join(cert_files)))
+
+        for nickname in trust_chain[1:]:
+            try:
+                nssdb.verify_ca_cert_validity(nickname)
+            except ValueError, e:
+                raise ScriptError(
+                    "CA certificate %s in %s is not valid: %s" %
+                    (subject, ", ".join(cert_files), e))
 
         # Check server validity
-        nssdb.trust_root_cert(ca_cert_name)
         try:
-            nssdb.verify_server_cert_validity(server_cert_name, hostname)
+            nssdb.verify_server_cert_validity(key_nickname, host_name)
         except ValueError as e:
             raise ScriptError(
-                'The server certificate in %s is not valid: %s' %
-                (pkcs12_filename, e))
+                "The server certificate in %s is not valid: %s" %
+                (", ".join(cert_files), e))
+
+        out_file = tempfile.NamedTemporaryFile()
+        out_password = ipautil.ipa_generate_password()
+        out_pwdfile = ipautil.write_tmp_file(out_password)
+        args = [
+            paths.PK12UTIL,
+            '-o', out_file.name,
+            '-n', key_nickname,
+            '-d', nssdb.secdir,
+            '-k', db_pwdfile.name,
+            '-w', out_pwdfile.name,
+        ]
+        ipautil.run(args)
 
-        return nssdb.get_cert(ca_cert_name)
+    return out_file, out_password, ca_cert
 
 @contextmanager
 def private_ccache(path=None):
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index dcd45fa..8811080 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -22,7 +22,7 @@ import os
 import shutil
 import tempfile
 import time
-from optparse import OptionGroup
+from optparse import OptionGroup, SUPPRESS_HELP
 from ConfigParser import SafeConfigParser
 
 import dns.resolver
@@ -75,21 +75,39 @@ class ReplicaPrepare(admintool.AdminTool):
 
         group = OptionGroup(parser, "SSL certificate options",
             "Only used if the server was installed using custom SSL certificates")
-        group.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12",
-            metavar="FILE",
-            help="install certificate for the directory server")
-        group.add_option("--http_pkcs12", dest="http_pkcs12",
-            metavar="FILE",
-            help="install certificate for the http server")
-        group.add_option("--pkinit_pkcs12", dest="pkinit_pkcs12",
-            metavar="FILE",
-            help="install certificate for the KDC")
-        group.add_option("--dirsrv_pin", dest="dirsrv_pin", metavar="PIN",
-            help="PIN for the Directory Server PKCS#12 file")
-        group.add_option("--http_pin", dest="http_pin", metavar="PIN",
-            help="PIN for the Apache Server PKCS#12 file")
-        group.add_option("--pkinit_pin", dest="pkinit_pin", metavar="PIN",
-            help="PIN for the KDC pkinit PKCS#12 file")
+        group.add_option("--dirsrv-cert-file", dest="dirsrv_cert_files",
+            action="append", metavar="FILE",
+            help="File containing the Directory Server SSL certificate and private key")
+        group.add_option("--dirsrv_pkcs12", dest="dirsrv_cert_files",
+            action="append",
+            help=SUPPRESS_HELP)
+        group.add_option("--http-cert-file", dest="http_cert_files",
+            action="append", metavar="FILE",
+            help="File containing the Apache Server SSL certificate and private key")
+        group.add_option("--http_pkcs12", dest="http_cert_files",
+            action="append",
+            help=SUPPRESS_HELP)
+        group.add_option("--pkinit-cert-file", dest="pkinit_cert_files",
+            action="append", metavar="FILE",
+            help="File containing the Kerberos KDC SSL certificate and private key")
+        group.add_option("--pkinit_pkcs12", dest="pkinit_cert_files",
+            action="append",
+            help=SUPPRESS_HELP)
+        group.add_option("--dirsrv-pin", dest="dirsrv_pin", sensitive=True,
+            metavar="PIN",
+            help="The password to unlock the Directory Server private key")
+        group.add_option("--dirsrv_pin", dest="dirsrv_pin", sensitive=True,
+            help=SUPPRESS_HELP)
+        group.add_option("--http-pin", dest="http_pin", sensitive=True,
+            metavar="PIN",
+            help="The password to unlock the Apache Server private key")
+        group.add_option("--http_pin", dest="http_pin", sensitive=True,
+            help=SUPPRESS_HELP)
+        group.add_option("--pkinit-pin", dest="pkinit_pin", sensitive=True,
+            metavar="PIN",
+            help="The password to unlock the Kerberos KDC private key")
+        group.add_option("--pkinit_pin", dest="pkinit_pin", sensitive=True,
+            help=SUPPRESS_HELP)
         parser.add_option_group(group)
 
     def validate_options(self):
@@ -112,11 +130,11 @@ class ReplicaPrepare(admintool.AdminTool):
         options.setup_pkinit = False
 
         # If any of the PKCS#12 options are selected, all are required.
-        pkcs12_req = (options.dirsrv_pkcs12, options.http_pkcs12)
-        pkcs12_opt = (options.pkinit_pkcs12,)
-        if any(pkcs12_req + pkcs12_opt) and not all(pkcs12_req):
+        cert_file_req = (options.dirsrv_cert_files, options.http_cert_files)
+        cert_file_opt = (options.pkinit_cert_files,)
+        if any(cert_file_req + cert_file_opt) and not all(cert_file_req):
             self.option_parser.error(
-                "--dirsrv_pkcs12 and --http_pkcs12 are required if any "
+                "--dirsrv-cert-file and --http-cert-file are required if any "
                 "PKCS#12 options are used.")
 
         if len(self.args) < 1:
@@ -134,11 +152,11 @@ class ReplicaPrepare(admintool.AdminTool):
         if api.env.host == self.replica_fqdn:
             raise admintool.ScriptError("You can't create a replica on itself")
 
-        if not api.env.enable_ra and not options.http_pkcs12:
+        if not api.env.enable_ra and not options.http_cert_files:
             raise admintool.ScriptError(
                 "Cannot issue certificates: a CA is not installed. Use the "
-                "--http_pkcs12, --dirsrv_pkcs12 options to provide custom "
-                "certificates.")
+                "--http-cert-file, --dirsrv-cert-file options to provide "
+                "custom certificates.")
 
         config_dir = dsinstance.config_dirname(
             dsinstance.realm_to_serverid(api.env.realm))
@@ -146,11 +164,13 @@ class ReplicaPrepare(admintool.AdminTool):
             raise admintool.ScriptError(
                 "could not find directory instance: %s" % config_dir)
 
-    def check_pkcs12(self, pkcs12_file, pkcs12_pin):
-        return installutils.check_pkcs12(
-            pkcs12_info=(pkcs12_file, pkcs12_pin),
-            ca_file=CACERT,
-            hostname=self.replica_fqdn)
+    def load_pkcs12(self, cert_files, key_password, key_nickname):
+        return installutils.load_pkcs12(
+            cert_files=cert_files,
+            key_password=key_password,
+            key_nickname=key_nickname,
+            ca_cert_files=[CACERT],
+            host_name=self.replica_fqdn)
 
     def ask_for_options(self):
         options = self.options
@@ -231,42 +251,52 @@ class ReplicaPrepare(admintool.AdminTool):
             if disconnect:
                 api.Backend.ldap2.disconnect()
 
-        if options.http_pkcs12:
+        self.http_pin = self.dirsrv_pin = self.pkinit_pin = None
+
+        if options.http_cert_files:
             if options.http_pin is None:
                 options.http_pin = installutils.read_password(
-                    "Enter %s unlock" % options.http_pkcs12,
+                    "Enter Apache Server private key unlock",
                     confirm=False, validate=False)
                 if options.http_pin is None:
                     raise admintool.ScriptError(
-                        "%s unlock password required" % options.http_pkcs12)
-            http_ca_cert = self.check_pkcs12(
-                options.http_pkcs12, options.http_pin)
+                        "Apache Server private key unlock password required")
+            http_pkcs12_file, http_pin, http_ca_cert = self.load_pkcs12(
+                options.http_cert_files, options.http_pin, None)
+            self.http_pkcs12_file = http_pkcs12_file
+            self.http_pin = http_pin
 
-        if options.dirsrv_pkcs12:
+        if options.dirsrv_cert_files:
             if options.dirsrv_pin is None:
                 options.dirsrv_pin = installutils.read_password(
-                    "Enter %s unlock" % options.dirsrv_pkcs12,
+                    "Enter Directory Server private key unlock",
                     confirm=False, validate=False)
                 if options.dirsrv_pin is None:
                     raise admintool.ScriptError(
-                        "%s unlock password required" % options.dirsrv_pkcs12)
-            dirsrv_ca_cert = self.check_pkcs12(
-                options.dirsrv_pkcs12, options.dirsrv_pin)
+                        "Directory Server private key unlock password required")
+            dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = self.load_pkcs12(
+                options.dirsrv_cert_files, options.dirsrv_pin, None)
+            self.dirsrv_pkcs12_file = dirsrv_pkcs12_file
+            self.dirsrv_pin = dirsrv_pin
 
-        if options.pkinit_pkcs12:
+        if options.pkinit_cert_files:
             if options.pkinit_pin is None:
                 options.pkinit_pin = installutils.read_password(
-                    "Enter %s unlock" % options.pkinit_pkcs12,
+                    "Enter Kerberos KDC private key unlock",
                     confirm=False, validate=False)
                 if options.pkinit_pin is None:
                     raise admintool.ScriptError(
-                        "%s unlock password required" % options.pkinit_pkcs12)
+                        "Kerberos KDC private key unlock password required")
+            pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = self.load_pkcs12(
+                options.pkinit_cert_files, options.pkinit_pin, None)
+            self.pkinit_pkcs12_file = pkinit_pkcs12_file
+            self.pkinit_pin = pkinit_pin
 
-        if (options.http_pkcs12 and options.dirsrv_pkcs12 and
+        if (options.http_cert_files and options.dirsrv_cert_files and
             http_ca_cert != dirsrv_ca_cert):
             raise admintool.ScriptError(
-                "%s and %s are not signed by the same CA certificate" %
-                (options.http_pkcs12, options.dirsrv_pkcs12))
+                "Apache Server SSL certificate and Directory Server SSL "
+                 "certificate are not signed by the same CA certificate")
 
         if (not ipautil.file_exists(
                     dogtag.configured_constants().CS_CFG_PATH) and
@@ -316,13 +346,11 @@ class ReplicaPrepare(admintool.AdminTool):
 
         passwd_fname = os.path.join(self.dir, "dirsrv_pin.txt")
         with open(passwd_fname, "w") as fd:
-            fd.write("%s\n" % (options.dirsrv_pin or ''))
+            fd.write("%s\n" % (self.dirsrv_pin or ''))
 
-        if options.dirsrv_pkcs12:
-            self.log.info(
-                "Copying SSL certificate for the Directory Server from %s",
-                options.dirsrv_pkcs12)
-            self.copy_info_file(options.dirsrv_pkcs12, "dscert.p12")
+        if options.dirsrv_cert_files:
+            self.log.info("Copying SSL certificate for the Directory Server")
+            self.copy_info_file(self.dirsrv_pkcs12_file.name, "dscert.p12")
         else:
             if ipautil.file_exists(options.ca_file):
                 # Since it is possible that the Directory Manager password
@@ -339,7 +367,7 @@ class ReplicaPrepare(admintool.AdminTool):
                 "Creating SSL certificate for the Directory Server")
             self.export_certdb("dscert", passwd_fname)
 
-        if not options.dirsrv_pkcs12:
+        if not options.dirsrv_cert_files:
             self.log.info(
                 "Creating SSL certificate for the dogtag Directory Server")
             self.export_certdb("dogtagcert", passwd_fname)
@@ -354,13 +382,11 @@ class ReplicaPrepare(admintool.AdminTool):
 
         passwd_fname = os.path.join(self.dir, "http_pin.txt")
         with open(passwd_fname, "w") as fd:
-            fd.write("%s\n" % (options.http_pin or ''))
+            fd.write("%s\n" % (self.http_pin or ''))
 
-        if options.http_pkcs12:
-            self.log.info(
-                "Copying SSL certificate for the Web Server from %s",
-                options.http_pkcs12)
-            self.copy_info_file(options.http_pkcs12, "httpcert.p12")
+        if options.http_cert_files:
+            self.log.info("Copying SSL certificate for the Web Server")
+            self.copy_info_file(self.http_pkcs12_file.name, "httpcert.p12")
         else:
             self.log.info("Creating SSL certificate for the Web Server")
             self.export_certdb("httpcert", passwd_fname)
@@ -373,13 +399,11 @@ class ReplicaPrepare(admintool.AdminTool):
 
         passwd_fname = os.path.join(self.dir, "pkinit_pin.txt")
         with open(passwd_fname, "w") as fd:
-            fd.write("%s\n" % (options.pkinit_pin or ''))
+            fd.write("%s\n" % (self.pkinit_pin or ''))
 
-        if options.pkinit_pkcs12:
-            self.log.info(
-                "Copying SSL certificate for the KDC from %s",
-                options.pkinit_pkcs12)
-            self.copy_info_file(options.pkinit_pkcs12, "pkinitcert.p12")
+        if options.pkinit_cert_files:
+            self.log.info("Copying SSL certificate for the KDC")
+            self.copy_info_file(self.pkinit_pkcs12_file.name, "pkinitcert.p12")
         else:
             self.log.info("Creating SSL certificate for the KDC")
             self.export_certdb("pkinitcert", passwd_fname, is_kdc=True)
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index 6300a14..1744a6e 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -36,7 +36,7 @@ from ipaserver.plugins.ldap2 import ldap2
 class ServerCertInstall(admintool.AdminTool):
     command_name = 'ipa-server-certinstall'
 
-    usage = "%prog <-d|-w> [options] <PKCS#12 file>"
+    usage = "%prog <-d|-w> [options] <file> ..."
 
     description = "Install new SSL server certificates."
 
@@ -54,7 +54,7 @@ class ServerCertInstall(admintool.AdminTool):
             help="install certificate for the http server")
         parser.add_option(
             "--pin",
-            dest="pin",
+            dest="pin", metavar="PIN", sensitive=True,
             help="The password of the PKCS#12 file")
         parser.add_option(
             "--dirsrv_pin", "--http_pin",
@@ -73,8 +73,8 @@ class ServerCertInstall(admintool.AdminTool):
         if not self.options.dirsrv and not self.options.http:
             self.option_parser.error("you must specify dirsrv and/or http")
 
-        if len(self.args) != 1:
-            self.option_parser.error("you must provide a pkcs12 filename")
+        if not self.args:
+            self.option_parser.error("you must provide certificate filename")
 
     def ask_for_options(self):
         super(ServerCertInstall, self).ask_for_options()
@@ -88,17 +88,15 @@ class ServerCertInstall(admintool.AdminTool):
 
         if self.options.pin is None:
             self.options.pin = installutils.read_password(
-                "Enter %s unlock" % self.args[0], confirm=False, validate=False)
+                "Enter private key unlock", confirm=False, validate=False)
             if self.options.pin is None:
                 raise admintool.ScriptError(
-                    "%s unlock password required" % self.args[0])
+                    "Private key unlock password required")
 
     def run(self):
         api.bootstrap(in_server=True)
         api.finalize()
 
-        self.pkcs12_fname = self.args[0]
-
         if self.options.dirsrv:
             self.install_dirsrv_cert()
 
@@ -154,10 +152,12 @@ class ServerCertInstall(admintool.AdminTool):
         os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
 
     def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
-        installutils.check_pkcs12(
-            pkcs12_info=(self.pkcs12_fname, pkcs12_passwd),
-            ca_file=CACERT,
-            hostname=api.env.host)
+        pkcs12_file, pin, ca_cert = installutils.load_pkcs12(
+            cert_files=self.args,
+            key_password=pkcs12_passwd,
+            key_nickname=None,
+            ca_cert_files=[CACERT],
+            host_name=api.env.host)
 
         cdb = certs.CertDB(api.env.realm, nssdir=dirname)
         try:
@@ -165,7 +165,7 @@ class ServerCertInstall(admintool.AdminTool):
                 cdb.untrack_server_cert(old_cert)
 
             cdb.delete_cert(old_cert)
-            cdb.import_pkcs12(self.pkcs12_fname, pkcs12_passwd)
+            cdb.import_pkcs12(pkcs12_file.name, pin)
             server_cert = cdb.find_server_certs()[0][0]
 
             if api.env.enable_ra:
diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py
index 28bfae5..1bd8202 100644
--- a/ipatests/test_integration/test_caless.py
+++ b/ipatests/test_integration/test_caless.py
@@ -154,9 +154,9 @@ class CALessBase(IntegrationTest):
 
         args = [
             'ipa-server-install',
-            '--http_pkcs12', http_pkcs12,
-            '--dirsrv_pkcs12', dirsrv_pkcs12,
-            '--root-ca-file', root_ca_file,
+            '--http-cert-file', http_pkcs12,
+            '--dirsrv-cert-file', dirsrv_pkcs12,
+            '--ca-cert-file', root_ca_file,
             '--ip-address', host.ip,
             '-r', host.domain.name,
             '-p', host.config.dirman_password,
@@ -166,9 +166,9 @@ class CALessBase(IntegrationTest):
         ]
 
         if http_pin is not None:
-            args.extend(['--http_pin', http_pin])
+            args.extend(['--http-pin', http_pin])
         if dirsrv_pin is not None:
-            args.extend(['--dirsrv_pin', dirsrv_pin])
+            args.extend(['--dirsrv-pin', dirsrv_pin])
         if unattended:
             args.extend(['-U'])
 
@@ -230,13 +230,13 @@ class CALessBase(IntegrationTest):
         ]
 
         if http_pkcs12:
-            args.extend(['--http_pkcs12', http_pkcs12])
+            args.extend(['--http-cert-file', http_pkcs12])
         if dirsrv_pkcs12:
-            args.extend(['--dirsrv_pkcs12', dirsrv_pkcs12])
+            args.extend(['--dirsrv-cert-file', dirsrv_pkcs12])
         if http_pin is not None:
-            args.extend(['--http_pin', http_pin])
+            args.extend(['--http-pin', http_pin])
         if dirsrv_pin is not None:
-            args.extend(['--dirsrv_pin', dirsrv_pin])
+            args.extend(['--dirsrv-pin', dirsrv_pin])
 
         args.extend([replica.hostname])
 
@@ -428,8 +428,8 @@ class TestServerInstall(CALessBase):
 
         result = self.install_server(http_pin=None)
         assert_error(result,
-                     'ipa-server-install: error: You must specify --http_pin '
-                     'with --http_pkcs12')
+                     'ipa-server-install: error: You must specify --http-pin '
+                     'with --http-cert-file')
 
     def test_missing_ds_password(self):
         "IPA server install with missing DS PKCS#12 password (unattended)"
@@ -441,7 +441,7 @@ class TestServerInstall(CALessBase):
         result = self.install_server(dirsrv_pin=None)
         assert_error(result,
                      'ipa-server-install: error: You must specify '
-                     '--dirsrv_pin with --dirsrv_pkcs12')
+                     '--dirsrv-pin with --dirsrv-cert-file')
 
     def test_incorect_http_pin(self):
         "IPA server install with incorrect HTTP PKCS#12 password"
@@ -784,8 +784,9 @@ class TestReplicaInstall(CALessBase):
                                          raiseonerr=False)
         assert result.returncode > 0
         assert ('Cannot issue certificates: a CA is not installed. Use the '
-                '--http_pkcs12, --dirsrv_pkcs12 options to provide custom '
-                'certificates.' in result.stderr_text), result.stderr_text
+                '--http-cert-file, --dirsrv-cert-file options to provide '
+                'custom certificates.' in result.stderr_text), \
+               result.stderr_text
 
     def test_nonexistent_http_pkcs12_file(self):
         "IPA replica install with non-existent HTTP PKCS#12 file"
@@ -1479,7 +1480,7 @@ class TestCertinstall(CALessBase):
 
         args = ['ipa-server-certinstall',
                 '-w', 'server.p12',
-                '--http_pin', self.cert_password]
+                '--http-pin', self.cert_password]
 
         result = self.certinstall('w', 'ca1/server', args=args)
         assert result.returncode == 0
@@ -1490,7 +1491,7 @@ class TestCertinstall(CALessBase):
 
         args = ['ipa-server-certinstall',
                 '-d', 'server.p12',
-                '--dirsrv_pin', self.cert_password]
+                '--dirsrv-pin', self.cert_password]
         stdin_text = self.master.config.dirman_password + '\n'
 
         result = self.certinstall('d', 'ca1/server',
-- 
1.9.3

>From 47ddddd399845295676c1a071ed820b135076e96 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 24 Sep 2014 16:48:15 +0200
Subject: [PATCH 4/5] Allow choosing CA-less server certificates by name

Added new --*-cert-name options to ipa-server-install and ipa-replica-prepare
and --cert-name option to ipa-server-certinstall. The options allows choosing
a particular certificate and private key from PKCS#12 files by its friendly
name.

https://fedorahosted.org/freeipa/ticket/4489
---
 install/tools/ipa-server-install            | 15 ++++++++++++---
 install/tools/man/ipa-replica-prepare.1     |  9 +++++++++
 install/tools/man/ipa-server-certinstall.1  |  3 +++
 install/tools/man/ipa-server-install.1      |  9 +++++++++
 ipaserver/install/ipa_replica_prepare.py    | 18 +++++++++++++++---
 ipaserver/install/ipa_server_certinstall.py |  6 +++++-
 6 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 331b08d..b827dfe 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -247,6 +247,15 @@ def parse_options():
                       help="The password to unlock the Kerberos KDC private key")
     cert_group.add_option("--pkinit_pin", dest="pkinit_pin", sensitive=True,
                       help=SUPPRESS_HELP)
+    cert_group.add_option("--dirsrv-cert-name", dest="dirsrv_cert_name",
+                      metavar="NAME",
+                      help="Name of the Directory Server SSL certificate to install")
+    cert_group.add_option("--http-cert-name", dest="http_cert_name",
+                      metavar="NAME",
+                      help="Name of the Apache Server SSL certificate to install")
+    cert_group.add_option("--pkinit-cert-name", dest="pkinit_cert_name",
+                      metavar="NAME",
+                      help="Name of the Kerberos KDC SSL certificate to install")
     cert_group.add_option("--ca-cert-file", dest="ca_cert_files",
                       action="append", metavar="FILE",
                       help="File containing CA certificates for the service certificate files")
@@ -926,7 +935,7 @@ def main():
         http_pkcs12_file, http_pin, http_ca_cert = load_pkcs12(
             cert_files=options.http_cert_files,
             key_password=options.http_pin,
-            key_nickname=None,
+            key_nickname=options.http_cert_name,
             ca_cert_files=options.ca_cert_files,
             host_name=host_name)
         http_pkcs12_info = (http_pkcs12_file.name, http_pin)
@@ -942,7 +951,7 @@ def main():
         dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = load_pkcs12(
             cert_files=options.dirsrv_cert_files,
             key_password=options.dirsrv_pin,
-            key_nickname=None,
+            key_nickname=options.dirsrv_cert_name,
             ca_cert_files=options.ca_cert_files,
             host_name=host_name)
         dirsrv_pkcs12_info = (dirsrv_pkcs12_file.name, dirsrv_pin)
@@ -958,7 +967,7 @@ def main():
         pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
             cert_files=options.pkinit_cert_files,
             key_password=options.pkinit_pin,
-            key_nickname=None,
+            key_nickname=options.pkinit_cert_name,
             ca_cert_files=options.ca_cert_files,
             host_name=host_name)
         pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)
diff --git a/install/tools/man/ipa-replica-prepare.1 b/install/tools/man/ipa-replica-prepare.1
index fc8bf83..a0d47c9 100644
--- a/install/tools/man/ipa-replica-prepare.1
+++ b/install/tools/man/ipa-replica-prepare.1
@@ -53,6 +53,15 @@ The password to unlock the Apache Server private key
 \fB\-\-pkinit\-pin\fR=\fIPIN\fR
 The password to unlock the Kerberos KDC private key
 .TP
+\fB\-\-dirsrv\-cert\-name\fR=\fINAME\fR
+Name of the Directory Server SSL certificate to install
+.TP
+\fB\-\-http\-cert\-name\fR=\fINAME\fR
+Name of the Apache Server SSL certificate to install
+.TP
+\fB\-\-pkinit\-cert\-name\fR=\fINAME\fR
+Name of the Kerberos KDC SSL certificate to install
+.TP
 \fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR
 Directory Manager (existing master) password
 .TP
diff --git a/install/tools/man/ipa-server-certinstall.1 b/install/tools/man/ipa-server-certinstall.1
index a5cb4bb..d23bbd4 100644
--- a/install/tools/man/ipa-server-certinstall.1
+++ b/install/tools/man/ipa-server-certinstall.1
@@ -40,6 +40,9 @@ Install the certificate in the Apache Web Server
 \fB\-\-pin\fR=\fIPIN\fR
 The password to unlock the private key
 .TP
+\fB\-\-cert\-name\fR=\fINAME\fR
+Name of the certificate to install
+.TP
 \fB\-\-dirman\-password\fR=\fIDIRMAN_PASSWORD\fR
 Directory Manager password
 .SH "EXIT STATUS"
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index 0bd5968..582108e 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -111,6 +111,15 @@ The password to unlock the Apache Server private key
 \fB\-\-pkinit\-pin\fR=\fIPIN\fR
 The password to unlock the Kerberos KDC private key
 .TP
+\fB\-\-dirsrv\-cert\-name\fR=\fINAME\fR
+Name of the Directory Server SSL certificate to install
+.TP
+\fB\-\-http\-cert\-name\fR=\fINAME\fR
+Name of the Apache Server SSL certificate to install
+.TP
+\fB\-\-pkinit\-cert\-name\fR=\fINAME\fR
+Name of the Kerberos KDC SSL certificate to install
+.TP
 \fB\-\-ca\-cert\-file\fR=\fIFILE\fR
 File containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC certificates. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times. Use this option if the CA certificate is not present in the certificate files.
 .TP
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index 8811080..cbd8df2 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -108,6 +108,15 @@ class ReplicaPrepare(admintool.AdminTool):
             help="The password to unlock the Kerberos KDC private key")
         group.add_option("--pkinit_pin", dest="pkinit_pin", sensitive=True,
             help=SUPPRESS_HELP)
+        group.add_option("--dirsrv-cert-name", dest="dirsrv_cert_name",
+            metavar="NAME",
+            help="Name of the Directory Server SSL certificate to install")
+        group.add_option("--http-cert-name", dest="http_cert_name",
+            metavar="NAME",
+            help="Name of the Apache Server SSL certificate to install")
+        group.add_option("--pkinit-cert-name", dest="pkinit_cert_name",
+            metavar="NAME",
+            help="Name of the Kerberos KDC SSL certificate to install")
         parser.add_option_group(group)
 
     def validate_options(self):
@@ -262,7 +271,8 @@ class ReplicaPrepare(admintool.AdminTool):
                     raise admintool.ScriptError(
                         "Apache Server private key unlock password required")
             http_pkcs12_file, http_pin, http_ca_cert = self.load_pkcs12(
-                options.http_cert_files, options.http_pin, None)
+                options.http_cert_files, options.http_pin,
+                options.http_cert_name)
             self.http_pkcs12_file = http_pkcs12_file
             self.http_pin = http_pin
 
@@ -275,7 +285,8 @@ class ReplicaPrepare(admintool.AdminTool):
                     raise admintool.ScriptError(
                         "Directory Server private key unlock password required")
             dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = self.load_pkcs12(
-                options.dirsrv_cert_files, options.dirsrv_pin, None)
+                options.dirsrv_cert_files, options.dirsrv_pin,
+                options.dirsrv_cert_name)
             self.dirsrv_pkcs12_file = dirsrv_pkcs12_file
             self.dirsrv_pin = dirsrv_pin
 
@@ -288,7 +299,8 @@ class ReplicaPrepare(admintool.AdminTool):
                     raise admintool.ScriptError(
                         "Kerberos KDC private key unlock password required")
             pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = self.load_pkcs12(
-                options.pkinit_cert_files, options.pkinit_pin, None)
+                options.pkinit_cert_files, options.pkinit_pin,
+                options.pkinit_cert_name)
             self.pkinit_pkcs12_file = pkinit_pkcs12_file
             self.pkinit_pin = pkinit_pin
 
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index 1744a6e..9165ac1 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -61,6 +61,10 @@ class ServerCertInstall(admintool.AdminTool):
             dest="pin",
             help=optparse.SUPPRESS_HELP)
         parser.add_option(
+            "--cert-name",
+            dest="cert_name", metavar="NAME",
+            help="Name of the certificate to install")
+        parser.add_option(
             "-p", "--dirman-password",
             dest="dirman_password",
             help="Directory Manager password")
@@ -155,7 +159,7 @@ class ServerCertInstall(admintool.AdminTool):
         pkcs12_file, pin, ca_cert = installutils.load_pkcs12(
             cert_files=self.args,
             key_password=pkcs12_passwd,
-            key_nickname=None,
+            key_nickname=self.options.cert_name,
             ca_cert_files=[CACERT],
             host_name=api.env.host)
 
-- 
1.9.3

>From 56b2d6340421dc7e57534cef5765b788c9d8739a Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Mon, 29 Sep 2014 16:22:30 +0200
Subject: [PATCH 5/5] Do stricter validation of CA certificates

Every CA certificate must have non-empty subject and basic constraints
extension with the CA flag set.

https://fedorahosted.org/freeipa/ticket/4477
---
 ipaserver/install/certs.py             |  4 ++++
 ipaserver/install/ipa_cacert_manage.py | 15 +++++++++++----
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 82d8290..55feb65 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -490,6 +490,10 @@ class NSSDatabase(object):
         try:
             certdb = nss.get_default_certdb()
             cert = nss.find_cert_from_nickname(nickname)
+            if not cert.subject:
+                raise ValueError("has empty subject")
+            if not cert.is_ca_cert():
+                raise ValueError("not a CA certificate")
             intended_usage = nss.certificateUsageSSLCA
             try:
                 approved_usage = cert.verify_now(certdb, True, intended_usage)
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index 6a7fd05..1acc623 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -215,8 +215,6 @@ class CACertManage(admintool.AdminTool):
             #pylint: enable=E1101
 
             nss_cert = x509.load_certificate_from_file(cert_file.name)
-            if not nss_cert.is_ca_cert():
-                raise admintool.ScriptError("Not a CA certificate")
             if nss_cert.subject != subject:
                 raise admintool.ScriptError("Subject name mismatch")
             #pylint: disable=E1101
@@ -319,8 +317,6 @@ class CACertManage(admintool.AdminTool):
                     "Can't open \"%s\": %s" % (cert_filename, e))
             except (TypeError, NSPRError), e:
                 raise admintool.ScriptError("Not a valid certificate: %s" % e)
-            if not nss_cert.is_ca_cert():
-                raise admintool.ScriptError("Not a CA certificate")
             subject = nss_cert.subject
             cert = nss_cert.der_data
         finally:
@@ -328,6 +324,17 @@ class CACertManage(admintool.AdminTool):
 
         nickname = options.nickname or str(subject)
 
+        with certs.NSSDatabase() as tmpdb:
+            pw = ipautil.write_tmp_file(ipautil.ipa_generate_password())
+            tmpdb.create_db(pw.name)
+            tmpdb.add_cert(cert, nickname, 'C,,')
+
+            try:
+                tmpdb.verify_ca_cert_validity(nickname)
+            except ValueError, e:
+                raise admintool.ScriptError(
+                    "Not a valid CA certificate: %s" % e)
+
         trust_flags = options.trust_flags
         if ((set(trust_flags) - set(',CPTcgpuw')) or
             len(trust_flags.split(',')) != 3):
-- 
1.9.3

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to