From 25d302f2a4799946636edcfe97a428303021d689 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 19 Feb 2016 15:09:49 +0100 Subject: [PATCH] Added pki-server commands to export system certificates. Some pki-server commands have been added to simplify exporting the required certificates for subsystem installations. These commands will invoke the pki pkcs12 utility to export the certificates from the instance NSS database. The pki-server ca-cert-chain-export command will export the the certificate chain needed for installing additional subsystems running on a separate instance. The pki-server -clone-prepare commands will export the certificates required for cloning a subsystem. https://fedorahosted.org/pki/ticket/1742 --- base/common/python/pki/nssdb.py | 22 ++- base/server/python/pki/server/__init__.py | 121 ++++++++++++++- base/server/python/pki/server/cli/ca.py | 202 ++++++++++++++++++++++++- base/server/python/pki/server/cli/instance.py | 94 ++++++++++++ base/server/python/pki/server/cli/kra.py | 139 +++++++++++++++++ base/server/python/pki/server/cli/ocsp.py | 138 +++++++++++++++++ base/server/python/pki/server/cli/subsystem.py | 74 +++++---- base/server/python/pki/server/cli/tks.py | 138 +++++++++++++++++ base/server/python/pki/server/cli/tps.py | 138 +++++++++++++++++ base/server/sbin/pki-server | 9 ++ 10 files changed, 1036 insertions(+), 39 deletions(-) create mode 100644 base/server/python/pki/server/cli/kra.py create mode 100644 base/server/python/pki/server/cli/ocsp.py create mode 100644 base/server/python/pki/server/cli/tks.py create mode 100644 base/server/python/pki/server/cli/tps.py diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py index c6beab317fd0f3ea67dc02c356528ae50f8d6e1a..a418cdc00aaadec6f7635dd2d2eefdf8fb8d7888 100644 --- a/base/common/python/pki/nssdb.py +++ b/base/common/python/pki/nssdb.py @@ -484,7 +484,7 @@ class NSSDatabase(object): finally: shutil.rmtree(tmpdir) - def export_pkcs12(self, pkcs12_file, nickname, pkcs12_password=None, + def export_pkcs12(self, pkcs12_file, nicknames=None, pkcs12_password=None, pkcs12_password_file=None): tmpdir = tempfile.mkdtemp() @@ -502,14 +502,24 @@ class NSSDatabase(object): raise Exception('Missing PKCS #12 password') cmd = [ - 'pk12util', + 'pki', '-d', self.directory, - '-k', self.password_file, - '-o', pkcs12_file, - '-w', password_file, - '-n', nickname + '-C', self.password_file ] + if self.token and self.token != 'internal': + cmd.extend(['--token', self.token]) + + cmd.extend(['pkcs12-export']) + + cmd.extend([ + '--pkcs12', pkcs12_file, + '--pkcs12-password-file', password_file + ]) + + if nicknames: + cmd.extend(nicknames) + subprocess.check_call(cmd) finally: diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index cb246aaa1a162c1c8971ef50bb8312a5b2e2737b..04635f85641b91b6093482884924e39cdc7ecf9b 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -64,10 +64,9 @@ class PKISubsystem(object): def __init__(self, instance, subsystem_name): self.instance = instance - self.name = subsystem_name - self.type = instance.type + self.name = subsystem_name # e.g. ca, kra - if self.type >= 10: + if instance.type >= 10: self.base_dir = os.path.join(self.instance.base_dir, self.name) else: self.base_dir = instance.base_dir @@ -87,8 +86,8 @@ class PKISubsystem(object): instance.conf_dir, 'Catalina', 'localhost', self.name + '.xml') self.config = {} - self.type = None - self.prefix = None + self.type = None # e.g. CA, KRA + self.prefix = None # e.g. ca, kra # custom subsystem location self.doc_base = os.path.join(self.base_dir, 'webapps', self.name) @@ -107,7 +106,7 @@ class PKISubsystem(object): self.type = self.config['cs.type'] self.prefix = self.type.lower() - def find_subsystem_certs(self): + def find_system_certs(self): certs = [] cert_ids = self.config['%s.cert.list' % self.name].split(',') @@ -144,6 +143,116 @@ class PKISubsystem(object): self.config['%s.%s.certreq' % (self.name, cert_id)] = ( cert.get('request', None)) + def export_system_cert( + self, + cert_id, + pkcs12_file, + pkcs12_password_file, + new_file=False): + + cert = self.get_subsystem_cert(cert_id) + nickname = cert['nickname'] + token = cert['token'] + if token == 'Internal Key Storage Token': + token = 'internal' + nssdb_password = self.instance.get_password(token) + + tmpdir = tempfile.mkdtemp() + + try: + nssdb_password_file = os.path.join(tmpdir, 'password.txt') + with open(nssdb_password_file, 'w') as f: + f.write(nssdb_password) + + # add the certificate, key, and chain + cmd = [ + 'pki', + '-d', self.instance.nssdb_dir, + '-C', nssdb_password_file + ] + + if token and token != 'internal': + cmd.extend(['--token', token]) + + cmd.extend([ + 'pkcs12-cert-add', + '--pkcs12', pkcs12_file, + '--pkcs12-password-file', pkcs12_password_file, + ]) + + if new_file: + cmd.extend(['--new-file']) + + cmd.extend([ + nickname + ]) + + subprocess.check_call(cmd) + + finally: + shutil.rmtree(tmpdir) + + def export_cert_chain( + self, + pkcs12_file, + pkcs12_password_file): + + # use subsystem certificate to get certificate chain + cert = self.get_subsystem_cert('subsystem') + nickname = cert['nickname'] + token = cert['token'] + if token == 'Internal Key Storage Token': + token = 'internal' + nssdb_password = self.instance.get_password(token) + + tmpdir = tempfile.mkdtemp() + + try: + nssdb_password_file = os.path.join(tmpdir, 'password.txt') + with open(nssdb_password_file, 'w') as f: + f.write(nssdb_password) + + # export the certificate, key, and chain + cmd = [ + 'pki', + '-d', self.instance.nssdb_dir, + '-C', nssdb_password_file + ] + + if token and token != 'internal': + cmd.extend(['--token', token]) + + cmd.extend([ + 'pkcs12-export', + '--pkcs12', pkcs12_file, + '--pkcs12-password-file', pkcs12_password_file, + nickname + ]) + + subprocess.check_call(cmd) + + # remove the certificate and key, but keep the chain + cmd = [ + 'pki', + '-d', self.instance.nssdb_dir, + '-C', nssdb_password_file + ] + + if token and token != 'internal': + cmd.extend(['--token', token]) + + cmd.extend([ + 'pkcs12-cert-del', + '--pkcs12', pkcs12_file, + '--pkcs12-password-file', pkcs12_password_file, + nickname + ]) + + subprocess.check_call(cmd) + + finally: + shutil.rmtree(tmpdir) + def save(self): sorted_config = sorted(self.config.items(), key=operator.itemgetter(0)) with io.open(self.cs_conf, 'wb') as f: diff --git a/base/server/python/pki/server/cli/ca.py b/base/server/python/pki/server/cli/ca.py index e35e741c55102e03276b81868a4997d1d05f4f7a..a47a293cffc3313f76e28c63934ea7ef526a6c25 100644 --- a/base/server/python/pki/server/cli/ca.py +++ b/base/server/python/pki/server/cli/ca.py @@ -22,10 +22,12 @@ from __future__ import absolute_import from __future__ import print_function import getopt import io +import os +import shutil import sys +import tempfile import pki.cli -import pki.server.ca class CACLI(pki.cli.CLI): @@ -35,6 +37,7 @@ class CACLI(pki.cli.CLI): 'ca', 'CA management commands') self.add_module(CACertCLI()) + self.add_module(CACloneCLI()) class CACertCLI(pki.cli.CLI): @@ -43,9 +46,106 @@ class CACertCLI(pki.cli.CLI): super(CACertCLI, self).__init__( 'cert', 'CA certificates management commands') + self.add_module(CACertChainCLI()) self.add_module(CACertRequestCLI()) +class CACertChainCLI(pki.cli.CLI): + + def __init__(self): + super(CACertChainCLI, self).__init__( + 'chain', 'CA certificate chain management commands') + + self.add_module(CACertChainExportCLI()) + + +class CACertChainExportCLI(pki.cli.CLI): + + def __init__(self): + super(CACertChainExportCLI, self).__init__( + 'export', 'Export certificate chain') + + def print_help(self): + print('Usage: pki-server ca-cert-chain-export [OPTIONS]') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --pkcs12-file PKCS #12 file to store certificates and keys.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file File containing the PKCS #12 password.') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, args): + + try: + opts, _ = getopt.gnu_getopt(args, 'i:v', [ + 'instance=', 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + pkcs12_file = None + pkcs12_password = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--pkcs12-file': + pkcs12_file = a + + elif o == '--pkcs12-password': + pkcs12_password = a + + elif o == '--pkcs12-password-file': + with io.open(a, 'rb') as f: + pkcs12_password = f.read() + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not pkcs12_file: + print('ERROR: Missing PKCS #12 file') + self.print_help() + sys.exit(1) + + if not pkcs12_password: + print('ERROR: Missing PKCS #12 password') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + subsystem = instance.get_subsystem('ca') + + tmpdir = tempfile.mkdtemp() + + try: + pkcs12_password_file = os.path.join(tmpdir, 'pkcs12_password.txt') + with open(pkcs12_password_file, 'w') as f: + f.write(pkcs12_password) + + subsystem.export_cert_chain(pkcs12_file, pkcs12_password_file) + + finally: + shutil.rmtree(tmpdir) + + class CACertRequestCLI(pki.cli.CLI): def __init__(self): @@ -203,3 +303,103 @@ class CACertRequestShowCLI(pki.cli.CLI): else: CACertRequestCLI.print_request(request, details=True) + + +class CACloneCLI(pki.cli.CLI): + + def __init__(self): + super(CACloneCLI, self).__init__( + 'clone', 'CA clone management commands') + + self.add_module(CAClonePrepareCLI()) + + +class CAClonePrepareCLI(pki.cli.CLI): + + def __init__(self): + super(CAClonePrepareCLI, self).__init__( + 'prepare', 'Prepare CA clone') + + def print_help(self): + print('Usage: pki-server ca-clone-prepare [OPTIONS]') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --pkcs12-file PKCS #12 file to store certificates and keys.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file File containing the PKCS #12 password.') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, args): + + try: + opts, _ = getopt.gnu_getopt(args, 'i:v', [ + 'instance=', 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + pkcs12_file = None + pkcs12_password = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--pkcs12-file': + pkcs12_file = a + + elif o == '--pkcs12-password': + pkcs12_password = a + + elif o == '--pkcs12-password-file': + with io.open(a, 'rb') as f: + pkcs12_password = f.read() + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not pkcs12_file: + print('ERROR: Missing PKCS #12 file') + self.print_help() + sys.exit(1) + + if not pkcs12_password: + print('ERROR: Missing PKCS #12 password') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + subsystem = instance.get_subsystem('ca') + + tmpdir = tempfile.mkdtemp() + + try: + pkcs12_password_file = os.path.join(tmpdir, 'pkcs12_password.txt') + with open(pkcs12_password_file, 'w') as f: + f.write(pkcs12_password) + + subsystem.export_system_cert( + 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) + subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert('ocsp_signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + + finally: + shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/instance.py b/base/server/python/pki/server/cli/instance.py index 5e70e5f28c566ed7db7f7b35fa08327661bacdbe..b5e6a5e4105b455337d33eefc61c631d57f3c6bd 100644 --- a/base/server/python/pki/server/cli/instance.py +++ b/base/server/python/pki/server/cli/instance.py @@ -21,6 +21,7 @@ from __future__ import absolute_import from __future__ import print_function import getopt +import getpass import os import sys @@ -35,6 +36,7 @@ class InstanceCLI(pki.cli.CLI): super(InstanceCLI, self).__init__('instance', 'Instance management commands') + self.add_module(InstanceCertCLI()) self.add_module(InstanceFindCLI()) self.add_module(InstanceShowCLI()) self.add_module(InstanceStartCLI()) @@ -49,6 +51,98 @@ class InstanceCLI(pki.cli.CLI): print(' Active: %s' % instance.is_active()) +class InstanceCertCLI(pki.cli.CLI): + + def __init__(self): + super(InstanceCertCLI, self).__init__( + 'cert', 'Instance certificate management commands') + + self.add_module(InstanceCertExportCLI()) + + +class InstanceCertExportCLI(pki.cli.CLI): + + def __init__(self): + super(InstanceCertExportCLI, self).__init__( + 'export', 'Export subsystem certificate') + + def print_help(self): # flake8: noqa + print('Usage: pki-server instance-cert-export [OPTIONS]') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --pkcs12-file Output file to store the exported certificate and key in PKCS #12 format.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file Input file containing the password for the PKCS #12 file.') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, argv): + + try: + opts, _ = getopt.gnu_getopt(argv, 'i:v', [ + 'instance=', + 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + pkcs12_file = None + pkcs12_password = None + pkcs12_password_file = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--pkcs12-file': + pkcs12_file = a + + elif o == '--pkcs12-password': + pkcs12_password = a + + elif o == '--pkcs12-password-file': + pkcs12_password_file = a + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not pkcs12_file: + print('ERROR: missing output file') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + if not pkcs12_password and not pkcs12_password_file: + pkcs12_password = getpass.getpass(prompt='Enter password for PKCS #12 file: ') + + nssdb = instance.open_nssdb() + try: + nssdb.export_pkcs12( + pkcs12_file=pkcs12_file, + pkcs12_password=pkcs12_password, + pkcs12_password_file=pkcs12_password_file) + finally: + nssdb.close() + + self.print_message('Exported certificates') + + class InstanceFindCLI(pki.cli.CLI): def __init__(self): diff --git a/base/server/python/pki/server/cli/kra.py b/base/server/python/pki/server/cli/kra.py new file mode 100644 index 0000000000000000000000000000000000000000..d1b27dbc13167af483ced1c1118f55ea44492419 --- /dev/null +++ b/base/server/python/pki/server/cli/kra.py @@ -0,0 +1,139 @@ +# Authors: +# Endi S. Dewata +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# + +from __future__ import absolute_import +from __future__ import print_function +import getopt +import io +import os +import shutil +import sys +import tempfile + +import pki.cli + + +class KRACLI(pki.cli.CLI): + + def __init__(self): + super(KRACLI, self).__init__( + 'kra', 'KRA management commands') + + self.add_module(KRACloneCLI()) + + +class KRACloneCLI(pki.cli.CLI): + + def __init__(self): + super(KRACloneCLI, self).__init__( + 'clone', 'KRA clone management commands') + + self.add_module(KRAClonePrepareCLI()) + + +class KRAClonePrepareCLI(pki.cli.CLI): + + def __init__(self): + super(KRAClonePrepareCLI, self).__init__( + 'prepare', 'Prepare KRA clone') + + def print_help(self): + print('Usage: pki-server kra-clone-prepare [OPTIONS]') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --pkcs12-file PKCS #12 file to store certificates and keys.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file File containing the PKCS #12 password.') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, args): + + try: + opts, _ = getopt.gnu_getopt(args, 'i:v', [ + 'instance=', 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + pkcs12_file = None + pkcs12_password = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--pkcs12-file': + pkcs12_file = a + + elif o == '--pkcs12-password': + pkcs12_password = a + + elif o == '--pkcs12-password-file': + with io.open(a, 'rb') as f: + pkcs12_password = f.read() + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not pkcs12_file: + print('ERROR: Missing PKCS #12 file') + self.print_help() + sys.exit(1) + + if not pkcs12_password: + print('ERROR: Missing PKCS #12 password') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + subsystem = instance.get_subsystem('kra') + + tmpdir = tempfile.mkdtemp() + + try: + pkcs12_password_file = os.path.join(tmpdir, 'pkcs12_password.txt') + with open(pkcs12_password_file, 'w') as f: + f.write(pkcs12_password) + + subsystem.export_system_cert( + 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) + subsystem.export_system_cert('transport', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert('storage', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + + finally: + shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/ocsp.py b/base/server/python/pki/server/cli/ocsp.py new file mode 100644 index 0000000000000000000000000000000000000000..7b1b43487aac97e86962edfd5ba1bb687e28a69e --- /dev/null +++ b/base/server/python/pki/server/cli/ocsp.py @@ -0,0 +1,138 @@ +# Authors: +# Endi S. Dewata +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# + +from __future__ import absolute_import +from __future__ import print_function +import getopt +import io +import os +import shutil +import sys +import tempfile + +import pki.cli + + +class OCSPCLI(pki.cli.CLI): + + def __init__(self): + super(OCSPCLI, self).__init__( + 'ocsp', 'OCSP management commands') + + self.add_module(OCSPCloneCLI()) + + +class OCSPCloneCLI(pki.cli.CLI): + + def __init__(self): + super(OCSPCloneCLI, self).__init__( + 'clone', 'OCSP clone management commands') + + self.add_module(OCSPClonePrepareCLI()) + + +class OCSPClonePrepareCLI(pki.cli.CLI): + + def __init__(self): + super(OCSPClonePrepareCLI, self).__init__( + 'prepare', 'Prepare OCSP clone') + + def print_help(self): + print('Usage: pki-server ocsp-clone-prepare [OPTIONS]') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --pkcs12-file PKCS #12 file to store certificates and keys.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file File containing the PKCS #12 password.') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, args): + + try: + opts, _ = getopt.gnu_getopt(args, 'i:v', [ + 'instance=', 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + pkcs12_file = None + pkcs12_password = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--pkcs12': + pkcs12_file = a + + elif o == '--pkcs12-password': + pkcs12_password = a + + elif o == '--pkcs12-password-file': + with io.open(a, 'rb') as f: + pkcs12_password = f.read() + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not pkcs12_file: + print('ERROR: Missing PKCS #12 file') + self.print_help() + sys.exit(1) + + if not pkcs12_password: + print('ERROR: Missing PKCS #12 password') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + subsystem = instance.get_subsystem('ocsp') + + tmpdir = tempfile.mkdtemp() + + try: + pkcs12_password_file = os.path.join(tmpdir, 'pkcs12_password.txt') + with open(pkcs12_password_file, 'w') as f: + f.write(pkcs12_password) + + subsystem.export_system_cert( + 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) + subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + + finally: + shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/subsystem.py b/base/server/python/pki/server/cli/subsystem.py index f0e38b65e3eec743773325e6f52debf6d665e891..8450f7b61d8191b737de8c3e47a80b4b2cca1691 100644 --- a/base/server/python/pki/server/cli/subsystem.py +++ b/base/server/python/pki/server/cli/subsystem.py @@ -301,12 +301,14 @@ class SubsystemCertCLI(pki.cli.CLI): self.add_module(SubsystemCertUpdateCLI()) @staticmethod - def print_subsystem_cert(cert): + def print_subsystem_cert(cert, show_all=False): print(' Cert ID: %s' % cert['id']) print(' Nickname: %s' % cert['nickname']) print(' Token: %s' % cert['token']) - print(' Certificate: %s' % cert['data']) - print(' Request: %s' % cert['request']) + + if show_all: + print(' Certificate: %s' % cert['data']) + print(' Request: %s' % cert['request']) class SubsystemCertFindCLI(pki.cli.CLI): @@ -315,10 +317,11 @@ class SubsystemCertFindCLI(pki.cli.CLI): super(SubsystemCertFindCLI, self).__init__( 'find', 'Find subsystem certificates') - def usage(self): + def print_help(self): print('Usage: pki-server subsystem-cert-find [OPTIONS] ') print() print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --show-all Show all attributes.') print(' -v, --verbose Run in verbose mode.') print(' --help Show help message.') print() @@ -327,26 +330,30 @@ class SubsystemCertFindCLI(pki.cli.CLI): try: opts, args = getopt.gnu_getopt(argv, 'i:v', [ - 'instance=', + 'instance=', 'show-all', 'verbose', 'help']) except getopt.GetoptError as e: print('ERROR: ' + str(e)) - self.usage() + self.print_help() sys.exit(1) if len(args) != 1: print('ERROR: missing subsystem ID') - self.usage() + self.print_help() sys.exit(1) subsystem_name = args[0] instance_name = 'pki-tomcat' + show_all = False for o, a in opts: if o in ('-i', '--instance'): instance_name = a + elif o == '--show-all': + show_all = True + elif o in ('-v', '--verbose'): self.set_verbose(True) @@ -356,14 +363,14 @@ class SubsystemCertFindCLI(pki.cli.CLI): else: print('ERROR: unknown option ' + o) - self.usage() + self.print_help() sys.exit(1) instance = pki.server.PKIInstance(instance_name) instance.load() subsystem = instance.get_subsystem(subsystem_name) - results = subsystem.find_subsystem_certs() + results = subsystem.find_system_certs() self.print_message('%s entries matched' % len(results)) @@ -374,7 +381,7 @@ class SubsystemCertFindCLI(pki.cli.CLI): else: print() - SubsystemCertCLI.print_subsystem_cert(cert) + SubsystemCertCLI.print_subsystem_cert(cert, show_all) class SubsystemCertShowCLI(pki.cli.CLI): @@ -448,8 +455,8 @@ class SubsystemCertExportCLI(pki.cli.CLI): super(SubsystemCertExportCLI, self).__init__( 'export', 'Export subsystem certificate') - def usage(self): # flake8: noqa - print('Usage: pki-server subsystem-cert-export [OPTIONS] ') + def print_help(self): # flake8: noqa + print('Usage: pki-server subsystem-cert-export [OPTIONS] [cert ID]') print() print(' -i, --instance Instance ID (default: pki-tomcat).') print(' --cert-file Output file to store the exported certificate in PEM format.') @@ -471,21 +478,16 @@ class SubsystemCertExportCLI(pki.cli.CLI): except getopt.GetoptError as e: print('ERROR: ' + str(e)) - self.usage() + self.print_help() sys.exit(1) if len(args) < 1: print('ERROR: missing subsystem ID') - self.usage() - sys.exit(1) - - if len(args) < 2: - print('ERROR: missing cert ID') - self.usage() + self.print_help() sys.exit(1) subsystem_name = args[0] - cert_id = args[1] + instance_name = 'pki-tomcat' cert_file = None csr_file = None @@ -521,19 +523,28 @@ class SubsystemCertExportCLI(pki.cli.CLI): else: print('ERROR: unknown option ' + o) - self.usage() + self.print_help() sys.exit(1) - if not cert_file and not csr_file and not pkcs12_file: + if not pkcs12_file: print('ERROR: missing output file') - self.usage() + self.print_help() sys.exit(1) instance = pki.server.PKIInstance(instance_name) instance.load() subsystem = instance.get_subsystem(subsystem_name) - subsystem_cert = subsystem.get_subsystem_cert(cert_id) + subsystem_cert = None + + if len(args) >= 2: + cert_id = args[1] + subsystem_cert = subsystem.get_subsystem_cert(cert_id) + + if (cert_file or csr_file) and not subsystem_cert: + print('ERROR: missing cert ID') + self.print_help() + sys.exit(1) if cert_file: @@ -552,17 +563,28 @@ class SubsystemCertExportCLI(pki.cli.CLI): if not pkcs12_password and not pkcs12_password_file: pkcs12_password = getpass.getpass(prompt='Enter password for PKCS #12 file: ') + nicknames = [] + + if subsystem_cert: + nicknames.append(subsystem_cert['nickname']) + + else: + subsystem_certs = subsystem.find_system_certs() + for subsystem_cert in subsystem_certs: + nicknames.append(subsystem_cert['nickname']) + nssdb = instance.open_nssdb() try: nssdb.export_pkcs12( pkcs12_file=pkcs12_file, - nickname=subsystem_cert['nickname'], + nicknames=nicknames, pkcs12_password=pkcs12_password, pkcs12_password_file=pkcs12_password_file) + finally: nssdb.close() - self.print_message('Exported %s certificate' % cert_id) + self.print_message('Export complete') class SubsystemCertUpdateCLI(pki.cli.CLI): diff --git a/base/server/python/pki/server/cli/tks.py b/base/server/python/pki/server/cli/tks.py new file mode 100644 index 0000000000000000000000000000000000000000..39343db98e937e23aaa08d5556f03b4ff8724917 --- /dev/null +++ b/base/server/python/pki/server/cli/tks.py @@ -0,0 +1,138 @@ +# Authors: +# Endi S. Dewata +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# + +from __future__ import absolute_import +from __future__ import print_function +import getopt +import io +import os +import shutil +import sys +import tempfile + +import pki.cli + + +class TKSCLI(pki.cli.CLI): + + def __init__(self): + super(TKSCLI, self).__init__( + 'tks', 'TKS management commands') + + self.add_module(TKSCloneCLI()) + + +class TKSCloneCLI(pki.cli.CLI): + + def __init__(self): + super(TKSCloneCLI, self).__init__( + 'clone', 'TKS clone management commands') + + self.add_module(TKSClonePrepareCLI()) + + +class TKSClonePrepareCLI(pki.cli.CLI): + + def __init__(self): + super(TKSClonePrepareCLI, self).__init__( + 'prepare', 'Prepare TKS clone') + + def print_help(self): + print('Usage: pki-server tks-clone-prepare [OPTIONS]') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --pkcs12-file PKCS #12 file to store certificates and keys.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file File containing the PKCS #12 password.') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, args): + + try: + opts, _ = getopt.gnu_getopt(args, 'i:v', [ + 'instance=', 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + pkcs12_file = None + pkcs12_password = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--pkcs12-file': + pkcs12_file = a + + elif o == '--pkcs12-password': + pkcs12_password = a + + elif o == '--pkcs12-password-file': + with io.open(a, 'rb') as f: + pkcs12_password = f.read() + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not pkcs12_file: + print('ERROR: Missing PKCS #12 file') + self.print_help() + sys.exit(1) + + if not pkcs12_password: + print('ERROR: Missing PKCS #12 password') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + subsystem = instance.get_subsystem('tks') + + tmpdir = tempfile.mkdtemp() + + try: + pkcs12_password_file = os.path.join(tmpdir, 'pkcs12_password.txt') + with open(pkcs12_password_file, 'w') as f: + f.write(pkcs12_password) + + subsystem.export_system_cert( + 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) + subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + + finally: + shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/tps.py b/base/server/python/pki/server/cli/tps.py new file mode 100644 index 0000000000000000000000000000000000000000..05045cb0d7c4bd19956f8b98afe5b370bc587d07 --- /dev/null +++ b/base/server/python/pki/server/cli/tps.py @@ -0,0 +1,138 @@ +# Authors: +# Endi S. Dewata +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# + +from __future__ import absolute_import +from __future__ import print_function +import getopt +import io +import os +import shutil +import sys +import tempfile + +import pki.cli + + +class TPSCLI(pki.cli.CLI): + + def __init__(self): + super(TPSCLI, self).__init__( + 'tps', 'TPS management commands') + + self.add_module(TPSCloneCLI()) + + +class TPSCloneCLI(pki.cli.CLI): + + def __init__(self): + super(TPSCloneCLI, self).__init__( + 'clone', 'TPS clone management commands') + + self.add_module(TPSClonePrepareCLI()) + + +class TPSClonePrepareCLI(pki.cli.CLI): + + def __init__(self): + super(TPSClonePrepareCLI, self).__init__( + 'prepare', 'Prepare TPS clone') + + def print_help(self): + print('Usage: pki-server tps-clone-prepare [OPTIONS]') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --pkcs12-file PKCS #12 file to store certificates and keys.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file File containing the PKCS #12 password.') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, args): + + try: + opts, _ = getopt.gnu_getopt(args, 'i:v', [ + 'instance=', 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + pkcs12_file = None + pkcs12_password = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--pkcs12-file': + pkcs12_file = a + + elif o == '--pkcs12-password': + pkcs12_password = a + + elif o == '--pkcs12-password-file': + with io.open(a, 'rb') as f: + pkcs12_password = f.read() + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not pkcs12_file: + print('ERROR: Missing PKCS #12 file') + self.print_help() + sys.exit(1) + + if not pkcs12_password: + print('ERROR: Missing PKCS #12 password') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + subsystem = instance.get_subsystem('tps') + + tmpdir = tempfile.mkdtemp() + + try: + pkcs12_password_file = os.path.join(tmpdir, 'pkcs12_password.txt') + with open(pkcs12_password_file, 'w') as f: + f.write(pkcs12_password) + + subsystem.export_system_cert( + 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) + subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + + finally: + shutil.rmtree(tmpdir) diff --git a/base/server/sbin/pki-server b/base/server/sbin/pki-server index f3e784a1539a4cff82cdc765da53f2cb01b27a3c..5e81ed6b9cc36c2358a7b25e162b1c79d53f2967 100644 --- a/base/server/sbin/pki-server +++ b/base/server/sbin/pki-server @@ -26,6 +26,10 @@ import sys import pki.cli import pki.server.cli.ca +import pki.server.cli.kra +import pki.server.cli.ocsp +import pki.server.cli.tks +import pki.server.cli.tps import pki.server.cli.instance import pki.server.cli.subsystem import pki.server.cli.migrate @@ -40,6 +44,11 @@ class PKIServerCLI(pki.cli.CLI): 'PKI server command-line interface') self.add_module(pki.server.cli.ca.CACLI()) + self.add_module(pki.server.cli.kra.KRACLI()) + self.add_module(pki.server.cli.ocsp.OCSPCLI()) + self.add_module(pki.server.cli.tks.TKSCLI()) + self.add_module(pki.server.cli.tps.TPSCLI()) + self.add_module(pki.server.cli.instance.InstanceCLI()) self.add_module(pki.server.cli.subsystem.SubsystemCLI()) self.add_module(pki.server.cli.migrate.MigrateCLI()) -- 2.4.3