From ftweedal at redhat.com Mon Nov 2 07:14:13 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Mon, 2 Nov 2015 17:14:13 +1000 Subject: [Pki-devel] [PATCH] 0054 Lightweight CAs: add audit events Message-ID: <20151102071413.GN20018@dhcp-40-8.bne.redhat.com> The attached patch adds audit events for lightweight CA administration, fixing https://fedorahosted.org/pki/ticket/1590. Cheers, Fraser -------------- next part -------------- From d09114698667d16c56fbc0eaa31bf9bf2a1a3275 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Mon, 2 Nov 2015 01:43:26 -0500 Subject: [PATCH] Lightweight CAs: add audit events Add audit events for lightweight CA administration. Fixes: https://fedorahosted.org/pki/ticket/1590 --- base/ca/shared/conf/CS.cfg.in | 4 +- .../dogtagpki/server/ca/rest/AuthorityService.java | 62 ++++++++++++++-------- .../src/com/netscape/certsrv/common/ScopeDef.java | 3 ++ base/server/cmsbundle/src/LogMessages.properties | 8 +++ 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/base/ca/shared/conf/CS.cfg.in b/base/ca/shared/conf/CS.cfg.in index 05508f8b289e027cc4e97df3fe584dfbc2290f7e..c679af5b3fd7f1fcda79848f68a9258580457b63 100644 --- a/base/ca/shared/conf/CS.cfg.in +++ b/base/ca/shared/conf/CS.cfg.in @@ -901,11 +901,11 @@ log.instance.SignedAudit._001=## Signed Audit Logging log.instance.SignedAudit._002=## log.instance.SignedAudit._003=## log.instance.SignedAudit._004=## Available Audit events: -log.instance.SignedAudit._005=## AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,LOG_EXPIRATION_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER +log.instance.SignedAudit._005=## AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,LOG_EXPIRATION_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG log.instance.SignedAudit._006=## log.instance.SignedAudit.bufferSize=512 log.instance.SignedAudit.enable=true -log.instance.SignedAudit.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER +log.instance.SignedAudit.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG log.instance.SignedAudit.expirationTime=0 log.instance.SignedAudit.fileName=[PKI_INSTANCE_PATH]/logs/[PKI_SUBSYSTEM_TYPE]/signedAudit/ca_audit log.instance.SignedAudit.flushInterval=5 diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java index b23a4b853252dd4e5dadef3b3ef508113e1d5337..e915579a4735d82a9673c3cb18bc2397eb619ec8 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java @@ -22,7 +22,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.cert.CertificateEncodingException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Context; @@ -48,6 +50,9 @@ import com.netscape.certsrv.ca.CANotLeafException; import com.netscape.certsrv.ca.CATypeException; import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.ca.IssuerUnavailableException; +import com.netscape.certsrv.common.OpDef; +import com.netscape.certsrv.common.ScopeDef; +import com.netscape.certsrv.logging.ILogger; import com.netscape.cms.servlet.base.PKIService; import com.netscape.cmsutil.util.Utils; @@ -74,12 +79,9 @@ public class AuthorityService extends PKIService implements AuthorityResource { @Context private HttpServletRequest servletRequest; - /* - private final static String LOGGING_SIGNED_AUDIT_CERT_PROFILE_APPROVAL = - "LOGGING_SIGNED_AUDIT_CERT_PROFILE_APPROVAL_4"; - private final static String LOGGING_SIGNED_AUDIT_CONFIG_CERT_PROFILE = - "LOGGING_SIGNED_AUDIT_CONFIG_CERT_PROFILE_3"; - */ + private final static String LOGGING_SIGNED_AUDIT_AUTHORITY_CONFIG = + "LOGGING_SIGNED_AUDIT_AUTHORITY_CONFIG_3"; + @Override public Response listCAs() { @@ -178,18 +180,29 @@ public class AuthorityService extends PKIService implements AuthorityResource { throw new BadRequestException("Bad Authority ID: " + parentAIDString); } + Map auditParams = new LinkedHashMap<>(); + auditParams.put("dn", data.getDN()); + if (parentAID != null) + auditParams.put("parent", parentAIDString); + if (data.getDescription() != null) + auditParams.put("description", data.getDescription()); + try { ICertificateAuthority subCA = hostCA.createCA( data.getDN(), parentAID, data.getDescription()); + audit(ILogger.SUCCESS, OpDef.OP_ADD, + subCA.getAuthorityID().toString(), auditParams); return createOKResponse(readAuthorityData(subCA)); } catch (IllegalArgumentException e) { throw new BadRequestException(e.toString()); } catch (CANotFoundException e) { throw new ResourceNotFoundException(e.toString()); } catch (IssuerUnavailableException e) { + audit(ILogger.FAILURE, OpDef.OP_ADD, "", auditParams); throw new ConflictingOperationException(e.toString()); } catch (Exception e) { CMS.debug(e); + audit(ILogger.FAILURE, OpDef.OP_ADD, "", auditParams); throw new PKIException("Error creating CA: " + e.toString()); } } @@ -207,15 +220,28 @@ public class AuthorityService extends PKIService implements AuthorityResource { if (ca == null) throw new ResourceNotFoundException("CA \"" + aidString + "\" not found"); + Map auditParams = new LinkedHashMap<>(); + if (data.getEnabled() != ca.getAuthorityEnabled()) + auditParams.put("enabled", data.getEnabled().toString()); + String curDesc = ca.getAuthorityDescription(); + String newDesc = data.getDescription(); + if (curDesc != null && !curDesc.equals(newDesc) + || curDesc == null && newDesc != null) + auditParams.put("description", data.getDescription()); + try { ca.modifyAuthority(data.getEnabled(), data.getDescription()); + audit(ILogger.SUCCESS, OpDef.OP_MODIFY, ca.getAuthorityID().toString(), auditParams); return createOKResponse(readAuthorityData(ca)); } catch (CATypeException e) { + audit(ILogger.FAILURE, OpDef.OP_MODIFY, ca.getAuthorityID().toString(), auditParams); throw new ForbiddenException(e.toString()); } catch (IssuerUnavailableException e) { + audit(ILogger.FAILURE, OpDef.OP_MODIFY, ca.getAuthorityID().toString(), auditParams); throw new ConflictingOperationException(e.toString()); } catch (EBaseException e) { CMS.debug(e); + audit(ILogger.FAILURE, OpDef.OP_MODIFY, ca.getAuthorityID().toString(), auditParams); throw new PKIException("Error modifying authority: " + e.toString()); } } @@ -249,13 +275,17 @@ public class AuthorityService extends PKIService implements AuthorityResource { try { ca.deleteAuthority(); + audit(ILogger.SUCCESS, OpDef.OP_DELETE, aidString, null); return createNoContentResponse(); } catch (CATypeException e) { + audit(ILogger.FAILURE, OpDef.OP_DELETE, aidString, null); throw new ForbiddenException(e.toString()); } catch (CAEnabledException | CANotLeafException e) { + audit(ILogger.FAILURE, OpDef.OP_DELETE, aidString, null); throw new ConflictingOperationException(e.toString()); } catch (EBaseException e) { CMS.debug(e); + audit(ILogger.FAILURE, OpDef.OP_DELETE, aidString, null); throw new PKIException("Error modifying authority: " + e.toString()); } } @@ -286,25 +316,15 @@ public class AuthorityService extends PKIService implements AuthorityResource { "-----END " + name + "-----\n"; } - /* TODO work out what audit messages are needed - public void auditProfileChangeState(String profileId, String op, String status) { + private void audit( + String status, String op, String id, + Map params) { String msg = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_CERT_PROFILE_APPROVAL, + LOGGING_SIGNED_AUDIT_AUTHORITY_CONFIG, auditor.getSubjectID(), status, - profileId, - op); + auditor.getParamString(ScopeDef.SC_AUTHORITY, op, id, params)); auditor.log(msg); } - public void auditProfileChange(String scope, String type, String id, String status, Map params) { - String msg = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_CONFIG_CERT_PROFILE, - auditor.getSubjectID(), - status, - auditor.getParamString(scope, type, id, params)); - auditor.log(msg); - } - */ - } diff --git a/base/common/src/com/netscape/certsrv/common/ScopeDef.java b/base/common/src/com/netscape/certsrv/common/ScopeDef.java index f689d1504d2cebf8868246e0ce51653a3d78d291..a06696e4b05e2f63e544bd2c0deaa2b8f6abd732 100644 --- a/base/common/src/com/netscape/certsrv/common/ScopeDef.java +++ b/base/common/src/com/netscape/certsrv/common/ScopeDef.java @@ -41,6 +41,9 @@ public interface ScopeDef { public final static String SC_ADMIN = "admin"; public final static String SC_NETWORK = "network"; + // lightweight authorities + public final static String SC_AUTHORITY = "authority"; + // profile public final static String SC_PROFILE_IMPLS = "profile"; public final static String SC_PROFILE_RULES = "rules"; diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties index e0e926ccb43a94c01bcb40cad81f7f04ee437f45..b3355f06869744fc29932d0e19c4a14a40f38845 100644 --- a/base/server/cmsbundle/src/LogMessages.properties +++ b/base/server/cmsbundle/src/LogMessages.properties @@ -2596,6 +2596,14 @@ LOGGING_SIGNED_AUDIT_CONFIG_TOKEN_PROFILE_3=:[AuditEv # - used when token state changed # LOGGING_SIGNED_AUDIT_TOKEN_STATE_CHANGE_5=:[AuditEvent=TOKEN_STATE_CHANGE][SubjectID={0}][Outcome={1}][CUID={2}][oldState={3}][newState={4}] token state changed +# +# LOGGING_SIGNED_AUDIT_AUTHORITY_CONFIG +# - used when configuring lightweight authorities +# ParamNameValPairs must be a name;;value pair +# (where name and value are separated by the delimiter ;;) +# separated by + (if more than one name;;value pair) of config params changed +# +LOGGING_SIGNED_AUDIT_AUTHORITY_CONFIG_3=:[AuditEvent=AUTHORITY_CONFIG][SubjectID={0}][Outcome={1}][ParamNameValPairs={2}] lightweight authority configuration change ########################### -- 2.4.3 From mharmsen at redhat.com Mon Nov 2 15:17:30 2015 From: mharmsen at redhat.com (Matthew Harmsen) Date: Mon, 2 Nov 2015 08:17:30 -0700 Subject: [Pki-devel] Karma Request for Dogtag 10.2.6 in Fedora 23 Message-ID: <56377E8A.6020307@redhat.com> Everyone, Please provide Karma for the following Dogtag 10.2.6 packages in Fedora 23: * pki-core-10.2.6-12.fc23 This build addresses the following issue related to an IPA issue: * PKI TRAC Ticket #1310 - Auto migration to Tomcat 8 Thanks, -- Matt P. S. - A build containing these fixes was also made available for 'rawhide' (Fedora 24): * pki-core-10.2.6-12.fc24 -------------- next part -------------- An HTML attachment was scrubbed... URL: From ftweedal at redhat.com Wed Nov 4 05:14:17 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Wed, 4 Nov 2015 15:14:17 +1000 Subject: [Pki-devel] [BLOG] FreeIPA PKI: current plans and a future vision Message-ID: <20151104051417.GY20018@dhcp-40-8.bne.redhat.com> I have been alluding for a while about my ideas for future FreeIPA/Dogtag PKI integration; I finally put the ideas down in a blog post. If you are interested in this aspect of IdM please read it; all feedback is welcome! http://blog-ftweedal.rhcloud.com/2015/11/freeipa-pki-current-plans-and-a-future-vision/ Thanks, Fraser From ftweedal at redhat.com Thu Nov 5 05:22:23 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 5 Nov 2015 15:22:23 +1000 Subject: [Pki-devel] [PATCH] 0055 Allow encoded slashes in HTTP paths Message-ID: <20151105052223.GC20018@dhcp-40-8.bne.redhat.com> The attached patch fixes GET-based OCSP requests, https://fedorahosted.org/pki/ticket/1658 Cheers, Fraser -------------- next part -------------- From e9f5d53a53c955279e0b56a5721e552cb39b0bd4 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 5 Nov 2015 00:17:24 -0500 Subject: [PATCH] Allow encoded slashes in HTTP paths Properly formed GET-based OCSP requests can contain URL-encoded slashes in the HTTP path[1] but our Tomcat configuration does not permit this (returns 400 Bad Request). Change catalina.properties to allow URL-encoded slashes in HTTP paths. [1] https://tools.ietf.org/html/rfc6960#appendix-A.1 Fixes: https://fedorahosted.org/pki/ticket/1658 --- base/ca/shared/conf/catalina.properties | 2 ++ base/ocsp/shared/conf/catalina.properties | 2 ++ 2 files changed, 4 insertions(+) diff --git a/base/ca/shared/conf/catalina.properties b/base/ca/shared/conf/catalina.properties index 70cb7c05e78e0c4ab4b64a74d3f9eaadf96a1420..7e104e52d14852a785b49013520e5102ff356c64 100644 --- a/base/ca/shared/conf/catalina.properties +++ b/base/ca/shared/conf/catalina.properties @@ -85,3 +85,5 @@ tomcat.util.buf.StringCache.byte.enabled=true #tomcat.util.buf.StringCache.char.enabled=true #tomcat.util.buf.StringCache.trainThreshold=500000 #tomcat.util.buf.StringCache.cacheSize=5000 + +org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true diff --git a/base/ocsp/shared/conf/catalina.properties b/base/ocsp/shared/conf/catalina.properties index 70cb7c05e78e0c4ab4b64a74d3f9eaadf96a1420..7e104e52d14852a785b49013520e5102ff356c64 100644 --- a/base/ocsp/shared/conf/catalina.properties +++ b/base/ocsp/shared/conf/catalina.properties @@ -85,3 +85,5 @@ tomcat.util.buf.StringCache.byte.enabled=true #tomcat.util.buf.StringCache.char.enabled=true #tomcat.util.buf.StringCache.trainThreshold=500000 #tomcat.util.buf.StringCache.cacheSize=5000 + +org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true -- 2.4.3 From edewata at redhat.com Mon Nov 9 19:59:00 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Mon, 9 Nov 2015 13:59:00 -0600 Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. Message-ID: <5640FB04.2050706@redhat.com> The CA certificate request and signing processes have been moved from the configuration servlet into the deployment scriptlet. This way the admin will have the option to: * generate self-signed CA certificate * import externally-signed CA certificate * import existing CA certificate before the server is started for the first time. https://fedorahosted.org/pki/ticket/456 Note: This is a preliminary patch. There are some unfinished works. -- Endi S. Dewata -------------- next part -------------- From f81bea0247d11109bb30c704648d50ab3050cc20 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sat, 7 Nov 2015 00:09:19 +0100 Subject: [PATCH] Refactored CA certificate generation. The CA certificate request and signing processes have been moved from the configuration servlet into the deployment scriptlet. This way the admin will have the option to: * generate self-signed CA certificate * import externally-signed CA certificate * import existing CA certificate before the server is started for the first time. https://fedorahosted.org/pki/ticket/456 --- base/common/python/pki/nss.py | 217 +++++++++++++++++++++ .../cms/servlet/csadmin/ConfigurationUtils.java | 86 +++++++- .../dogtagpki/server/rest/SystemConfigService.java | 29 ++- .../python/pki/server/deployment/pkihelper.py | 16 +- .../server/deployment/scriptlets/configuration.py | 207 ++++++++++++++++---- .../server/deployment/scriptlets/finalization.py | 12 +- 6 files changed, 505 insertions(+), 62 deletions(-) create mode 100644 base/common/python/pki/nss.py diff --git a/base/common/python/pki/nss.py b/base/common/python/pki/nss.py new file mode 100644 index 0000000000000000000000000000000000000000..d156418fba34ddf6a6ad7ac3949afa401f86b22b --- /dev/null +++ b/base/common/python/pki/nss.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# 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) 2015 Red Hat, Inc. +# All rights reserved. +# + +import base64 +import os +import shutil +import subprocess +import tempfile + + +class NSSDatabase(object): + + def __init__(self, directory, password_file=None): + self.directory = directory + self.password_file = password_file + + def add_cert(self, + nickname, cert_file, + trust_attributes='u,u,u'): + + subprocess.check_call([ + 'certutil', + '-A', + '-d', self.directory, + '-n', nickname, + '-i', cert_file, + '-t', trust_attributes, + ]) + + def modify_cert(self, + nickname, + trust_attributes='u,u,u'): + + subprocess.check_call([ + 'certutil', + '-M', + '-d', self.directory, + '-n', nickname, + '-t', trust_attributes, + ]) + + def create_noise(self, noise_file, size=2048): + + subprocess.check_call([ + 'openssl', + 'rand', + '-out', noise_file, + str(size) + ]) + + def create_request(self, + subject_dn, + noise_file, + request_file): + + tmpdir = tempfile.mkdtemp() + + try: + binary_request_file = os.path.join(tmpdir, 'request.bin') + b64_request_file = os.path.join(tmpdir, 'request.b64') + + # generate binary request + subprocess.check_call([ + 'certutil', + '-R', + '-d', self.directory, + '-f', self.password_file, + '-s', subject_dn, + '-z', noise_file, + '-o', binary_request_file + ]) + + # encode binary request in base-64 + subprocess.check_call([ + 'BtoA', binary_request_file, b64_request_file]) + + # read base-64 request + with open(b64_request_file, 'r') as f: + b64_request = f.read() + + # add header and footer + with open(request_file, 'w') as f: + f.write('-----BEGIN NEW CERTIFICATE REQUEST-----\n') + f.write(b64_request) + f.write('-----END NEW CERTIFICATE REQUEST-----\n') + + finally: + shutil.rmtree(tmpdir) + + def create_self_signed_ca_cert(self, + issuer_dn, + request_file, + cert_file, + serial='1', + validity=240): + + p = subprocess.Popen([ + 'certutil', + '-C', + '-x', + '-d', self.directory, + '-f', self.password_file, + '-c', issuer_dn, + '-a', + '-i', request_file, + '-o', cert_file, + '-m', serial, + '-v', str(validity), + '--keyUsage', 'digitalSignature,nonRepudiation,certSigning,crlSigning,critical', + '-2', + '-3', + '--extSKID', + '--extAIA' + ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + keystroke = '' + + # Is this a CA certificate [y/N]? + keystroke += 'y\n' + + # Enter the path length constraint, enter to skip [<0 for unlimited path]: + keystroke += '\n' + + # Is this a critical extension [y/N]? + keystroke += 'y\n' + + # Enter value for the authKeyID extension [y/N]? + keystroke += 'y\n' + + # TODO: generate SHA1 ID (see APolicyRule.formSHA1KeyId()) + # Enter value for the key identifier fields,enter to omit: + keystroke += '2d:7e:83:37:75:5a:fd:0e:8d:52:a3:70:16:93:36:b8:4a:d6:84:9f\n' + + # Select one of the following general name type: + keystroke += '0\n' + + # Enter value for the authCertSerial field, enter to omit: + keystroke += '\n' + + # Is this a critical extension [y/N]? + keystroke += '\n' + + # TODO: generate SHA1 ID (see APolicyRule.formSHA1KeyId()) + # Adding Subject Key ID extension. + # Enter value for the key identifier fields,enter to omit: + keystroke += '2d:7e:83:37:75:5a:fd:0e:8d:52:a3:70:16:93:36:b8:4a:d6:84:9f\n' + + # Is this a critical extension [y/N]? + keystroke += '\n' + + # Enter access method type for Authority Information Access extension: + keystroke += '2\n' + + # Select one of the following general name type: + keystroke += '7\n' + + # TODO: replace with actual hostname name and port number + # Enter data: + keystroke += 'http://server.example.com:8080/ca/ocsp\n' + + # Select one of the following general name type: + keystroke += '0\n' + + # Add another location to the Authority Information Access extension [y/N] + keystroke += '\n' + + # Is this a critical extension [y/N]? + keystroke += '\n' + + p.communicate(keystroke) + + rc = p.wait() + + if rc: + raise Exception('Failed to generate self-signed CA certificate. RC: %d' + rc) + + def get_cert(self, nickname, cert_type='pem'): + + if cert_type == 'pem': + cert_type_option = '-a' + + elif cert_type == 'der' or cert_type == 'base64': + cert_type_option = '-r' + + else: + raise Exception('Invalid certificate type: %s' % cert_type) + + cert = subprocess.check_output([ + 'certutil', + '-L', + '-d', self.directory, + '-n', nickname, + cert_type_option + ]) + + if cert_type == 'base64': + cert = base64.b64encode(cert) + + return cert diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java index 88118adf8a7535442d8f1f678ce14f6f6ac07e51..31d18f06e863a3388c6a2a79345a1f07b1926f6d 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java +++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java @@ -126,6 +126,7 @@ import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.base.ISubsystem; +import com.netscape.certsrv.base.MetaInfo; import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.base.ResourceNotFoundException; import com.netscape.certsrv.ca.ICertificateAuthority; @@ -133,6 +134,8 @@ import com.netscape.certsrv.client.ClientConfig; import com.netscape.certsrv.client.PKIClient; import com.netscape.certsrv.client.PKIConnection; import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.certsrv.dbs.certdb.ICertRecord; +import com.netscape.certsrv.dbs.certdb.ICertificateRepository; import com.netscape.certsrv.dbs.crldb.ICRLIssuingPointRecord; import com.netscape.certsrv.key.KeyData; import com.netscape.certsrv.ldap.ILdapConnFactory; @@ -2318,24 +2321,55 @@ public class ConfigurationUtils { public static void createRSAKeyPair(String token, int keysize, IConfigStore config, String ct) throws NoSuchAlgorithmException, NoSuchTokenException, TokenException, CryptoManager.NotInitializedException, EPropertyNotFound, EBaseException { - /* generate key pair */ + KeyPair pair = null; do { + // generate key pair pair = CryptoUtil.generateRSAKeyPair(token, keysize); + + // get private key ID byte id[] = ((org.mozilla.jss.crypto.PrivateKey) pair.getPrivate()).getUniqueID(); - String kid = CryptoUtil.byte2string(id); - config.putString(PCERT_PREFIX + ct + ".privkey.id", kid); - // try to locate the private key + + // find private key org.mozilla.jss.crypto.PrivateKey privk = - CryptoUtil.findPrivateKeyFromID(CryptoUtil.string2byte(kid)); + CryptoUtil.findPrivateKeyFromID(id); + + // if private key not found, generate a new key pair if (privk == null) { - CMS.debug("Found bad RSA key id " + kid); pair = null; } + } while (pair == null); - byte modulus[] = ((RSAPublicKey) pair.getPublic()).getModulus().toByteArray(); - byte exponent[] = ((RSAPublicKey) pair.getPublic()).getPublicExponent().toByteArray(); + storeKeyPair(config, ct, pair); + } + + public static void loadKeyPair(String nickname, IConfigStore store, String ct) throws Exception { + + CryptoManager cm = CryptoManager.getInstance(); + + X509Certificate cert = cm.findCertByNickname(nickname); + + PublicKey publicKey = cert.getPublicKey(); + PrivateKey privateKey = cm.findPrivKeyByCert(cert); + + KeyPair pair = new KeyPair(publicKey, privateKey); + ConfigurationUtils.storeKeyPair(store, ct, pair); + } + + public static void storeKeyPair(IConfigStore config, String ct, KeyPair pair) + throws TokenException, EBaseException { + + RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic(); + PrivateKey privateKey = (PrivateKey) pair.getPrivate(); + + byte id[] = privateKey.getUniqueID(); + + String kid = CryptoUtil.byte2string(id); + config.putString(PCERT_PREFIX + ct + ".privkey.id", kid); + + byte modulus[] = publicKey.getModulus().toByteArray(); + byte exponent[] = publicKey.getPublicExponent().toByteArray(); config.putString(PCERT_PREFIX + ct + ".pubkey.modulus", CryptoUtil.byte2string(modulus)); @@ -2812,6 +2846,20 @@ public class ConfigurationUtils { } } + public static void loadCertRequest(IConfigStore config, String certTag, Cert cert) throws Exception { + + CMS.debug("ConfigurationUtils.loadCertRequest()"); + + String caDN = config.getString(PCERT_PREFIX + certTag + ".dn"); + cert.setDN(caDN); + + String subsystem = config.getString(PCERT_PREFIX + certTag + ".subsystem"); + String certReqs = config.getString(subsystem + "." + certTag + ".certreq"); + String certReqf = CryptoUtil.reqFormat(certReqs); + + cert.setRequest(certReqf); + } + public static void handleCertRequest(IConfigStore config, String certTag, Cert cert) throws EPropertyNotFound, EBaseException, InvalidKeyException, NotInitializedException, TokenException, NoSuchAlgorithmException, NoSuchProviderException, CertificateException, SignatureException, IOException { @@ -2953,6 +3001,28 @@ public class ConfigurationUtils { return pubk; } + public static void loadCert(Cert cert) throws Exception { + + String certTag = cert.getCertTag(); + CMS.debug("Loading certificate " + certTag); + + // load certificate + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate x509Cert = cm.findCertByNickname(cert.getNickname()); + X509CertImpl x509CertImpl = new X509CertImpl(x509Cert.getEncoded()); + + ICertificateAuthority ca = (ICertificateAuthority) CMS.getSubsystem(ICertificateAuthority.ID); + ICertificateRepository cr = ca.getCertificateRepository(); + + BigInteger serialNo = x509Cert.getSerialNumber(); + + MetaInfo meta = new MetaInfo(); + + // create certificate record + ICertRecord record = cr.createCertRecord(serialNo, x509CertImpl, meta); + cr.addCertificateRecord(record); + } + public static int handleCerts(Cert cert) throws IOException, EBaseException, CertificateException, NotInitializedException, TokenException, InvalidKeyException { String certTag = cert.getCertTag(); diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java index a0138681a39baeff272d75408dbee9a74d0529dc..64c9cea408909025a03b44b243d3a865576c1d58 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java @@ -420,7 +420,14 @@ public class SystemConfigService extends PKIService implements SystemConfigResou } cs.commit(false); - if (!request.getStepTwo()) { + if (csType.equals("CA") && tag.equals("signing")) { // self-signed CA cert + CMS.debug("configure(): loading signing cert key pair"); + ConfigurationUtils.loadKeyPair(certData.getNickname(), cs, tag); + + } else if (csType.equals("CA") && tag.equals("external_signing")) { // externally-signed CA cert + CMS.debug("configure(): external signing cert doesn't have key pair"); + + } else if (!request.getStepTwo()) { if (keytype.equals("ecc")) { String curvename = certData.getKeyCurveName() != null ? certData.getKeyCurveName() : cs.getString("keys.ecc.curve.default"); @@ -443,7 +450,14 @@ public class SystemConfigService extends PKIService implements SystemConfigResou cert.setSubsystem(cs.getString("preop.cert." + tag + ".subsystem")); cert.setType(cs.getString("preop.cert." + tag + ".type")); - if (!request.getStepTwo()) { + if (csType.equals("CA") && (tag.equals("signing") || tag.equals("external_signing"))) { + // self-signed && externally-signed CA + String certStr = cs.getString("ca." + tag + ".cert" ); + cert.setCert(certStr); + CMS.debug("ConfigurationUtils: certificate " + tag + ": " + certStr); + ConfigurationUtils.updateConfig(cs, tag); + + } else if (!request.getStepTwo()) { ConfigurationUtils.configCert(null, null, null, cert); } else { @@ -465,8 +479,15 @@ public class SystemConfigService extends PKIService implements SystemConfigResou CMS.debug("Step 2: certStr for '" + tag + "' is " + certStr); } - // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) - if (request.getStandAlone()) { + if (csType.equals("CA") && (tag.equals("signing") || tag.equals("external_signing"))) { + CMS.debug("Loading cert request for " + tag + " certificate"); + ConfigurationUtils.loadCertRequest(cs, tag, cert); + + CMS.debug("Loading cert " + tag); + ConfigurationUtils.loadCert(cert); + + } else if (request.getStandAlone()) { + // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) if (!request.getStepTwo()) { // Stand-alone PKI (Step 1) ConfigurationUtils.handleCertRequest(cs, tag, cert); diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py index 61f04d215ba3eba48b7e18733fd58b29555ced83..26c37034c2ecceac89b6f1008731a9f402dc55f5 100644 --- a/base/server/python/pki/server/deployment/pkihelper.py +++ b/base/server/python/pki/server/deployment/pkihelper.py @@ -3813,17 +3813,7 @@ class ConfigClient: if not isinstance(certs, list): certs = [certs] for cdata in certs: - if (self.subsystem == "CA" and self.external and - not self.external_step_two): - # External CA (Step 1) - if cdata['tag'].lower() == "signing": - # Save 'External CA Signing Certificate' CSR (Step 1) - self.save_system_csr( - cdata['request'], - log.PKI_CONFIG_EXTERNAL_CSR_SAVE, - self.mdict['pki_external_csr_path']) - return - elif self.standalone and not self.external_step_two: + if self.standalone and not self.external_step_two: # Stand-alone PKI (Step 1) if cdata['tag'].lower() == "audit_signing": # Save Stand-alone PKI 'Audit Signing Certificate' CSR @@ -3992,7 +3982,9 @@ class ConfigClient: data.tokenPassword = self.mdict['pki_token_password'] data.subsystemName = self.mdict['pki_subsystem_name'] data.standAlone = self.standalone - data.stepTwo = self.external_step_two + + # step two is no longer needed in the configuration servlet + data.stepTwo = False # Cloning parameters if self.mdict['pki_instance_type'] == "Tomcat": diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py index c6e89023560fc92cb9bd451d9b7f05818807da8a..fa878d591602cd248e51a5c95b35c541fe18d59e 100644 --- a/base/server/python/pki/server/deployment/scriptlets/configuration.py +++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py @@ -21,19 +21,75 @@ from __future__ import absolute_import import json +import os # PKI Deployment Imports from .. import pkiconfig as config from .. import pkimessages as log from .. import pkiscriptlet -import pki.system + +import pki.nss import pki.encoder +import pki.server +import pki.system +import pki.util # PKI Deployment Configuration Scriptlet class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): rv = 0 + def generate_ca_csr(self, deployer, subsystem, nssdb, request_file, password_file): + + deployer.password.create_password_conf( + password_file, + deployer.mdict['pki_pin'], pin_sans_token=True) + deployer.file.modify(password_file, + uid=0, gid=0) + + noise_file = os.path.join( + deployer.mdict['pki_database_path'], 'noise.bin') + + nssdb.create_noise( + noise_file=noise_file, + size=int(deployer.mdict['pki_ca_signing_key_size'])) + + nssdb.create_request( + subject_dn=deployer.mdict['pki_ca_signing_subject_dn'], + noise_file=noise_file, + request_file=request_file) + + b64_request = '' + + with open(request_file) as f: + for line in f: + if line.startswith('-----BEGIN NEW CERTIFICATE REQUEST-----'): + continue + if line.startswith('-----END NEW CERTIFICATE REQUEST-----'): + continue + else: + b64_request += line.rstrip('\r\n') + + subsystem.config['ca.signing.certreq'] = b64_request + + def import_ca_cert(self, subsystem, nssdb, nickname, cert_file): + + nssdb.add_cert( + nickname=nickname, + cert_file=cert_file, + trust_attributes='CTu,CTu,CTu') + + b64_cert = nssdb.get_cert( + nickname=nickname, + cert_type='base64' + ) + + subsystem.config['ca.signing.nickname'] = nickname + subsystem.config['ca.signing.tokenname'] = 'internal' + subsystem.config['ca.signing.cert'] = b64_cert + subsystem.config['ca.signing.cacertnickname'] = nickname + subsystem.config['ca.signing.defaultSigningAlgorithm'] = 'SHA256withRSA' + def spawn(self, deployer): # ALWAYS establish the following Tomcat instance symbolic link since @@ -81,40 +137,121 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_client_secmod_database'], password_file=deployer.mdict['pki_client_password_conf']) - # Start/Restart this Tomcat PKI Process - # Optionally prepare to enable a java debugger - # (e. g. - 'eclipse'): - if config.str2bool(deployer.mdict['pki_enable_java_debugger']): - config.prepare_for_an_external_java_debugger( - deployer.mdict['pki_target_tomcat_conf_instance_id']) - tomcat_instance_subsystems = \ - len(deployer.instance.tomcat_instance_subsystems()) - if tomcat_instance_subsystems == 1: - deployer.systemd.start() - elif tomcat_instance_subsystems > 1: - deployer.systemd.restart() - - # wait for startup - status = deployer.instance.wait_for_startup(60) - if status is None: - config.pki_log.error( - "server failed to restart", - extra=config.PKI_INDENTATION_LEVEL_2) - raise Exception("server failed to restart") - - # Optionally wait for debugger to attach (e. g. - 'eclipse'): - if config.str2bool(deployer.mdict['pki_enable_java_debugger']): - config.wait_to_attach_an_external_java_debugger() - - # Construct PKI Subsystem Configuration Data - data = None - if deployer.mdict['pki_instance_type'] == "Tomcat": - # CA, KRA, OCSP, TKS, or TPS - data = deployer.config_client.construct_pki_configuration_data() - - # Configure the subsystem - deployer.config_client.configure_pki_data( - json.dumps(data, cls=pki.encoder.CustomTypeEncoder)) + instance = pki.server.PKIInstance(deployer.mdict['pki_instance_name']) + instance.load() + + subsystem = instance.get_subsystem(deployer.mdict['pki_subsystem'].lower()) + + password_file = os.path.join( + deployer.mdict['pki_database_path'], "password.txt") + + nssdb = pki.nss.NSSDatabase( + directory=deployer.mdict['pki_database_path'], + password_file=password_file + ) + + if deployer.mdict['pki_subsystem'] == 'CA': + + if not config.str2bool(deployer.mdict['pki_external']): # self-signed CA + + request_file = os.path.join(deployer.mdict['pki_database_path'], 'signing.csr') + + # generate CA certificate request + self.generate_ca_csr(deployer, subsystem, nssdb, request_file, password_file) + + cert_file = os.path.join( + deployer.mdict['pki_database_path'], 'signing.crt') + + # self-sign CA certificate + nssdb.create_self_signed_ca_cert( + issuer_dn=deployer.mdict['pki_ca_signing_subject_dn'], + request_file=request_file, + cert_file=cert_file) + + nickname = deployer.mdict['pki_ca_signing_nickname'] + + # import self-signed CA certificate + self.import_ca_cert(subsystem, nssdb, nickname, cert_file) + + subsystem.save() + + elif not config.str2bool(deployer.mdict['pki_external_step_two']): # external CA step 1 + + request_file = deployer.mdict['pki_external_csr_path'] + + # generate CA certificate request + self.generate_ca_csr(deployer, subsystem, nssdb, request_file, password_file) + + subsystem.save() + + else: # external CA step 2 + + external_signing_nickname = 'caSigningCert External CA' + external_signing_cert_file = deployer.mdict['pki_external_ca_cert_chain_path'] + + # import external CA certificate + nssdb.add_cert( + nickname=external_signing_nickname, + cert_file=external_signing_cert_file, + trust_attributes='CTu,CTu,CTu' + ) + + external_signing_cert = nssdb.get_cert( + nickname=external_signing_nickname, + cert_type='base64' + ) + + subsystem.config['ca.external_signing.nickname'] = external_signing_nickname + subsystem.config['ca.external_signing.tokenname'] = 'internal' + subsystem.config['ca.external_signing.cert'] = external_signing_cert + + signing_nickname = deployer.mdict['pki_ca_signing_nickname'] + signing_cert_file = deployer.mdict['pki_external_ca_cert_path'] + + # import externally-signed CA certificate + self.import_ca_cert(subsystem, nssdb, signing_nickname, signing_cert_file) + + subsystem.save() + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + + if not (external and step_one): + + # Start/Restart this Tomcat PKI Process + # Optionally prepare to enable a java debugger + # (e. g. - 'eclipse'): + if config.str2bool(deployer.mdict['pki_enable_java_debugger']): + config.prepare_for_an_external_java_debugger( + deployer.mdict['pki_target_tomcat_conf_instance_id']) + tomcat_instance_subsystems = \ + len(deployer.instance.tomcat_instance_subsystems()) + if tomcat_instance_subsystems == 1: + deployer.systemd.start() + elif tomcat_instance_subsystems > 1: + deployer.systemd.restart() + + # wait for startup + status = deployer.instance.wait_for_startup(60) + if status is None: + config.pki_log.error( + "server failed to restart", + extra=config.PKI_INDENTATION_LEVEL_2) + raise Exception("server failed to restart") + + # Optionally wait for debugger to attach (e. g. - 'eclipse'): + if config.str2bool(deployer.mdict['pki_enable_java_debugger']): + config.wait_to_attach_an_external_java_debugger() + + # Construct PKI Subsystem Configuration Data + data = None + if deployer.mdict['pki_instance_type'] == "Tomcat": + # CA, KRA, OCSP, TKS, or TPS + data = deployer.config_client.construct_pki_configuration_data() + + # Configure the subsystem + deployer.config_client.configure_pki_data( + json.dumps(data, cls=pki.encoder.CustomTypeEncoder)) return self.rv diff --git a/base/server/python/pki/server/deployment/scriptlets/finalization.py b/base/server/python/pki/server/deployment/scriptlets/finalization.py index 56ddf0219d37dc7258e95464aff9ae925456a1a8..3c4f469aced9eec7928cf2c1a27ac43ebe5e1886 100644 --- a/base/server/python/pki/server/deployment/scriptlets/finalization.py +++ b/base/server/python/pki/server/deployment/scriptlets/finalization.py @@ -67,9 +67,15 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): if len(deployer.instance.tomcat_instance_subsystems()) == 1: # Modify contents of 'serverCertNick.conf' (if necessary) deployer.servercertnick_conf.modify() - # Optionally, programmatically 'restart' the configured PKI instance - if config.str2bool(deployer.mdict['pki_restart_configured_instance']): - deployer.systemd.restart() + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + + if not (external and step_one): + # Optionally, programmatically 'restart' the configured PKI instance + if config.str2bool(deployer.mdict['pki_restart_configured_instance']): + deployer.systemd.restart() + # Optionally, 'purge' the entire temporary client infrastructure # including the client NSS security databases and password files # -- 2.4.3 From ftweedal at redhat.com Thu Nov 12 06:40:10 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 12 Nov 2015 16:40:10 +1000 Subject: [Pki-devel] SPNEGO for Dogtag Message-ID: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> Hi all, Just an update on my investigations of doing SPNEGO authn to Dogtag/Tomcat and a summary of what I think I'll need to do to get it going. Even for a trivial app configuring Tomcat to do SPNEGO is pretty awful. I made some notes[1] on this and I'll turn that into a blog post later, once I have a Realm story to go with it (I'm thinking mod_lookup_identity for Tomcat; more on that below). [1] https://github.com/frasertweedale/notes-redhat/blob/master/tomcat.rst The unfortunate case is that the `Authenticator' interface does not chain, hence we already have our own `SSLAuthenticatorWithFallback' authenticator valve implementation which we use to fall back to Basic authentication when there is no client certificate. I see two general approaches to the "multiple authentication options" problem. a) a "parallel deployment" in a separate where we host the app in parallel at '//spnego/...' or '/spnego//...' and use the `SpnegoAuthenticator' valve. This is a pretty "heavyweight" approach and I believe sessions from one do not flow to the other, and a whole separate CMS application is running. So it does not seem viable. b) `SSLAuthenticatorWithFallback' becomes "SSL or SPNEGO or Basic" valve. The problem here is that protocol-wise there is no indication on initial request whether client can do SPNEGO or not, so it is not clear how server should respond. An example of how this could be resolved is the client providing a '?spnego=1' query param; ugly but it should work, and w.r.t. REST API the client need only do it on /rest/account/login and use their session cookie thereafter, so I don't think it is too onerous. So it looks like (b) but if someone can suggest another approach I'm listening! As for the Realm, the current implementation is a bit unorthodox - we use our own ProxyRealm class and "inject" a PKIRealm into it. I'm not sure why this approach is needed becaues PKIRealm does not need any configuration either on instantiation or afterwards. I think the best approach here would be to use CombinedRealm[2] and add PKIRealm as well as whatever other Realms are needed. For IPA usage I am leaning toward the mod_lookup_identity approach; given principal name look up roles (groups) via D-Bus. [2] https://tomcat.apache.org/tomcat-7.0-doc/config/realm.html#Combined_Realm_-_org.apache.catalina.realm.CombinedRealm Jan: does this approach make sense to you and do you know of any projects to do this with Tomcat already? Ade: what sort of behaviour do you need in the Realm for the Barbian use case? Any and all feedback welcome. Thanks, Fraser From ftweedal at redhat.com Thu Nov 12 09:46:25 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 12 Nov 2015 19:46:25 +1000 Subject: [Pki-devel] SPNEGO for Dogtag In-Reply-To: <20151112073411.GC13367@redhat.com> References: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> <20151112073411.GC13367@redhat.com> Message-ID: <20151112094625.GT5336@dhcp-40-8.bne.redhat.com> On Thu, Nov 12, 2015 at 08:34:11AM +0100, Jan Pazdziora wrote: > On Thu, Nov 12, 2015 at 04:40:10PM +1000, Fraser Tweedale wrote: > > > > Just an update on my investigations of doing SPNEGO authn to > > Dogtag/Tomcat and a summary of what I think I'll need to do to get > > it going. > > > > Even for a trivial app configuring Tomcat to do SPNEGO is pretty > > awful. I made some notes[1] on this and I'll turn that into a blog > > post later, once I have a Realm story to go with it (I'm thinking > > mod_lookup_identity for Tomcat; more on that below). > > > > [1] https://github.com/frasertweedale/notes-redhat/blob/master/tomcat.rst > > > > > > The unfortunate case is that the `Authenticator' interface does not > > chain, hence we already have our own `SSLAuthenticatorWithFallback' > > authenticator valve implementation which we use to fall back to > > Basic authentication when there is no client certificate. > > > > I see two general approaches to the "multiple authentication > > options" problem. > > > > a) a "parallel deployment" in a separate where we host the > > app in parallel at '//spnego/...' or > > '/spnego//...' and use the `SpnegoAuthenticator' valve. > > This is a pretty "heavyweight" approach and I believe sessions from > > one do not flow to the other, and a whole separate CMS application > > is running. So it does not seem viable. > > > > b) `SSLAuthenticatorWithFallback' becomes "SSL or SPNEGO or Basic" > > valve. The problem here is that protocol-wise there is no > > indication on initial request whether client can do SPNEGO or not, > > so it is not clear how server should respond. An example of how > > this could be resolved is the client providing a '?spnego=1' query > > param; ugly but it should work, and w.r.t. REST API the client need > > only do it on /rest/account/login and use their session cookie > > thereafter, so I don't think it is too onerous. > > > > So it looks like (b) but if someone can suggest another approach I'm > > listening! > > > > > > As for the Realm, the current implementation is a bit unorthodox - > > we use our own ProxyRealm class and "inject" a PKIRealm into it. > > I'm not sure why this approach is needed becaues PKIRealm does not > > need any configuration either on instantiation or afterwards. > > > > I think the best approach here would be to use CombinedRealm[2] and > > add PKIRealm as well as whatever other Realms are needed. For IPA > > usage I am leaning toward the mod_lookup_identity approach; given > > principal name look up roles (groups) via D-Bus. > > > > [2] https://tomcat.apache.org/tomcat-7.0-doc/config/realm.html#Combined_Realm_-_org.apache.catalina.realm.CombinedRealm > > > > Jan: does this approach make sense to you and do you know of any > > projects to do this with Tomcat already? > > I'm a bit confused. Do you try to do the authentication in tomcat > or do you try to front-end tomcat with Apache? If you do it in tomcat > itself (like the investigation seems to suggest), what is the role > of mod_lookup_identity here? > No Apache, no mod_lookup_identity. But a Tomcat Realm implementation that does a lookup of principal info via SSSD via D-Bus, like what mod_lookup_identity does for Apache. From ftweedal at redhat.com Thu Nov 12 11:26:19 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 12 Nov 2015 21:26:19 +1000 Subject: [Pki-devel] SPNEGO for Dogtag In-Reply-To: <20151112102010.GE13367@redhat.com> References: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> <20151112073411.GC13367@redhat.com> <20151112094625.GT5336@dhcp-40-8.bne.redhat.com> <20151112102010.GE13367@redhat.com> Message-ID: <20151112112619.GW5336@dhcp-40-8.bne.redhat.com> On Thu, Nov 12, 2015 at 11:20:10AM +0100, Jan Pazdziora wrote: > On Thu, Nov 12, 2015 at 07:46:25PM +1000, Fraser Tweedale wrote: > > On Thu, Nov 12, 2015 at 08:34:11AM +0100, Jan Pazdziora wrote: > > > > > > I'm a bit confused. Do you try to do the authentication in tomcat > > > or do you try to front-end tomcat with Apache? If you do it in tomcat > > > itself (like the investigation seems to suggest), what is the role > > > of mod_lookup_identity here? > > > > No Apache, no mod_lookup_identity. But a Tomcat Realm > > implementation that does a lookup of principal info via SSSD via > > D-Bus, like what mod_lookup_identity does for Apache. > > In general, that is what we tell people not to do. > > The goal is to use external authenticatication and identity operations > in frontend server (Apache) and applications / frameworks consuming the > results. The benefit of this approach is that you don't have to > reimplement things when you say want to support additional protocol > -- hopefully, the platform will do it for you in the form of Apache > modules. The mod_auth_openidc is a prime example -- ideally, any > application that consumes results of external authentication (which > was initially done for example to support Kerberos) gets OpenId Connect > for free, just by reconfiguring the frontend Apache HTTP Server. > In this case I would be implementing a Realm module for Tomcat as an application server (Dogtag to consume the resulting information in our case). It does not always make sense to put Apache in front of Tomcat, and we cannot assume such a setup. The similar argument exists for Nginx; for which a "lookup identity" module was also implemented. I was wondering if you (or others) were aware of any existing implementation for Tomcat. Cheers, Fraser > -- > Jan Pazdziora | adelton at #ipa*, #brno > Senior Principal Software Engineer, Identity Management Engineering, Red Hat From edewata at redhat.com Thu Nov 12 15:32:00 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Thu, 12 Nov 2015 09:32:00 -0600 Subject: [Pki-devel] SPNEGO for Dogtag In-Reply-To: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> References: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> Message-ID: <5644B0F0.70001@redhat.com> On 11/12/2015 12:40 AM, Fraser Tweedale wrote: > Hi all, > > Just an update on my investigations of doing SPNEGO authn to > Dogtag/Tomcat and a summary of what I think I'll need to do to get > it going. > > Even for a trivial app configuring Tomcat to do SPNEGO is pretty > awful. I made some notes[1] on this and I'll turn that into a blog > post later, once I have a Realm story to go with it (I'm thinking > mod_lookup_identity for Tomcat; more on that below). > > [1] https://github.com/frasertweedale/notes-redhat/blob/master/tomcat.rst > > > The unfortunate case is that the `Authenticator' interface does not > chain, hence we already have our own `SSLAuthenticatorWithFallback' > authenticator valve implementation which we use to fall back to > Basic authentication when there is no client certificate. > > I see two general approaches to the "multiple authentication > options" problem. > > a) a "parallel deployment" in a separate where we host the > app in parallel at '//spnego/...' or > '/spnego//...' and use the `SpnegoAuthenticator' valve. > This is a pretty "heavyweight" approach and I believe sessions from > one do not flow to the other, and a whole separate CMS application > is running. So it does not seem viable. This would be the cleanest approach since the client will only talk to the web application that supports the same authentication. So IPA and Barbican will be talking to /ipa/kra and /barbican/kra that is configured with SPNEGO, and CA will be talking to /pki/kra that is configured with basic/client cert authentication. As you know the issue is currently we do not support multiple PKI subsystems of the same type running on the same Tomcat instance, and adding that support is a huge task. However, there might be a relatively simpler alternative. The PKI subsystems probably can be moved out of the web application (WEB-INF/lib) into the Tomcat server itself (common/lib). The web applications will remain exists, but it's only used as a fa?ade to the PKI subsystems running in the background. This way a single PKI subsystem can have multiple web applications customized for different clients. > b) `SSLAuthenticatorWithFallback' becomes "SSL or SPNEGO or Basic" > valve. The problem here is that protocol-wise there is no > indication on initial request whether client can do SPNEGO or not, > so it is not clear how server should respond. An example of how > this could be resolved is the client providing a '?spnego=1' query > param; ugly but it should work, and w.r.t. REST API the client need > only do it on /rest/account/login and use their session cookie > thereafter, so I don't think it is too onerous. > > So it looks like (b) but if someone can suggest another approach I'm > listening! The issue with (b) is it's mixing the users, groups, authenticators, realms, and subsystem-specific objects such as KRA secrets for different clients (IPA, Barbican, and PKI itself) into a single pool, plus the ugly URL (your word, not mine :) ). If the option (a) is doable within the timeframe I'd rather pick that one. It can avoid a number of problems and solve other issues as well. > As for the Realm, the current implementation is a bit unorthodox - > we use our own ProxyRealm class and "inject" a PKIRealm into it. > I'm not sure why this approach is needed becaues PKIRealm does not > need any configuration either on instantiation or afterwards. The ProxyRealm is needed to resolve class dependency issue (which can be solved with option (a)): http://pki.fedoraproject.org/wiki/Authentication Tomcat requires that the classes for the authenticator and the realm must be stored in common/lib. The problem is the PKI classes that do the authentication are stored in the WEB-INF/lib, which is not accessible directly by the realm. To address this problem, a ProxyRealm is created in common/lib to satisfy Tomcat's requirement and a PKIRealm is created in WEB-INF/lib to execute the actual authentication. When the Web application is started, the ProxyRealm will be linked to the PKIRealm (see CMSStartServlet.java below). The ProxyRealm will then forward any incoming authentication request to the PKIRealm. > I think the best approach here would be to use CombinedRealm[2] and > add PKIRealm as well as whatever other Realms are needed. For IPA > usage I am leaning toward the mod_lookup_identity approach; given > principal name look up roles (groups) via D-Bus. The CombinedRealm will not be needed with option (a). > [2] https://tomcat.apache.org/tomcat-7.0-doc/config/realm.html#Combined_Realm_-_org.apache.catalina.realm.CombinedRealm > > Jan: does this approach make sense to you and do you know of any > projects to do this with Tomcat already? > > Ade: what sort of behaviour do you need in the Realm for the Barbian > use case? > > > Any and all feedback welcome. > > Thanks, > Fraser > -- Endi S. Dewata From edewata at redhat.com Thu Nov 12 16:18:38 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Thu, 12 Nov 2015 10:18:38 -0600 Subject: [Pki-devel] [PATCH] 658 Added pki-server subsystem-cert-export command. Message-ID: <5644BBDE.3080809@redhat.com> A new command has been added to export a system certificate, the CSR, and the key. This command can be used to migrate a system certificate into another instance. https://fedorahosted.org/pki/ticket/456 -- Endi S. Dewata -------------- next part -------------- From 6dbe3082fa7c7c2e7b1ef020c51c08568ff46547 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Thu, 12 Nov 2015 00:23:26 +0100 Subject: [PATCH] Added pki-server subsystem-cert-export command. A new command has been added to export a system certificate, the CSR, and the key. This command can be used to migrate a system certificate into another instance. https://fedorahosted.org/pki/ticket/456 --- base/common/python/pki/nss.py | 336 +++++++++++++++++++++++++ base/server/python/pki/server/__init__.py | 6 + base/server/python/pki/server/cli/subsystem.py | 126 ++++++++++ 3 files changed, 468 insertions(+) create mode 100644 base/common/python/pki/nss.py diff --git a/base/common/python/pki/nss.py b/base/common/python/pki/nss.py new file mode 100644 index 0000000000000000000000000000000000000000..f36b771f85eb45641022d6033c23a88aca50757a --- /dev/null +++ b/base/common/python/pki/nss.py @@ -0,0 +1,336 @@ +#!/usr/bin/python +# 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) 2015 Red Hat, Inc. +# All rights reserved. +# + +import base64 +import os +import shutil +import subprocess +import tempfile + + +CSR_HEADER = '-----BEGIN NEW CERTIFICATE REQUEST-----' +CSR_FOOTER = '-----END NEW CERTIFICATE REQUEST-----' + +CERT_HEADER = '-----BEGIN CERTIFICATE-----' +CERT_FOOTER = '-----END CERTIFICATE-----' + + +def convert_data(data, input_format, output_format, header=None, footer=None): + + if input_format == 'base64' and output_format == 'pem': + + # split a single line into multiple lines + lines = [data[i:i+64] for i in range(0, len(data), 64)] + return '%s\n%s\n%s\n' % (header, '\n'.join(lines), footer) + + if input_format == 'pem' and output_format == 'base64': + + # join multiple lines into a single line + lines = [] + for line in data.splitlines(): + line = line.rstrip('\r\n') + if line == header: + continue + if line == footer: + continue + lines.append(line) + + return ''.join(lines) + + raise Exception('Unable to convert data from %s to %s' % (input_format, output_format)) + +def convert_csr(csr_data, input_format, output_format): + + return convert_data(csr_data, input_format, output_format, CSR_HEADER, CSR_FOOTER) + +def convert_cert(cert_data, input_format, output_format): + + return convert_data(cert_data, input_format, output_format, CERT_HEADER, CERT_FOOTER) + + +class NSSDatabase(object): + + def __init__(self, directory, password=None, password_file=None): + self.directory = directory + + self.tmpdir = tempfile.mkdtemp() + + if password: + self.password_file = os.path.join(self.tmpdir, 'password.txt') + with open(self.password_file, 'w') as f: + f.write(password) + + elif password_file: + self.password_file = password_file + + else: + raise Exception('Missing NSS database password') + + def close(self): + shutil.rmtree(self.tmpdir) + + def add_cert(self, + nickname, cert_file, + trust_attributes='u,u,u'): + + subprocess.check_call([ + 'certutil', + '-A', + '-d', self.directory, + '-n', nickname, + '-i', cert_file, + '-t', trust_attributes + ]) + + def modify_cert(self, + nickname, + trust_attributes='u,u,u'): + + subprocess.check_call([ + 'certutil', + '-M', + '-d', self.directory, + '-n', nickname, + '-t', trust_attributes + ]) + + def create_noise(self, noise_file, size=2048): + + subprocess.check_call([ + 'openssl', + 'rand', + '-out', noise_file, + str(size) + ]) + + def create_request(self, + subject_dn, + noise_file, + request_file): + + tmpdir = tempfile.mkdtemp() + + try: + binary_request_file = os.path.join(tmpdir, 'request.bin') + b64_request_file = os.path.join(tmpdir, 'request.b64') + + # generate binary request + subprocess.check_call([ + 'certutil', + '-R', + '-d', self.directory, + '-f', self.password_file, + '-s', subject_dn, + '-z', noise_file, + '-o', binary_request_file + ]) + + # encode binary request in base-64 + subprocess.check_call([ + 'BtoA', binary_request_file, b64_request_file]) + + # read base-64 request + with open(b64_request_file, 'r') as f: + b64_request = f.read() + + # add header and footer + with open(request_file, 'w') as f: + f.write('-----BEGIN NEW CERTIFICATE REQUEST-----\n') + f.write(b64_request) + f.write('-----END NEW CERTIFICATE REQUEST-----\n') + + finally: + shutil.rmtree(tmpdir) + + def create_self_signed_ca_cert(self, + subject_dn, + request_file, + cert_file, + serial='1', + validity=240): + + p = subprocess.Popen([ + 'certutil', + '-C', + '-x', + '-d', self.directory, + '-f', self.password_file, + '-c', subject_dn, + '-a', + '-i', request_file, + '-o', cert_file, + '-m', serial, + '-v', str(validity), + '--keyUsage', 'digitalSignature,nonRepudiation,certSigning,crlSigning,critical', + '-2', + '-3', + '--extSKID', + '--extAIA' + ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + keystroke = '' + + # Is this a CA certificate [y/N]? + keystroke += 'y\n' + + # Enter the path length constraint, enter to skip [<0 for unlimited path]: + keystroke += '\n' + + # Is this a critical extension [y/N]? + keystroke += 'y\n' + + # Enter value for the authKeyID extension [y/N]? + keystroke += 'y\n' + + # TODO: generate SHA1 ID (see APolicyRule.formSHA1KeyId()) + # Enter value for the key identifier fields,enter to omit: + keystroke += '2d:7e:83:37:75:5a:fd:0e:8d:52:a3:70:16:93:36:b8:4a:d6:84:9f\n' + + # Select one of the following general name type: + keystroke += '0\n' + + # Enter value for the authCertSerial field, enter to omit: + keystroke += '\n' + + # Is this a critical extension [y/N]? + keystroke += '\n' + + # TODO: generate SHA1 ID (see APolicyRule.formSHA1KeyId()) + # Adding Subject Key ID extension. + # Enter value for the key identifier fields,enter to omit: + keystroke += '2d:7e:83:37:75:5a:fd:0e:8d:52:a3:70:16:93:36:b8:4a:d6:84:9f\n' + + # Is this a critical extension [y/N]? + keystroke += '\n' + + # Enter access method type for Authority Information Access extension: + keystroke += '2\n' + + # Select one of the following general name type: + keystroke += '7\n' + + # TODO: replace with actual hostname name and port number + # Enter data: + keystroke += 'http://server.example.com:8080/ca/ocsp\n' + + # Select one of the following general name type: + keystroke += '0\n' + + # Add another location to the Authority Information Access extension [y/N] + keystroke += '\n' + + # Is this a critical extension [y/N]? + keystroke += '\n' + + p.communicate(keystroke) + + rc = p.wait() + + if rc: + raise Exception('Failed to generate self-signed CA certificate. RC: %d' + rc) + + def get_cert(self, nickname, output_format='pem'): + + if output_format == 'pem': + output_format_option = '-a' + + elif output_format == 'base64': + output_format_option = '-r' + + else: + raise Exception('Unsupported output format: %s' % output_format) + + cert_data = subprocess.check_output([ + 'certutil', + '-L', + '-d', self.directory, + '-n', nickname, + output_format_option + ]) + + if output_format == 'base64': + cert_data = base64.b64encode(cert_data) + + return cert_data + + def remove_cert(self, nickname): + + subprocess.check_call([ + 'certutil', + '-D', + '-d', self.directory, + '-n', nickname + ]) + + def import_pkcs12(self, pkcs12_file, pkcs12_password=None, pkcs12_password_file=None): + + tmpdir = tempfile.mkdtemp() + + try: + if pkcs12_password: + password_file = os.path.join(tmpdir, 'password.txt') + with open(password_file, 'w') as f: + f.write(pkcs12_password) + + elif pkcs12_password_file: + password_file = pkcs12_password_file + + else: + raise Exception('Missing PKCS #12 password') + + subprocess.check_call([ + 'pk12util', + '-d', self.directory, + '-k', self.password_file, + '-i', pkcs12_file, + '-w', password_file + ]) + + finally: + shutil.rmtree(tmpdir) + + def export_pkcs12(self, pkcs12_file, nickname, pkcs12_password=None, pkcs12_password_file=None): + + tmpdir = tempfile.mkdtemp() + + try: + if pkcs12_password: + password_file = os.path.join(tmpdir, 'password.txt') + with open(password_file, 'w') as f: + f.write(pkcs12_password) + + elif pkcs12_password_file: + password_file = pkcs12_password_file + + else: + raise Exception('Missing PKCS #12 password') + + subprocess.check_call([ + 'pk12util', + '-d', self.directory, + '-k', self.password_file, + '-o', pkcs12_file, + '-w', password_file, + '-n', nickname + ]) + + finally: + shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index 0d522084c0bf210e93599ce09c2d23d0214c4aa7..d55a3691d180ede7dd1731b7490957c816bd8a3b 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -34,6 +34,7 @@ import subprocess import tempfile import pki +import pki.nss INSTANCE_BASE_DIR = '/var/lib/pki' REGISTRY_DIR = '/etc/sysconfig/pki' @@ -327,6 +328,11 @@ class PKIInstance(object): return password + def open_nssdb(self): + return pki.nss.NSSDatabase( + directory=self.nssdb_dir, + password=self.get_password('internal')) + def get_subsystem(self, name): for subsystem in self.subsystems: if name == subsystem.name: diff --git a/base/server/python/pki/server/cli/subsystem.py b/base/server/python/pki/server/cli/subsystem.py index 3b9f9860f3b8b8cc9a3722b019c6d93181da6469..91a50cc38de01c98f43bb22935171f7317dbe886 100644 --- a/base/server/python/pki/server/cli/subsystem.py +++ b/base/server/python/pki/server/cli/subsystem.py @@ -23,11 +23,13 @@ from __future__ import absolute_import from __future__ import print_function import base64 import getopt +import getpass import nss.nss as nss import string import sys import pki.cli +import pki.nss import pki.server @@ -296,6 +298,7 @@ class SubsystemCertCLI(pki.cli.CLI): self.add_module(SubsystemCertFindCLI()) self.add_module(SubsystemCertShowCLI()) + self.add_module(SubsystemCertExportCLI()) self.add_module(SubsystemCertUpdateCLI()) @staticmethod @@ -440,6 +443,129 @@ class SubsystemCertShowCLI(pki.cli.CLI): SubsystemCertCLI.print_subsystem_cert(subsystem_cert) +class SubsystemCertExportCLI(pki.cli.CLI): + + def __init__(self): + super(SubsystemCertExportCLI, self).__init__( + 'export', 'Export subsystem certificate') + + def usage(self): + print('Usage: pki-server subsystem-cert-export [OPTIONS] ') + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --cert-file PEM file to store the certificate.') + print(' --csr-file PEM file to store the CSR.') + print(' --pkcs12-file PKCS #12 file to store the certificate and key.') + print(' --pkcs12-password Password for the PKCS #12 file.') + print(' --pkcs12-password-file 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, args = getopt.gnu_getopt(argv, 'i:v', [ + 'instance=', 'cert-file=', 'csr-file=', + 'pkcs12-file=', 'pkcs12-password=', 'pkcs12-password-file=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.usage() + 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() + sys.exit(1) + + subsystem_name = args[0] + cert_id = args[1] + instance_name = 'pki-tomcat' + cert_file = None + csr_file = None + 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 == '--cert-file': + cert_file = a + + elif o == '--csr-file': + csr_file = 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.usage() + sys.exit(1) + + if not cert_file and not csr_file and not pkcs12_file: + print('ERROR: missing output file') + self.usage() + 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) + + if cert_file: + + cert_data = pki.nss.convert_cert(subsystem_cert['data'], 'base64', 'pem') + with open(cert_file, 'w') as f: + f.write(cert_data) + + if csr_file: + + csr_data = pki.nss.convert_csr(subsystem_cert['request'], 'base64', 'pem') + with open(csr_file, 'w') as f: + f.write(csr_data) + + if pkcs12_file: + + 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, + nickname=subsystem_cert['nickname'], + pkcs12_password=pkcs12_password, + pkcs12_password_file=pkcs12_password_file) + finally: + nssdb.close() + + self.print_message('Exported %s certificate' % cert_id) + + class SubsystemCertUpdateCLI(pki.cli.CLI): def __init__(self): -- 2.4.3 From edewata at redhat.com Fri Nov 13 16:07:29 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Fri, 13 Nov 2015 10:07:29 -0600 Subject: [Pki-devel] [PATCH] 659 Added CLI options to simplify submitting CSR. Message-ID: <56460AC1.5050501@redhat.com> The pki ca-cert-request-submit command has been modified to provide options to specify the profile name and the CSR which will be used to create and populate the request object. This way it's no longer necessary to download the request template and insert the CSR manually. https://fedorahosted.org/pki/ticket/456 Usage: http://pki.fedoraproject.org/wiki/Externaly-Signed_CA_Setup -- Endi S. Dewata -------------- next part -------------- From 8ad8a03cf5e8c54e4ed93ad34e107f9d762ccc0a Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 13 Nov 2015 16:55:43 +0100 Subject: [PATCH] Added CLI options to simplify submitting CSR. The pki ca-cert-request-submit command has been modified to provide options to specify the profile name and the CSR which will be used to create and populate the request object. This way it's no longer necessary to download the request template and insert the CSR manually. https://fedorahosted.org/pki/ticket/456 --- .../cmstools/cert/CertRequestSubmitCLI.java | 146 +++++++++++++++++++-- 1 file changed, 136 insertions(+), 10 deletions(-) diff --git a/base/java-tools/src/com/netscape/cmstools/cert/CertRequestSubmitCLI.java b/base/java-tools/src/com/netscape/cmstools/cert/CertRequestSubmitCLI.java index cec1cff4f2c8167c7c16a3d095963039840b1486..991ab462be4dc15f40d41e3d59acdba0470f9c63 100644 --- a/base/java-tools/src/com/netscape/cmstools/cert/CertRequestSubmitCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/cert/CertRequestSubmitCLI.java @@ -5,9 +5,10 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Scanner; - -import javax.xml.bind.JAXBException; +import java.util.Vector; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; @@ -16,9 +17,13 @@ import org.apache.commons.cli.ParseException; import com.netscape.certsrv.ca.AuthorityID; import com.netscape.certsrv.cert.CertEnrollmentRequest; import com.netscape.certsrv.cert.CertRequestInfos; +import com.netscape.certsrv.profile.ProfileAttribute; +import com.netscape.certsrv.profile.ProfileInput; import com.netscape.cmstools.cli.CLI; import com.netscape.cmstools.cli.MainCLI; +import netscape.ldap.util.DN; +import netscape.ldap.util.RDN; import netscape.security.x509.X500Name; public class CertRequestSubmitCLI extends CLI { @@ -30,11 +35,11 @@ public class CertRequestSubmitCLI extends CLI { this.certCLI = certCLI; Option option = new Option(null, "issuer-id", true, "Authority ID (host authority if omitted)"); - option.setArgName("id"); + option.setArgName("ID"); options.addOption(option); option = new Option(null, "issuer-dn", true, "Authority DN (host authority if omitted)"); - option.setArgName("dn"); + option.setArgName("DN"); options.addOption(option); option = new Option(null, "username", true, "Username for request authentication"); @@ -43,6 +48,22 @@ public class CertRequestSubmitCLI extends CLI { option = new Option(null, "password", false, "Prompt password for request authentication"); options.addOption(option); + + option = new Option(null, "profile", true, "Certificate profile"); + option.setArgName("profile"); + options.addOption(option); + + option = new Option(null, "request-type", true, "Request type (default: pkcs10)"); + option.setArgName("type"); + options.addOption(option); + + option = new Option(null, "csr-file", true, "File containing the CSR"); + option.setArgName("path"); + options.addOption(option); + + option = new Option(null, "subject", true, "Subject DN"); + option.setArgName("DN"); + options.addOption(option); } public void printHelp() { @@ -70,8 +91,17 @@ public class CertRequestSubmitCLI extends CLI { String[] cmdArgs = cmd.getArgs(); - if (cmdArgs.length < 1) { - System.err.println("Error: No filename specified."); + String requestFilename = cmdArgs.length > 0 ? cmdArgs[0] : null; + String profileID = cmd.getOptionValue("profile"); + + if (requestFilename == null && profileID == null) { + System.err.println("Error: Missing request file or profile ID."); + printHelp(); + System.exit(-1); + } + + if (requestFilename != null && profileID != null) { + System.err.println("Error: Request file and profile ID are mutually exclusive."); printHelp(); System.exit(-1); } @@ -106,7 +136,104 @@ public class CertRequestSubmitCLI extends CLI { System.exit(-1); } - CertEnrollmentRequest request = getEnrollmentRequest(cmdArgs[0]); + String requestType = cmd.getOptionValue("request-type"); + + CertEnrollmentRequest request; + if (requestFilename == null) { // if no request file specified, generate new request from profile + + if (verbose) { + System.out.println("Retrieving " + profileID + " profile."); + } + + request = certCLI.certClient.getEnrollmentTemplate(profileID); + + // set default request type for new request + if (requestType == null) requestType = "pkcs10"; + + } else { // otherwise, load request from file + + if (verbose) { + System.out.println("Loading request from " + requestFilename + "."); + } + + String xml = loadFile(requestFilename); + request = CertEnrollmentRequest.fromXML(xml); + } + + if (requestType != null) { + + if (verbose) { + System.out.println("Request type: " + requestType); + } + + for (ProfileInput input : request.getInputs()) { + ProfileAttribute typeAttr = input.getAttribute("cert_request_type"); + if (typeAttr != null) { + typeAttr.setValue(requestType); + } + } + } + + String csrFilename = cmd.getOptionValue("csr-file"); + if (csrFilename != null) { + + String csr = loadFile(csrFilename); + + if (verbose) { + System.out.println("CSR:"); + System.out.println(csr); + } + + for (ProfileInput input : request.getInputs()) { + ProfileAttribute csrAttr = input.getAttribute("cert_request"); + if (csrAttr != null) { + csrAttr.setValue(csr); + } + } + } + + String subjectDN = cmd.getOptionValue("subject"); + if (subjectDN != null) { + DN dn = new DN(subjectDN); + Vector rdns = dn.getRDNs(); + + Map subjectAttributes = new HashMap(); + for (int i=0; i< rdns.size(); i++) { + RDN rdn = (RDN)rdns.elementAt(i); + String type = rdn.getTypes()[0].toLowerCase(); + String value = rdn.getValues()[0]; + subjectAttributes.put(type, value); + } + + ProfileInput sn = request.getInput("Subject Name"); + if (sn != null) { + if (verbose) System.out.println("Subject Name:"); + + for (ProfileAttribute attribute : sn.getAttributes()) { + String name = attribute.getName(); + String value = null; + + if (name.equals("subject")) { + // get the whole subject DN + value = subjectDN; + + } else if (name.startsWith("sn_")) { + // get value from subject DN + value = subjectAttributes.get(name.substring(3)); + + } else { + // unknown attribute, ignore + if (verbose) System.out.println(" - " + name); + continue; + } + + if (value == null) continue; + + if (verbose) System.out.println(" - " + name + ": " + value); + attribute.setValue(value); + } + } + } String certRequestUsername = cmd.getOptionValue("username"); if (certRequestUsername != null) { @@ -124,10 +251,9 @@ public class CertRequestSubmitCLI extends CLI { CertCLI.printCertRequestInfos(cri); } - private CertEnrollmentRequest getEnrollmentRequest(String fileName) throws JAXBException, FileNotFoundException { + private String loadFile(String fileName) throws FileNotFoundException { try (Scanner scanner = new Scanner(new File(fileName))) { - String xml = scanner.useDelimiter("\\A").next(); - return CertEnrollmentRequest.fromXML(xml); + return scanner.useDelimiter("\\A").next(); } } } -- 2.4.3 From edewata at redhat.com Sat Nov 14 05:18:59 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Fri, 13 Nov 2015 23:18:59 -0600 Subject: [Pki-devel] [PATCH] 658 Added pki-server subsystem-cert-export command. In-Reply-To: <5644BBDE.3080809@redhat.com> References: <5644BBDE.3080809@redhat.com> Message-ID: <5646C443.2050206@redhat.com> On 11/12/2015 10:18 AM, Endi Sukma Dewata wrote: > A new command has been added to export a system certificate, the > CSR, and the key. This command can be used to migrate a system > certificate into another instance. > > https://fedorahosted.org/pki/ticket/456 Revised option descriptions. ACKed by jmagne. Pushed to master. -- Endi S. Dewata From edewata at redhat.com Mon Nov 16 17:24:04 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Mon, 16 Nov 2015 11:24:04 -0600 Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <5640FB04.2050706@redhat.com> References: <5640FB04.2050706@redhat.com> Message-ID: <564A1134.2010406@redhat.com> On 11/9/2015 1:59 PM, Endi Sukma Dewata wrote: > The CA certificate request and signing processes have been moved > from the configuration servlet into the deployment scriptlet. This > way the admin will have the option to: > > * generate self-signed CA certificate > * import externally-signed CA certificate > * import existing CA certificate > > before the server is started for the first time. > > https://fedorahosted.org/pki/ticket/456 > > Note: This is a preliminary patch. There are some unfinished works. Attached is the actual patch. -- Endi S. Dewata -------------- next part -------------- >From 01d9557933236ebc3b74a5221cea8ef8cda8a91f Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sat, 7 Nov 2015 00:09:19 +0100 Subject: [PATCH] Added mechanism to import existing CA certificate. The deployment procedure for external CA has been modified such that it generates the CA CSR before starting the server. This allows the same procedure to be used to import CA certificate from an existing server. It also removes the requirement to keep the server running while waiting to get the CSR signed by an external CA. https://fedorahosted.org/pki/ticket/456 --- base/common/python/pki/nss.py | 48 +++++-- .../certsrv/system/ConfigurationRequest.java | 12 ++ .../cms/servlet/csadmin/ConfigurationUtils.java | 87 +++++++++++++ .../dogtagpki/server/rest/SystemConfigService.java | 40 +++++- base/server/etc/default.cfg | 9 +- .../python/pki/server/deployment/pkihelper.py | 71 +++++++---- .../server/deployment/scriptlets/configuration.py | 139 ++++++++++++++++++++- .../server/deployment/scriptlets/finalization.py | 12 +- 8 files changed, 373 insertions(+), 45 deletions(-) diff --git a/base/common/python/pki/nss.py b/base/common/python/pki/nss.py index f36b771f85eb45641022d6033c23a88aca50757a..1330771c08558be98f7c25dc9bd68dd3c6a5c4f4 100644 --- a/base/common/python/pki/nss.py +++ b/base/common/python/pki/nss.py @@ -123,27 +123,59 @@ class NSSDatabase(object): def create_request(self, subject_dn, - noise_file, - request_file): + request_file, + noise_file=None, + token=None, + key_type=None, + key_size=None, + curve=None, + hash_alg=None): tmpdir = tempfile.mkdtemp() try: + if not noise_file: + noise_file = os.path.join(tmpdir, 'noise.bin') + if key_size: + size = key_size + else: + size = 2048 + self.create_noise( + noise_file=noise_file, + size=size) + binary_request_file = os.path.join(tmpdir, 'request.bin') - b64_request_file = os.path.join(tmpdir, 'request.b64') - # generate binary request - subprocess.check_call([ + cmd = [ 'certutil', '-R', '-d', self.directory, '-f', self.password_file, '-s', subject_dn, - '-z', noise_file, - '-o', binary_request_file - ]) + '-o', binary_request_file, + '-z', noise_file + ] + + if token: + cmd.extend(['-h', token]) + + if key_type: + cmd.extend(['-k', key_type]) + + if key_size: + cmd.extend(['-g', str(key_size)]) + + if curve: + cmd.extend(['-q', curve]) + + if hash_alg: + cmd.extend(['-Z', hash_alg]) + + # generate binary request + subprocess.check_call(cmd) # encode binary request in base-64 + b64_request_file = os.path.join(tmpdir, 'request.b64') subprocess.check_call([ 'BtoA', binary_request_file, b64_request_file]) diff --git a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java index 7c6c339f5ddff40018c4d14c97ca63e12e9b9289..8c9da6f373be192a6b5cf99a3cf8cd6ce288c3aa 100644 --- a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java +++ b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java @@ -178,6 +178,9 @@ public class ConfigurationRequest { protected String adminCert; @XmlElement + protected Boolean external; + + @XmlElement protected String standAlone; @XmlElement @@ -754,6 +757,14 @@ public class ConfigurationRequest { this.adminCert = adminCert; } + public Boolean isExternal() { + return external; + } + + public void setExternal(Boolean external) { + this.external = external; + } + public boolean getStandAlone() { return (standAlone != null && standAlone.equalsIgnoreCase("true")); } @@ -945,6 +956,7 @@ public class ConfigurationRequest { ", adminCert=" + adminCert + ", importAdminCert=" + importAdminCert + ", generateServerCert=" + generateServerCert + + ", external=" + external + ", standAlone=" + standAlone + ", stepTwo=" + stepTwo + ", authdbBaseDN=" + authdbBaseDN + diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java index 88118adf8a7535442d8f1f678ce14f6f6ac07e51..5e2936f3d73089090c9b042ed8900fce62177996 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java +++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java @@ -126,6 +126,7 @@ import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.base.ISubsystem; +import com.netscape.certsrv.base.MetaInfo; import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.base.ResourceNotFoundException; import com.netscape.certsrv.ca.ICertificateAuthority; @@ -133,6 +134,8 @@ import com.netscape.certsrv.client.ClientConfig; import com.netscape.certsrv.client.PKIClient; import com.netscape.certsrv.client.PKIConnection; import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.certsrv.dbs.certdb.ICertRecord; +import com.netscape.certsrv.dbs.certdb.ICertificateRepository; import com.netscape.certsrv.dbs.crldb.ICRLIssuingPointRecord; import com.netscape.certsrv.key.KeyData; import com.netscape.certsrv.ldap.ILdapConnFactory; @@ -2248,6 +2251,54 @@ public class ConfigurationUtils { certObj.setCertChain(certChainStr); } + public static KeyPair loadKeyPair(String nickname) throws Exception { + + CMS.debug("ConfigurationUtils: loadKeyPair(" + nickname + ")"); + + CryptoManager cm = CryptoManager.getInstance(); + + X509Certificate cert = cm.findCertByNickname(nickname); + PublicKey publicKey = cert.getPublicKey(); + PrivateKey privateKey = cm.findPrivKeyByCert(cert); + + return new KeyPair(publicKey, privateKey); + } + + public static void storeKeyPair(IConfigStore config, String tag, KeyPair pair) + throws TokenException, EBaseException { + + CMS.debug("ConfigurationUtils: storeKeyPair(" + tag + ")"); + + PublicKey publicKey = pair.getPublic(); + + if (publicKey instanceof RSAPublicKey) { + + RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; + + byte modulus[] = rsaPublicKey.getModulus().toByteArray(); + config.putString(PCERT_PREFIX + tag + ".pubkey.modulus", + CryptoUtil.byte2string(modulus)); + + byte exponent[] = rsaPublicKey.getPublicExponent().toByteArray(); + config.putString(PCERT_PREFIX + tag + ".pubkey.exponent", + CryptoUtil.byte2string(exponent)); + + } else { // ECC + + CMS.debug("ConfigurationUtils: Public key class: " + publicKey.getClass().getName()); + byte encoded[] = publicKey.getEncoded(); + config.putString(PCERT_PREFIX + tag + ".pubkey.encoded", CryptoUtil.byte2string(encoded)); + } + + PrivateKey privateKey = (PrivateKey) pair.getPrivate(); + byte id[] = privateKey.getUniqueID(); + String kid = CryptoUtil.byte2string(id); + config.putString(PCERT_PREFIX + tag + ".privkey.id", kid); + + String keyAlgo = config.getString(PCERT_PREFIX + tag + ".signingalgorithm"); + setSigningAlgorithm(tag, keyAlgo, config); + } + public static void createECCKeyPair(String token, String curveName, IConfigStore config, String ct) throws NoSuchAlgorithmException, NoSuchTokenException, TokenException, CryptoManager.NotInitializedException, EPropertyNotFound, EBaseException { @@ -2812,6 +2863,20 @@ public class ConfigurationUtils { } } + public static void loadCertRequest(IConfigStore config, String tag, Cert cert) throws Exception { + + CMS.debug("ConfigurationUtils.loadCertRequest(" + tag + ")"); + + String subjectDN = config.getString(PCERT_PREFIX + tag + ".dn"); + cert.setDN(subjectDN); + + String subsystem = config.getString(PCERT_PREFIX + tag + ".subsystem"); + String certreq = config.getString(subsystem + "." + tag + ".certreq"); + String formattedCertreq = CryptoUtil.reqFormat(certreq); + + cert.setRequest(formattedCertreq); + } + public static void handleCertRequest(IConfigStore config, String certTag, Cert cert) throws EPropertyNotFound, EBaseException, InvalidKeyException, NotInitializedException, TokenException, NoSuchAlgorithmException, NoSuchProviderException, CertificateException, SignatureException, IOException { @@ -2953,6 +3018,28 @@ public class ConfigurationUtils { return pubk; } + public static void loadCert(Cert cert) throws Exception { + + String tag = cert.getCertTag(); + CMS.debug("ConfigurationUtils: loadCert(" + tag + ")"); + + // load certificate + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate x509Cert = cm.findCertByNickname(cert.getNickname()); + X509CertImpl x509CertImpl = new X509CertImpl(x509Cert.getEncoded()); + + ICertificateAuthority ca = (ICertificateAuthority) CMS.getSubsystem(ICertificateAuthority.ID); + ICertificateRepository cr = ca.getCertificateRepository(); + + BigInteger serialNo = x509Cert.getSerialNumber(); + + MetaInfo meta = new MetaInfo(); + + // create certificate record to reserve the serial number in internal database + ICertRecord record = cr.createCertRecord(serialNo, x509CertImpl, meta); + cr.addCertificateRecord(record); + } + public static int handleCerts(Cert cert) throws IOException, EBaseException, CertificateException, NotInitializedException, TokenException, InvalidKeyException { String certTag = cert.getCertTag(); diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java index a0138681a39baeff272d75408dbee9a74d0529dc..bee6122385fcbb0fda4245f26c6a419c17a81b3f 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java @@ -20,6 +20,7 @@ package org.dogtagpki.server.rest; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; +import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.util.ArrayList; @@ -420,7 +421,13 @@ public class SystemConfigService extends PKIService implements SystemConfigResou } cs.commit(false); - if (!request.getStepTwo()) { + if (request.isExternal() && tag.equals("signing")) { + // load key pair for existing and externally-signed signing cert + CMS.debug("SystemConfigService: loading signing cert key pair"); + KeyPair pair = ConfigurationUtils.loadKeyPair(certData.getNickname()); + ConfigurationUtils.storeKeyPair(cs, tag, pair); + + } else if (!request.getStepTwo()) { if (keytype.equals("ecc")) { String curvename = certData.getKeyCurveName() != null ? certData.getKeyCurveName() : cs.getString("keys.ecc.curve.default"); @@ -443,7 +450,16 @@ public class SystemConfigService extends PKIService implements SystemConfigResou cert.setSubsystem(cs.getString("preop.cert." + tag + ".subsystem")); cert.setType(cs.getString("preop.cert." + tag + ".type")); - if (!request.getStepTwo()) { + if (request.isExternal() + && (tag.equals("signing") || tag.equals("external_signing"))) { + + // update configuration for existing and externally-signed signing certificates + String certStr = cs.getString("ca." + tag + ".cert" ); + cert.setCert(certStr); + CMS.debug("SystemConfigService: certificate " + tag + ": " + certStr); + ConfigurationUtils.updateConfig(cs, tag); + + } else if (!request.getStepTwo()) { ConfigurationUtils.configCert(null, null, null, cert); } else { @@ -465,8 +481,17 @@ public class SystemConfigService extends PKIService implements SystemConfigResou CMS.debug("Step 2: certStr for '" + tag + "' is " + certStr); } - // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) - if (request.getStandAlone()) { + if (request.isExternal() + && (tag.equals("signing") || tag.equals("external_signing"))) { + + CMS.debug("SystemConfigService: Loading cert request for " + tag + " cert"); + ConfigurationUtils.loadCertRequest(cs, tag, cert); + + CMS.debug("SystemConfigService: Loading cert " + tag); + ConfigurationUtils.loadCert(cert); + + } else if (request.getStandAlone()) { + // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) if (!request.getStepTwo()) { // Stand-alone PKI (Step 1) ConfigurationUtils.handleCertRequest(cs, tag, cert); @@ -489,6 +514,13 @@ public class SystemConfigService extends PKIService implements SystemConfigResou ConfigurationUtils.updateCloneConfig(); } + if (request.isExternal() && (tag.equals("signing") || tag.equals("external_signing"))) { + CMS.debug("SystemConfigService: External CA has signing cert"); + hasSigningCert.setValue(true); + certs.add(cert); + continue; + } + // to determine if we have the signing cert when using an external ca // this will only execute on a ca or stand-alone pki String b64 = certData.getCert(); diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index ddd2d83670dd191f38b8905cdea03172bbbc1e95..5b5224bdcf479271f701733be454e815feec40e6 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -22,6 +22,7 @@ sensitive_parameters= pki_client_pkcs12_password pki_clone_pkcs12_password pki_ds_password + pki_external_pkcs12_password pki_one_time_pin pki_pin pki_replication_password @@ -365,10 +366,12 @@ pki_req_ext_add=False pki_req_ext_oid=1.3.6.1.4.1.311.20.2 pki_req_ext_critical=False pki_req_ext_data=1E0A00530075006200430041 -pki_external_csr_path=%(pki_instance_configuration_path)s/ca_signing.csr +pki_external_csr_path= pki_external_step_two=False -pki_external_ca_cert_chain_path=%(pki_instance_configuration_path)s/external_ca_chain.cert -pki_external_ca_cert_path=%(pki_instance_configuration_path)s/external_ca.cert +pki_external_ca_cert_chain_path= +pki_external_ca_cert_path= +pki_external_pkcs12= +pki_external_pkcs12_password= pki_import_admin_cert=False pki_ocsp_signing_key_algorithm=SHA256withRSA pki_ocsp_signing_key_size=2048 diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py index 61f04d215ba3eba48b7e18733fd58b29555ced83..ca54c58b6e87a5e6378cf6dfb058231bd97cf2d3 100644 --- a/base/server/python/pki/server/deployment/pkihelper.py +++ b/base/server/python/pki/server/deployment/pkihelper.py @@ -757,8 +757,7 @@ class ConfigurationFile: # External CA if not self.external_step_two: # External CA (Step 1) - self.confirm_data_exists("pki_external_csr_path") - self.confirm_missing_file("pki_external_csr_path") + # The pki_external_csr_path is optional. # generic extension support in CSR - for external CA if self.add_req_ext: self.confirm_data_exists("pki_req_ext_oid") @@ -766,10 +765,9 @@ class ConfigurationFile: self.confirm_data_exists("pki_req_ext_data") else: # External CA (Step 2) - self.confirm_data_exists("pki_external_ca_cert_chain_path") - self.confirm_file_exists("pki_external_ca_cert_chain_path") - self.confirm_data_exists("pki_external_ca_cert_path") - self.confirm_file_exists("pki_external_ca_cert_path") + # The pki_external_ca_cert_chain_path and + # pki_external_ca_cert_path are optional. + pass elif not self.skip_configuration and self.standalone: if not self.external_step_two: # Stand-alone PKI Admin CSR (Step 1) @@ -3813,17 +3811,7 @@ class ConfigClient: if not isinstance(certs, list): certs = [certs] for cdata in certs: - if (self.subsystem == "CA" and self.external and - not self.external_step_two): - # External CA (Step 1) - if cdata['tag'].lower() == "signing": - # Save 'External CA Signing Certificate' CSR (Step 1) - self.save_system_csr( - cdata['request'], - log.PKI_CONFIG_EXTERNAL_CSR_SAVE, - self.mdict['pki_external_csr_path']) - return - elif self.standalone and not self.external_step_two: + if self.standalone and not self.external_step_two: # Stand-alone PKI (Step 1) if cdata['tag'].lower() == "audit_signing": # Save Stand-alone PKI 'Audit Signing Certificate' CSR @@ -3991,8 +3979,17 @@ class ConfigClient: data.token = self.mdict['pki_token_name'] data.tokenPassword = self.mdict['pki_token_password'] data.subsystemName = self.mdict['pki_subsystem_name'] + + data.external = self.external data.standAlone = self.standalone - data.stepTwo = self.external_step_two + + if self.standalone: + # standalone installation uses two-step process (ticket #1698) + data.stepTwo = self.external_step_two + + else: + # other installations use only one step in the configuration servlet + data.stepTwo = False # Cloning parameters if self.mdict['pki_instance_type'] == "Tomcat": @@ -4122,25 +4119,47 @@ class ConfigClient: self.mdict['pki_req_ext_critical'] cert1.req_ext_data = \ self.mdict['pki_req_ext_data'] - if self.external_step_two: - # External CA (Step 2) or Stand-alone PKI (Step 2) - if not self.subsystem == "CA": - # Stand-alone PKI (Step 2) - cert1 = pki.system.SystemCertData() - cert1.tag = self.mdict['pki_ca_signing_tag'] - # Load the External CA or Stand-alone PKI + + if self.external and self.external_step_two: + + # If specified, load the externally-signed CA cert + if os.path.isfile(self.mdict['pki_external_ca_cert_path']): + self.load_system_cert( + cert1, + log.PKI_CONFIG_EXTERNAL_CA_LOAD, + self.mdict['pki_external_ca_cert_path']) + + # If specified, load the external CA cert chain + if os.path.isfile(self.mdict['pki_external_ca_cert_chain_path']): + self.load_system_cert_chain( + cert1, + log.PKI_CONFIG_EXTERNAL_CA_CHAIN_LOAD, + self.mdict['pki_external_ca_cert_chain_path']) + + systemCerts.append(cert1) + + elif self.standalone and self.external_step_two: + + # Stand-alone PKI (Step 2) + cert1 = pki.system.SystemCertData() + cert1.tag = self.mdict['pki_ca_signing_tag'] + + # Load the stand-alone PKI # 'External CA Signing Certificate' (Step 2) self.load_system_cert( cert1, log.PKI_CONFIG_EXTERNAL_CA_LOAD, self.mdict['pki_external_ca_cert_path']) - # Load the External CA or Stand-alone PKI + + # Load the stand-alone PKI # 'External CA Signing Certificate Chain' (Step 2) self.load_system_cert_chain( cert1, log.PKI_CONFIG_EXTERNAL_CA_CHAIN_LOAD, self.mdict['pki_external_ca_cert_chain_path']) + systemCerts.append(cert1) + elif self.subsystem == "CA": # PKI CA or Subordinate CA systemCerts.append(cert1) diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py index c6e89023560fc92cb9bd451d9b7f05818807da8a..bed19d9bfee838c44d32ff9f42df24a371e0671a 100644 --- a/base/server/python/pki/server/deployment/scriptlets/configuration.py +++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py @@ -21,13 +21,19 @@ from __future__ import absolute_import import json +import os +import re # PKI Deployment Imports from .. import pkiconfig as config from .. import pkimessages as log from .. import pkiscriptlet -import pki.system + import pki.encoder +import pki.nss +import pki.server +import pki.system +import pki.util # PKI Deployment Configuration Scriptlet @@ -81,6 +87,137 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_client_secmod_database'], password_file=deployer.mdict['pki_client_password_conf']) + instance = pki.server.PKIInstance(deployer.mdict['pki_instance_name']) + instance.load() + + subsystem = instance.get_subsystem(deployer.mdict['pki_subsystem'].lower()) + + nssdb = instance.open_nssdb() + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + step_two = not step_one + + try: + if external and step_one: # external CA step 1 + + key_type = deployer.mdict['pki_ca_signing_key_type'] + + if key_type == 'rsa': + key_size = int(deployer.mdict['pki_ca_signing_key_size']) + curve = None + + m = re.match(r'(.*)withRSA', deployer.mdict['pki_ca_signing_key_algorithm']) + if not m: + raise Exception('Invalid key algorithm: %s' % deployer.mdict['pki_ca_signing_key_algorithm']) + hash_alg = m.group(1) + + elif key_type == 'ec' or key_type == 'ecc': + key_type = 'ec' + key_size = None + curve = deployer.mdict['pki_ca_signing_key_size'] + + m = re.match(r'(.*)withEC', deployer.mdict['pki_ca_signing_key_algorithm']) + if not m: + raise Exception('Invalid key algorithm: %s' % deployer.mdict['pki_ca_signing_key_algorithm']) + hash_alg = m.group(1) + + else: + raise Exception('Invalid key type: %s' % key_type) + + token = deployer.mdict['pki_ca_signing_token'] + if token == 'Internal Key Storage Token': + token = None + + # If filename specified, generate CA cert request and + # import it into CS.cfg. + request_file = deployer.mdict['pki_external_csr_path'] + if request_file: + nssdb.create_request( + subject_dn=deployer.mdict['pki_ca_signing_subject_dn'], + request_file=request_file, + token=token, + key_type=key_type, + key_size=key_size, + curve=curve, + hash_alg=hash_alg) + with open(request_file) as f: + signing_csr = f.read() + signing_csr = pki.nss.convert_csr(signing_csr, 'pem', 'base64') + subsystem.config['ca.signing.certreq'] = signing_csr + + subsystem.save() + + elif external and step_two: # external CA step 2 + + # If specified, import existing CA cert request into CS.cfg. + request_file = deployer.mdict['pki_external_csr_path'] + if os.path.isfile(request_file): + with open(request_file) as f: + signing_csr = f.read() + signing_csr = pki.nss.convert_csr(signing_csr, 'pem', 'base64') + subsystem.config['ca.signing.certreq'] = signing_csr + + # If specified, import external CA cert into NSS database. + external_ca_nickname = 'caSigningCert External CA' + external_ca_cert_chain_file = deployer.mdict['pki_external_ca_cert_chain_path'] + if os.path.isfile(external_ca_cert_chain_file): + nssdb.add_cert( + nickname=external_ca_nickname, + cert_file=external_ca_cert_chain_file, + trust_attributes='CTu,CTu,CTu') + + # Export external CA cert from NSS database and import it into CS.cfg. + external_signing_cert = nssdb.get_cert( + nickname=external_ca_nickname, + output_format='base64') + subsystem.config['ca.external_signing.nickname'] = external_ca_nickname + subsystem.config['ca.external_signing.tokenname'] = deployer.mdict['pki_ca_signing_token'] + subsystem.config['ca.external_signing.cert'] = external_signing_cert + + # If specified, import externally-signed CA cert in NSS database. + signing_nickname = deployer.mdict['pki_ca_signing_nickname'] + signing_cert_file = deployer.mdict['pki_external_ca_cert_path'] + if os.path.isfile(signing_cert_file): + nssdb.add_cert( + nickname=signing_nickname, + cert_file=signing_cert_file, + trust_attributes='CTu,CTu,CTu') + + # If specified, import CA cert and key from PKCS #12 file into NSS database. + pkcs12_file = deployer.mdict['pki_external_pkcs12'] + if os.path.isfile(pkcs12_file): + pkcs12_password = deployer.mdict['pki_external_pkcs12_password'] + nssdb.import_pkcs12(pkcs12_file, pkcs12_password) + + # Export CA cert from NSS database and import it into CS.cfg. + signing_cert_data = nssdb.get_cert( + nickname=signing_nickname, + output_format='base64') + subsystem.config['ca.signing.nickname'] = signing_nickname + subsystem.config['ca.signing.tokenname'] = deployer.mdict['pki_ca_signing_token'] + subsystem.config['ca.signing.cert'] = signing_cert_data + subsystem.config['ca.signing.cacertnickname'] = signing_nickname + subsystem.config['ca.signing.defaultSigningAlgorithm'] = deployer.mdict['pki_ca_signing_signing_algorithm'] + + subsystem.save() + + else: # self-signed CA + + # To be implemented in ticket #1692. + + # Generate CA cert request. + # Self sign CA cert. + # Import self-signed CA cert into NSS database. + + pass + + finally: + nssdb.close() + + if external and step_one: + return self.rv + # Start/Restart this Tomcat PKI Process # Optionally prepare to enable a java debugger # (e. g. - 'eclipse'): diff --git a/base/server/python/pki/server/deployment/scriptlets/finalization.py b/base/server/python/pki/server/deployment/scriptlets/finalization.py index 56ddf0219d37dc7258e95464aff9ae925456a1a8..3c4f469aced9eec7928cf2c1a27ac43ebe5e1886 100644 --- a/base/server/python/pki/server/deployment/scriptlets/finalization.py +++ b/base/server/python/pki/server/deployment/scriptlets/finalization.py @@ -67,9 +67,15 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): if len(deployer.instance.tomcat_instance_subsystems()) == 1: # Modify contents of 'serverCertNick.conf' (if necessary) deployer.servercertnick_conf.modify() - # Optionally, programmatically 'restart' the configured PKI instance - if config.str2bool(deployer.mdict['pki_restart_configured_instance']): - deployer.systemd.restart() + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + + if not (external and step_one): + # Optionally, programmatically 'restart' the configured PKI instance + if config.str2bool(deployer.mdict['pki_restart_configured_instance']): + deployer.systemd.restart() + # Optionally, 'purge' the entire temporary client infrastructure # including the client NSS security databases and password files # -- 2.4.3 From edewata at redhat.com Mon Nov 16 23:11:43 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Mon, 16 Nov 2015 17:11:43 -0600 Subject: [Pki-devel] [PATCH] 659 Added CLI options to simplify submitting CSR. In-Reply-To: <56460AC1.5050501@redhat.com> References: <56460AC1.5050501@redhat.com> Message-ID: <564A62AF.6090203@redhat.com> On 11/13/2015 10:07 AM, Endi Sukma Dewata wrote: > The pki ca-cert-request-submit command has been modified to > provide options to specify the profile name and the CSR which > will be used to create and populate the request object. This > way it's no longer necessary to download the request template > and insert the CSR manually. > > https://fedorahosted.org/pki/ticket/456 > > Usage: http://pki.fedoraproject.org/wiki/Externaly-Signed_CA_Setup ACKed by alee. Pushed to master. Man page will be updated in a separate patch. -- Endi S. Dewata From cfu at redhat.com Tue Nov 17 01:48:38 2015 From: cfu at redhat.com (Christina Fu) Date: Mon, 16 Nov 2015 17:48:38 -0800 Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <564A1134.2010406@redhat.com> References: <5640FB04.2050706@redhat.com> <564A1134.2010406@redhat.com> Message-ID: <564A8776.9050600@redhat.com> This is what I have so far. Just a few comments on the overall logic. I'm not making any Python coding-specific comments. 1 in base/server/python/pki/server/deployment/scriptlets/configuration.py doesn't this just add the leaf cert rather than the whole chain? In other words, if your chain contains 2 or more certs, only the leaf subca cert is added, isn't it? + nssdb.add_cert( + nickname=external_ca_nickname, + cert_file=external_ca_cert_chain_file, + trust_attributes='CTu,CTu,CTu') 2 Also in the same file + # If specified, import externally-signed CA cert in NSS database. ... Shouldn't there be a case when the externally signed ca keys were generated on the hsm, you'd then need to import the issued externally signed ca cert into the hsm db as well? 3 base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java I"m not seeing the following method being called, yet the getExternal() is being called...did I miss something? + public void setExternal(Boolean external) { + this.external = external; + } 4. base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java + public static void loadCert(Cert cert) throws Exception { ... + // create certificate record to reserve the serial number in internal database + ICertRecord record = cr.createCertRecord(serialNo, x509CertImpl, meta); + cr.addCertificateRecord(record); In case of an externally signed ca or existing ca, why would you need to reserve the serial number or even add in the certificate repository? 5. Finally, please add comments to explain the cases for clarification... such as "stand-alone v.s. external; step 1, step 2, etc." For example, it seems the "external" could imply "existing" as well in terms of ca cert, you might want to put in comment. Christina On 11/16/2015 09:24 AM, Endi Sukma Dewata wrote: > On 11/9/2015 1:59 PM, Endi Sukma Dewata wrote: >> The CA certificate request and signing processes have been moved >> from the configuration servlet into the deployment scriptlet. This >> way the admin will have the option to: >> >> * generate self-signed CA certificate >> * import externally-signed CA certificate >> * import existing CA certificate >> >> before the server is started for the first time. >> >> https://fedorahosted.org/pki/ticket/456 >> >> Note: This is a preliminary patch. There are some unfinished works. > > Attached is the actual patch. > > > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From edewata at redhat.com Tue Nov 17 16:57:08 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Tue, 17 Nov 2015 10:57:08 -0600 Subject: [Pki-devel] [PATCH] 660 Updated pki-cert and pki-server-subsystem man pages. Message-ID: <564B5C64.908@redhat.com> The pki-cert and pki-server-subsystem man pages have been updated to include recent changes. https://fedorahosted.org/pki/ticket/456 -- Endi S. Dewata -------------- next part -------------- From 41eb99982e4f9c764480d58ef6b30186a201093d Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Tue, 17 Nov 2015 05:08:51 +0100 Subject: [PATCH] Updated pki-cert and pki-server-subsystem man pages. The pki-cert and pki-server-subsystem man pages have been updated to include recent changes. https://fedorahosted.org/pki/ticket/456 --- base/java-tools/man/man1/pki-cert.1 | 23 ++++++++++++++++------- base/server/man/man8/pki-server-subsystem.8 | 26 +++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/base/java-tools/man/man1/pki-cert.1 b/base/java-tools/man/man1/pki-cert.1 index ffa1fea5d0a29a5b425b1fcaa497e8b831251f7f..7ece1ad7bfc277a4093acdee9592d8671b00b6bd 100644 --- a/base/java-tools/man/man1/pki-cert.1 +++ b/base/java-tools/man/man1/pki-cert.1 @@ -191,23 +191,32 @@ To release a certificate that has been placed on hold: .B pki ca-cert-release-hold .SS Certificate Requests -To request a certificate, first generate a certificate request in PKCS #10 or CRMF, and store this request in the XML template file, of the profile type the request relates to. -The list of profiles can be viewed using the CLI command: +To request a certificate, first generate a certificate signing request (CSR), +then submit it with a certificate profile. The list of available profiles can +be viewed using the following command: .B pki ca-cert-request-profile-find -The XML template file for a profile type can be created by calling the ca-cert-request-profile-show CLI command. For example: +To generate a CSR, use the certutil, PKCS10Client, or +CRMFPopClient, and store it into a file. -\fBpki ca-cert-request-profile-show \-\-output \fP +Basic requests can be submitted using the following command: -will store the XML template of the request in the specified output file. +.B pki ca-cert-request-submit --profile --request-type --csr-file --subject -Then, fill in the values in the XML file and submit the request for review. This can be done without authentication. +To submit more advanced requests, download a template of the request file for +a particular profile using the following command: + +.B pki ca-cert-request-profile-show \-\-output + +Then, edit the request file, fill in the input attributes required by the +profile, and submit the request using the following command: .B pki ca-cert-request-submit -Then, an agent needs to review the request by running the following command: +Depending on the profile, an agent may need to review the request by running +the following command: .B pki ca-cert-request-review --file diff --git a/base/server/man/man8/pki-server-subsystem.8 b/base/server/man/man8/pki-server-subsystem.8 index 04d57f5ac21ca488ca0d1b6311e73fb143d6d784..719982c51262c0d224d6e991788ab4cc655f9d71 100644 --- a/base/server/man/man8/pki-server-subsystem.8 +++ b/base/server/man/man8/pki-server-subsystem.8 @@ -24,6 +24,10 @@ pki-server subsystem \- Command-Line Interface for managing Certificate System s \fBpki-server [CLI options] subsystem-show\fR -i \fBpki-server [CLI options] subsystem-enable\fR -i \fBpki-server [CLI options] subsystem-disable\fR -i +\fBpki-server [CLI options] subsystem-cert-find\fR -i +\fBpki-server [CLI options] subsystem-cert-show\fR -i +\fBpki-server [CLI options] subsystem-cert-export\fR -i +\fBpki-server [CLI options] subsystem-cert-update\fR -i .fi .SH DESCRIPTION @@ -53,7 +57,7 @@ This command is to list subsystems within a specific instance. .PP \fBpki-server [CLI options] subsystem-show\fR -i .RS 4 -This command is to view a details about a particular subsystem. +This command is to view the details about a particular subsystem. .RE .PP \fBpki-server [CLI options] subsystem-enable\fR -i @@ -78,6 +82,26 @@ through the web interfaces. This is useful when specific subsystems need to be made inaccessible for maintenance as Apache Tomcat allows web applications to be deployed/undeployed while the instance is still running (hot deployment). .RE +.PP +\fBpki-server [CLI options] subsystem-cert-find\fR -i +.RS 4 +This command is to list system certificates in a particular subsystem. +.RE +.PP +\fBpki-server [CLI options] subsystem-cert-show\fR -i +.RS 4 +This command is to view the details about a system certificate in a particular subsystem. +.RE +.PP +\fBpki-server [CLI options] subsystem-cert-export\fR -i +.RS 4 +This command is to export a system certificate in a particular subsystem. +.RE +.PP +\fBpki-server [CLI options] subsystem-cert-update\fR -i +.RS 4 +This command is to update a system certificate in a particular subsystem. +.RE .SH OPTIONS The CLI options are described in \fBpki-server\fR(8). -- 2.4.3 From ftweedal at redhat.com Wed Nov 18 06:01:18 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Wed, 18 Nov 2015 16:01:18 +1000 Subject: [Pki-devel] [Freeipa-users] Unable to communicate with CMS (Service Unavailable) (Solved) In-Reply-To: <5645C2C0.7090806@redhat.com> References: <5645C2C0.7090806@redhat.com> Message-ID: <20151118060117.GF5336@dhcp-40-8.bne.redhat.com> On Fri, Nov 13, 2015 at 12:00:16PM +0100, Martin Kosek wrote: > On 11/13/2015 11:14 AM, Terry John wrote: > >>On 11/12/2015 04:51 PM, Terry John wrote: > >>>I got a core dump of certmonger failing user abrt but it's huge. Is there any particular part that would be useful. > > > >>CCing Nalin and David for the core dump. More below. > > > >>On 11/12/2015 02:17 PM, Terry John wrote: > >>>>I had a working freeipa setup on a CentOS release 6.7 machine. All was well until I did a yum update. Now I have multiple issue apparently based around the CMS (Service Unavailable) issue. > >>>>My current version of ipa-server is 3.0.0-47 Certmonger crashes with > >>>>a segmentation fault at boot time and crashes every time I try to restart it when ipa is running. > >> > > > > > >>>>># ipa cert-status > >>>>>Request id: 20140417164153 > >>>>>ipa: ERROR: Certificate operation cannot be completed: Unable to > >>>>>communicate with CMS (Service Unavailable) # service certmonger > >>>>>status certmonger (pid 3030) is running... > >>> > >>>>It looks like PKI cannot be contacted. I would recommend checking /var/log/httpd/error_log, it may have more details. I would also recommend checking "ipa cert-show 1", it will probably fail with the same bug. > >>>Yes ipa cert-show 1 does show the same thing # ipa cert-show 1 > >>>ipa: ERROR: Certificate operation cannot be completed: Unable to > >>>communicate with CMS (Service Unavailable) > >>> > >>>>Next steps may include checking that dogtag service really runs, there is no SELinux AVC. If neither of this helps, you can check PKI logs /var/log/pki... to see what went wrong. > >>>I'm pretty certain the dogtag service is not running > > > >Then you have your lucky winner! :-) > > > >>>>Some pointers to logs are for example here: > >>>>http://www.freeipa.org/page/Troubleshooting#Server_Installation > >> > >>>/var/log/pki-ca/catalina.out contains the lines at boot time: > > > > > >>>SEVERE: Error deploying web application directory ca > >>>java.lang.UnsupportedClassVersionError: com/netscape/cms/servlet/filter/AgentRequestFilter : Unsupported major.minor version 51.0 (unable to load class com.netscape.cms.servlet.filter.AgentRequestFilter) at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappC> lassLoader.java:2334).... lots of traceback > >>> > >>>/var/log/pki-ca/system is empty > >>>/var/log/pki-ca/debug has nothing new for 2 days > > > >>CCing Fraser. This is a wild guess, but maybe you updated your java to java-1.8.0-openjdk? PKI does not work on it on RHEL/CentOS: > > > >>https://bugzilla.redhat.com/show_bug.cgi?id=1262516 > > > >>java would need to be switched with "alternate" to pre-1.8.0 version if this is the case. > > > >The java version was the problem. > > Good! Fraser, can we improve anything in pki-core, so that wrong java > version issue like this one does not occur? IIRC, pki-core in RHEL-6.x was > updated to somehow deal with java 1.8.0 (conflict), not sure if lower > versions are also covered. > AFAICT there is no such protection. It seems to be more of an unspoken "don't do that". I guess the right approach when an unsupported alternative is selected is to explicitly use a supported one. I'm not sure what's involved in making that change or whether it is worth the effort. Adding pki-devel for comment from those with packaging experience. Cheers, Fraser > >Luckily I have a java expert to hand and explained that major.minor version 51.0 corresponds to java 7 > >http://stackoverflow.com/questions/9170832/list-of-java-class-file-format-major-version-numbers > >When I did > ># ps ax | grep java I got" > >1460 ? Sl 1:21 /usr/java/default/bin/java -Djavax.sql.Da....... > ># /usr/java/default/bin/java -version > >java version "1.6.0_31" > >Java(TM) SE Runtime Environment (build 1.6.0_31-b04) > >Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode) > > > >I have both java-1.6.0-openjdk and java-1.7.0-openjdk installed but the /usr/java/default/bin is all from java-1.6.0-openjdk > > > >I have renamed /usr/java/default/bin/java to javaold and done > ># ln -s /usr/bin/java /usr/java/default/bin/java > ># /usr/java/default/bin/java -version > >java version "1.7.0_91" > >OpenJDK Runtime Environment (rhel-2.6.2.2.el6_7-x86_64 u91-b00) > >OpenJDK 64-Bit Server VM (build 24.91-b01, mixed mode) > > This may work, but looks a bit hacky. I think the right way is to use > "alternate" program I mentioned earlier to let you choose the right version > of the java executable and/or libraries. > > >After a reboot FreeIPA works properly which is great but I'm wondering if there is a better fix though since all the other executables in are from the 1.6 version. I can't find a corresponding location for 1.7 executables. > > The "alternate" approach should "just work". I am glad you made the instance > working again! > > > > >Thanks very much > > > > > >The Manheim group of companies within the UK comprises: Manheim Europe Limited (registered number: 03183918), Manheim Auctions Limited (registered number: 00448761), Manheim Retail Services Limited (registered number: 02838588), Motors.co.uk Limited (registered number: 05975777), Real Time Communications Limited (registered number: 04277845) and Complete Automotive Solutions Limited (registered number: 05302535). Each of these companies is registered in England and Wales with the registered office address of Central House, Leeds Road, Rothwell, Leeds LS26 0JE. The Manheim group of companies operates under various brand/trading names including Manheim Inspection Services, Manheim Auctions, Manheim Direct, Manheim De-fleet and Manheim Aftersales Solutions. > > > >V:0CF72C13B2AC > > > > > From ftweedal at redhat.com Mon Nov 23 06:04:25 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Mon, 23 Nov 2015 16:04:25 +1000 Subject: [Pki-devel] [PATCH] 0056 Avoid superfluous ConfigStore commit during profile creation Message-ID: <20151123060425.GN5336@dhcp-40-8.bne.redhat.com> The attached patch is a drive-by improvement during analysis of https://fedorahosted.org/pki/ticket/1700. Thanks, Fraser -------------- next part -------------- From 8436f33d84e1dc3eb8fbefa8e5f174511547afbc Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Fri, 20 Nov 2015 16:15:06 +1100 Subject: [PATCH] Avoid superfluous ConfigStore commit during profile creation Part of: https://fedorahosted.org/pki/ticket/1700 --- base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java b/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java index a1dba8064314cd36179715272c80f1da2cbc6244..488dd5ab960fd45fa3dad0dd83398b4317f2cb4f 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java @@ -605,9 +605,8 @@ public class ProfileService extends PKIService implements ProfileResource { // no error thrown, proceed with profile creation profile = ps.createProfile(profileId, classId, className); - profile.getConfigStore().commit(false); profile.getConfigStore().load(new ByteArrayInputStream(data)); - ps.disableProfile(profileId); + ps.disableProfile(profileId); // also commits auditProfileChange( ScopeDef.SC_PROFILE_RULES, -- 2.4.3 From cheimes at redhat.com Mon Nov 23 11:07:28 2015 From: cheimes at redhat.com (Christian Heimes) Date: Mon, 23 Nov 2015 12:07:28 +0100 Subject: [Pki-devel] [PATCH 0042] Fix escaping of password fields to prevent interpolation Message-ID: <5652F370.8090606@redhat.com> Some password and pin fields are missing from the no_interpolation list. One entry is misspelled. A '%' in password field such as pki_clone_pkcs12_password causes an installation error. https://fedorahosted.org/pki/ticket/1703 https://bugzilla.redhat.com/show_bug.cgi?id=1283631 -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 455 bytes Desc: OpenPGP digital signature URL: From cheimes at redhat.com Mon Nov 23 11:09:48 2015 From: cheimes at redhat.com (Christian Heimes) Date: Mon, 23 Nov 2015 12:09:48 +0100 Subject: [Pki-devel] [PATCH 0042] Fix escaping of password fields to prevent interpolation In-Reply-To: <5652F370.8090606@redhat.com> References: <5652F370.8090606@redhat.com> Message-ID: <5652F3FC.7040200@redhat.com> On 2015-11-23 12:07, Christian Heimes wrote: > Some password and pin fields are missing from the no_interpolation list. > One entry is misspelled. A '%' in password field such as > pki_clone_pkcs12_password causes an installation error. > > https://fedorahosted.org/pki/ticket/1703 > https://bugzilla.redhat.com/show_bug.cgi?id=1283631 And here is the patch file. -------------- next part -------------- A non-text attachment was scrubbed... Name: pki-cheimes-0042-Fix-escaping-of-password-fields-to-prevent-interpola.patch Type: text/x-patch Size: 1794 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 455 bytes Desc: OpenPGP digital signature URL: From edewata at redhat.com Mon Nov 23 19:54:20 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Mon, 23 Nov 2015 13:54:20 -0600 Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <564A8776.9050600@redhat.com> References: <5640FB04.2050706@redhat.com> <564A1134.2010406@redhat.com> <564A8776.9050600@redhat.com> Message-ID: <56536EEC.5070902@redhat.com> Thanks for the feedback. New patch attached. On 11/16/2015 7:48 PM, Christina Fu wrote: > 1 in base/server/python/pki/server/deployment/scriptlets/configuration.py > doesn't this just add the leaf cert rather than the whole chain? In > other words, if your chain contains 2 or more certs, only the leaf subca > cert is added, isn't it? > > + nssdb.add_cert( > + nickname=external_ca_nickname, > + cert_file=external_ca_cert_chain_file, > + trust_attributes='CTu,CTu,CTu') Fixed. The new patch now supports PKCS #7 file, a single PEM cert, and the base-64 PKCS #7 data generated by getCertChain servlet. > 2 Also in the same file > + # If specified, import externally-signed CA cert in NSS database. > ... > Shouldn't there be a case when the externally signed ca keys were > generated on the hsm, you'd then need to import the issued externally > signed ca cert into the hsm db as well? If the externally-signed CA cert is specified in pki_external_ca_cert_path parameter it will be imported into the NSS database, regardless whether HSM is used. The code now calls certutil with -h option to specify the target token. Alternatively, the certificate can be imported manually before starting step 2. I've updated the docs: http://pki.fedoraproject.org/wiki/Installing_with_Externaly-Signed_CA_Certificate > 3 base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java > I"m not seeing the following method being called, yet the getExternal() > is being called...did I miss something? > > + public void setExternal(Boolean external) { > > + this.external = external; > + } The external attribute is set in Python in pkihelper.py:3983: data.external = self.external The value will be sent to the server via REST interface. The getExternal() will read that value. > 4. base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java > + public static void loadCert(Cert cert) throws Exception { > ... > + // create certificate record to reserve the serial number in internal database > + ICertRecord record = cr.createCertRecord(serialNo, x509CertImpl, meta); > + cr.addCertificateRecord(record); > > In case of an externally signed ca or existing ca, why would you need to > reserve the serial number or even add in the certificate repository? Fixed. This code is actually only needed when importing existing self-signed CA cert. This way when the code generates the system certificates it will not conflict with the CA cert's serial number both in NSS database and in internal LDAP database. For existing non-self-signed CA cert or externally signed CA cert the code will not create the LDAP record. > 5. > Finally, please add comments to explain the cases for clarification... > such as "stand-alone v.s. external; step 1, step 2, etc." For example, > it seems the "external" could imply "existing" as well in terms of ca > cert, you might want to put in comment. Yes, the "external" code handles both external CA and existing CA cases. I've added some inline comments. Please let me know if we need more. -- Endi S. Dewata -------------- next part -------------- >From cce2c2ea05142fe71c418e5288506dd8dfce4a14 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sat, 7 Nov 2015 00:09:19 +0100 Subject: [PATCH] Added mechanism to import existing CA certificate. The deployment procedure for external CA has been modified such that it generates the CA CSR before starting the server. This allows the same procedure to be used to import CA certificate from an existing server. It also removes the requirement to keep the server running while waiting to get the CSR signed by an external CA. https://fedorahosted.org/pki/ticket/456 --- base/common/python/pki/nss.py | 247 ++++++++++++++++++--- .../certsrv/system/ConfigurationRequest.java | 12 + .../cms/servlet/csadmin/ConfigurationUtils.java | 101 +++++++++ .../dogtagpki/server/rest/SystemConfigService.java | 38 +++- base/server/etc/default.cfg | 10 +- base/server/python/pki/server/__init__.py | 5 +- .../python/pki/server/deployment/pkihelper.py | 70 +++--- .../server/deployment/scriptlets/configuration.py | 131 ++++++++++- .../server/deployment/scriptlets/finalization.py | 12 +- 9 files changed, 560 insertions(+), 66 deletions(-) diff --git a/base/common/python/pki/nss.py b/base/common/python/pki/nss.py index f36b771f85eb45641022d6033c23a88aca50757a..f5384bc66be3ed90448b060d4d61105e4bf61421 100644 --- a/base/common/python/pki/nss.py +++ b/base/common/python/pki/nss.py @@ -32,12 +32,19 @@ CSR_FOOTER = '-----END NEW CERTIFICATE REQUEST-----' CERT_HEADER = '-----BEGIN CERTIFICATE-----' CERT_FOOTER = '-----END CERTIFICATE-----' +PKCS7_HEADER = '-----BEGIN PKCS7-----' +PKCS7_FOOTER = '-----END PKCS7-----' + def convert_data(data, input_format, output_format, header=None, footer=None): + if input_format == output_format: + return data + if input_format == 'base64' and output_format == 'pem': # split a single line into multiple lines + data = data.rstrip('\r\n') lines = [data[i:i+64] for i in range(0, len(data), 64)] return '%s\n%s\n%s\n' % (header, '\n'.join(lines), footer) @@ -65,11 +72,32 @@ def convert_cert(cert_data, input_format, output_format): return convert_data(cert_data, input_format, output_format, CERT_HEADER, CERT_FOOTER) +def convert_pkcs7(pkcs7_data, input_format, output_format): + + return convert_data(pkcs7_data, input_format, output_format, PKCS7_HEADER, PKCS7_FOOTER) + +def get_file_type(filename): + + with open(filename, 'r') as f: + data = f.read() + + if data.startswith(CSR_HEADER): + return 'csr' + + if data.startswith(CERT_HEADER): + return 'cert' + + if data.startswith(PKCS7_HEADER): + return 'pkcs7' + + return None + class NSSDatabase(object): - def __init__(self, directory, password=None, password_file=None): + def __init__(self, directory, token='internal', password=None, password_file=None): self.directory = directory + self.token = token self.tmpdir = tempfile.mkdtemp() @@ -88,29 +116,38 @@ class NSSDatabase(object): shutil.rmtree(self.tmpdir) def add_cert(self, - nickname, cert_file, - trust_attributes='u,u,u'): + nickname, + cert_file, + trust_attributes=',,'): - subprocess.check_call([ + cmd = [ 'certutil', '-A', '-d', self.directory, + '-h', self.token, + '-f', self.password_file, '-n', nickname, '-i', cert_file, '-t', trust_attributes - ]) + ] + + subprocess.check_call(cmd) def modify_cert(self, nickname, - trust_attributes='u,u,u'): + trust_attributes): - subprocess.check_call([ + cmd = [ 'certutil', '-M', '-d', self.directory, + '-h', self.token, + '-f', self.password, '-n', nickname, '-t', trust_attributes - ]) + ] + + subprocess.check_call(cmd) def create_noise(self, noise_file, size=2048): @@ -123,27 +160,56 @@ class NSSDatabase(object): def create_request(self, subject_dn, - noise_file, - request_file): + request_file, + noise_file=None, + key_type=None, + key_size=None, + curve=None, + hash_alg=None): tmpdir = tempfile.mkdtemp() try: + if not noise_file: + noise_file = os.path.join(tmpdir, 'noise.bin') + if key_size: + size = key_size + else: + size = 2048 + self.create_noise( + noise_file=noise_file, + size=size) + binary_request_file = os.path.join(tmpdir, 'request.bin') - b64_request_file = os.path.join(tmpdir, 'request.b64') - # generate binary request - subprocess.check_call([ + cmd = [ 'certutil', '-R', '-d', self.directory, + '-h', self.token, '-f', self.password_file, '-s', subject_dn, - '-z', noise_file, - '-o', binary_request_file - ]) + '-o', binary_request_file, + '-z', noise_file + ] + + if key_type: + cmd.extend(['-k', key_type]) + + if key_size: + cmd.extend(['-g', str(key_size)]) + + if curve: + cmd.extend(['-q', curve]) + + if hash_alg: + cmd.extend(['-Z', hash_alg]) + + # generate binary request + subprocess.check_call(cmd) # encode binary request in base-64 + b64_request_file = os.path.join(tmpdir, 'request.b64') subprocess.check_call([ 'BtoA', binary_request_file, b64_request_file]) @@ -167,11 +233,12 @@ class NSSDatabase(object): serial='1', validity=240): - p = subprocess.Popen([ + cmd = [ 'certutil', '-C', '-x', '-d', self.directory, + '-h', self.token, '-f', self.password_file, '-c', subject_dn, '-a', @@ -184,7 +251,9 @@ class NSSDatabase(object): '-3', '--extSKID', '--extAIA' - ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + ] + + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) keystroke = '' @@ -245,7 +314,7 @@ class NSSDatabase(object): rc = p.wait() if rc: - raise Exception('Failed to generate self-signed CA certificate. RC: %d' + rc) + raise Exception('Failed to generate self-signed CA certificate. RC: %d' % rc) def get_cert(self, nickname, output_format='pem'): @@ -258,13 +327,17 @@ class NSSDatabase(object): else: raise Exception('Unsupported output format: %s' % output_format) - cert_data = subprocess.check_output([ + cmd = [ 'certutil', '-L', '-d', self.directory, + '-h', self.token, + '-f', self.password_file, '-n', nickname, output_format_option - ]) + ] + + cert_data = subprocess.check_output(cmd) if output_format == 'base64': cert_data = base64.b64encode(cert_data) @@ -273,12 +346,127 @@ class NSSDatabase(object): def remove_cert(self, nickname): - subprocess.check_call([ + cmd = [ 'certutil', '-D', '-d', self.directory, + '-h', self.token, + '-f', self.password_file, '-n', nickname - ]) + ] + + subprocess.check_call(cmd) + + def import_cert_chain(self, nickname, cert_chain_file, trust_attributes=None): + + tmpdir = tempfile.mkdtemp() + + try: + file_type = get_file_type(cert_chain_file) + + if file_type == 'cert': # import single PEM cert + self.add_cert( + nickname=nickname, + cert_file=cert_chain_file, + trust_attributes=trust_attributes) + return self.get_cert( + nickname=nickname, + output_format='base64') + + elif file_type == 'pkcs7': # import PKCS #7 cert chain + return self.import_pkcs7( + pkcs7_file=cert_chain_file, + nickname=nickname, + trust_attributes=trust_attributes, + output_format='base64') + + else: # import PKCS #7 data without header/footer + with open(cert_chain_file, 'r') as f: + base64_data = f.read() + pkcs7_data = convert_pkcs7(base64_data, 'base64', 'pem') + + tmp_cert_chain_file = os.path.join(tmpdir, 'cert_chain.p7b') + with open(tmp_cert_chain_file, 'w') as f: + f.write(pkcs7_data) + + self.import_pkcs7( + pkcs7_file=tmp_cert_chain_file, + nickname=nickname, + trust_attributes=trust_attributes) + + return base64_data + + finally: + shutil.rmtree(tmpdir) + + def import_pkcs7(self, pkcs7_file, nickname, trust_attributes=None, output_format='pem'): + + tmpdir = tempfile.mkdtemp() + + try: + # export certs from PKCS #7 into PEM output + output = subprocess.check_output([ + 'openssl', + 'pkcs7', + '-print_certs', + '-in', pkcs7_file + ]) + + # parse PEM output into separate PEM certificates + certs = [] + lines = [] + state = 'header' + + for line in output.splitlines(): + + if state == 'header': + if line != CERT_HEADER: + # ignore header lines + pass + else: + # save cert header + lines.append(line) + state = 'body' + + elif state == 'body': + if line != CERT_FOOTER: + # save cert body + lines.append(line) + else: + # save cert footer + lines.append(line) + + # construct PEM cert + cert = '\n'.join(lines) + certs.append(cert) + lines = [] + state = 'header' + + # import PEM certs into NSS database + counter = 1 + for cert in certs: + + cert_file = os.path.join(tmpdir, 'cert%d.pem' % counter) + with open(cert_file, 'w') as f: + f.write(cert) + + if counter == 1: + n = nickname + else: + n = '%s #%d' % (nickname, counter) + + self.add_cert(n, cert_file, trust_attributes) + + counter += 1 + + # convert PKCS #7 data to the requested format + with open(pkcs7_file, 'r') as f: + data = f.read() + + return convert_pkcs7(data, 'pem', output_format) + + finally: + shutil.rmtree(tmpdir) def import_pkcs12(self, pkcs12_file, pkcs12_password=None, pkcs12_password_file=None): @@ -296,13 +484,16 @@ class NSSDatabase(object): else: raise Exception('Missing PKCS #12 password') - subprocess.check_call([ + cmd = [ 'pk12util', '-d', self.directory, + '-h', self.token, '-k', self.password_file, '-i', pkcs12_file, '-w', password_file - ]) + ] + + subprocess.check_call(cmd) finally: shutil.rmtree(tmpdir) @@ -323,14 +514,16 @@ class NSSDatabase(object): else: raise Exception('Missing PKCS #12 password') - subprocess.check_call([ + cmd = [ 'pk12util', '-d', self.directory, '-k', self.password_file, '-o', pkcs12_file, '-w', password_file, '-n', nickname - ]) + ] + + subprocess.check_call(cmd) finally: shutil.rmtree(tmpdir) diff --git a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java index 7c6c339f5ddff40018c4d14c97ca63e12e9b9289..8c9da6f373be192a6b5cf99a3cf8cd6ce288c3aa 100644 --- a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java +++ b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java @@ -178,6 +178,9 @@ public class ConfigurationRequest { protected String adminCert; @XmlElement + protected Boolean external; + + @XmlElement protected String standAlone; @XmlElement @@ -754,6 +757,14 @@ public class ConfigurationRequest { this.adminCert = adminCert; } + public Boolean isExternal() { + return external; + } + + public void setExternal(Boolean external) { + this.external = external; + } + public boolean getStandAlone() { return (standAlone != null && standAlone.equalsIgnoreCase("true")); } @@ -945,6 +956,7 @@ public class ConfigurationRequest { ", adminCert=" + adminCert + ", importAdminCert=" + importAdminCert + ", generateServerCert=" + generateServerCert + + ", external=" + external + ", standAlone=" + standAlone + ", stepTwo=" + stepTwo + ", authdbBaseDN=" + authdbBaseDN + diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java index 88118adf8a7535442d8f1f678ce14f6f6ac07e51..91dad159bb39605d094c87c1958cc57772dbb732 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java +++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java @@ -126,6 +126,7 @@ import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.base.ISubsystem; +import com.netscape.certsrv.base.MetaInfo; import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.base.ResourceNotFoundException; import com.netscape.certsrv.ca.ICertificateAuthority; @@ -133,6 +134,8 @@ import com.netscape.certsrv.client.ClientConfig; import com.netscape.certsrv.client.PKIClient; import com.netscape.certsrv.client.PKIConnection; import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.certsrv.dbs.certdb.ICertRecord; +import com.netscape.certsrv.dbs.certdb.ICertificateRepository; import com.netscape.certsrv.dbs.crldb.ICRLIssuingPointRecord; import com.netscape.certsrv.key.KeyData; import com.netscape.certsrv.ldap.ILdapConnFactory; @@ -2248,6 +2251,54 @@ public class ConfigurationUtils { certObj.setCertChain(certChainStr); } + public static KeyPair loadKeyPair(String nickname) throws Exception { + + CMS.debug("ConfigurationUtils: loadKeyPair(" + nickname + ")"); + + CryptoManager cm = CryptoManager.getInstance(); + + X509Certificate cert = cm.findCertByNickname(nickname); + PublicKey publicKey = cert.getPublicKey(); + PrivateKey privateKey = cm.findPrivKeyByCert(cert); + + return new KeyPair(publicKey, privateKey); + } + + public static void storeKeyPair(IConfigStore config, String tag, KeyPair pair) + throws TokenException, EBaseException { + + CMS.debug("ConfigurationUtils: storeKeyPair(" + tag + ")"); + + PublicKey publicKey = pair.getPublic(); + + if (publicKey instanceof RSAPublicKey) { + + RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; + + byte modulus[] = rsaPublicKey.getModulus().toByteArray(); + config.putString(PCERT_PREFIX + tag + ".pubkey.modulus", + CryptoUtil.byte2string(modulus)); + + byte exponent[] = rsaPublicKey.getPublicExponent().toByteArray(); + config.putString(PCERT_PREFIX + tag + ".pubkey.exponent", + CryptoUtil.byte2string(exponent)); + + } else { // ECC + + CMS.debug("ConfigurationUtils: Public key class: " + publicKey.getClass().getName()); + byte encoded[] = publicKey.getEncoded(); + config.putString(PCERT_PREFIX + tag + ".pubkey.encoded", CryptoUtil.byte2string(encoded)); + } + + PrivateKey privateKey = (PrivateKey) pair.getPrivate(); + byte id[] = privateKey.getUniqueID(); + String kid = CryptoUtil.byte2string(id); + config.putString(PCERT_PREFIX + tag + ".privkey.id", kid); + + String keyAlgo = config.getString(PCERT_PREFIX + tag + ".signingalgorithm"); + setSigningAlgorithm(tag, keyAlgo, config); + } + public static void createECCKeyPair(String token, String curveName, IConfigStore config, String ct) throws NoSuchAlgorithmException, NoSuchTokenException, TokenException, CryptoManager.NotInitializedException, EPropertyNotFound, EBaseException { @@ -2812,6 +2863,20 @@ public class ConfigurationUtils { } } + public static void loadCertRequest(IConfigStore config, String tag, Cert cert) throws Exception { + + CMS.debug("ConfigurationUtils.loadCertRequest(" + tag + ")"); + + String subjectDN = config.getString(PCERT_PREFIX + tag + ".dn"); + cert.setDN(subjectDN); + + String subsystem = config.getString(PCERT_PREFIX + tag + ".subsystem"); + String certreq = config.getString(subsystem + "." + tag + ".certreq"); + String formattedCertreq = CryptoUtil.reqFormat(certreq); + + cert.setRequest(formattedCertreq); + } + public static void handleCertRequest(IConfigStore config, String certTag, Cert cert) throws EPropertyNotFound, EBaseException, InvalidKeyException, NotInitializedException, TokenException, NoSuchAlgorithmException, NoSuchProviderException, CertificateException, SignatureException, IOException { @@ -2953,6 +3018,42 @@ public class ConfigurationUtils { return pubk; } + public static void loadCert(IConfigStore config, Cert cert) throws Exception { + + String tag = cert.getCertTag(); + CMS.debug("ConfigurationUtils: loadCert(" + tag + ")"); + + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate x509Cert = cm.findCertByNickname(cert.getNickname()); + + if (!x509Cert.getSubjectDN().equals(x509Cert.getIssuerDN())) { + CMS.debug("ConfigurationUtils: " + tag + " cert is not self-signed"); + + String subsystem = config.getString(PCERT_PREFIX + tag + ".subsystem"); + String certChain = config.getString(subsystem + ".external_ca_chain.cert"); + cert.setCertChain(certChain); + + return; + } + + CMS.debug("ConfigurationUtils: " + tag + " cert is self-signed"); + + // When importing existing self-signed CA certificate, create a + // certificate record to reserve the serial number. Otherwise it + // might conflict with system certificates to be created later. + + X509CertImpl x509CertImpl = new X509CertImpl(x509Cert.getEncoded()); + + ICertificateAuthority ca = (ICertificateAuthority) CMS.getSubsystem(ICertificateAuthority.ID); + ICertificateRepository cr = ca.getCertificateRepository(); + + BigInteger serialNo = x509Cert.getSerialNumber(); + MetaInfo meta = new MetaInfo(); + + ICertRecord record = cr.createCertRecord(serialNo, x509CertImpl, meta); + cr.addCertificateRecord(record); + } + public static int handleCerts(Cert cert) throws IOException, EBaseException, CertificateException, NotInitializedException, TokenException, InvalidKeyException { String certTag = cert.getCertTag(); diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java index a0138681a39baeff272d75408dbee9a74d0529dc..697196a6ea6beb22210fed1f5680f7d02cea1533 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java @@ -20,6 +20,7 @@ package org.dogtagpki.server.rest; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; +import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.util.ArrayList; @@ -420,7 +421,13 @@ public class SystemConfigService extends PKIService implements SystemConfigResou } cs.commit(false); - if (!request.getStepTwo()) { + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + // load key pair for existing and externally-signed signing cert + CMS.debug("SystemConfigService: loading signing cert key pair"); + KeyPair pair = ConfigurationUtils.loadKeyPair(certData.getNickname()); + ConfigurationUtils.storeKeyPair(cs, tag, pair); + + } else if (!request.getStepTwo()) { if (keytype.equals("ecc")) { String curvename = certData.getKeyCurveName() != null ? certData.getKeyCurveName() : cs.getString("keys.ecc.curve.default"); @@ -443,7 +450,15 @@ public class SystemConfigService extends PKIService implements SystemConfigResou cert.setSubsystem(cs.getString("preop.cert." + tag + ".subsystem")); cert.setType(cs.getString("preop.cert." + tag + ".type")); - if (!request.getStepTwo()) { + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + + // update configuration for existing or externally-signed signing certificate + String certStr = cs.getString("ca." + tag + ".cert" ); + cert.setCert(certStr); + CMS.debug("SystemConfigService: certificate " + tag + ": " + certStr); + ConfigurationUtils.updateConfig(cs, tag); + + } else if (!request.getStepTwo()) { ConfigurationUtils.configCert(null, null, null, cert); } else { @@ -465,8 +480,16 @@ public class SystemConfigService extends PKIService implements SystemConfigResou CMS.debug("Step 2: certStr for '" + tag + "' is " + certStr); } - // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) - if (request.getStandAlone()) { + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + + CMS.debug("SystemConfigService: Loading cert request for " + tag + " cert"); + ConfigurationUtils.loadCertRequest(cs, tag, cert); + + CMS.debug("SystemConfigService: Loading cert " + tag); + ConfigurationUtils.loadCert(cs, cert); + + } else if (request.getStandAlone()) { + // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) if (!request.getStepTwo()) { // Stand-alone PKI (Step 1) ConfigurationUtils.handleCertRequest(cs, tag, cert); @@ -489,6 +512,13 @@ public class SystemConfigService extends PKIService implements SystemConfigResou ConfigurationUtils.updateCloneConfig(); } + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + CMS.debug("SystemConfigService: External CA has signing cert"); + hasSigningCert.setValue(true); + certs.add(cert); + continue; + } + // to determine if we have the signing cert when using an external ca // this will only execute on a ca or stand-alone pki String b64 = certData.getCert(); diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index ddd2d83670dd191f38b8905cdea03172bbbc1e95..1c1ae92b323d67dc5fb810df79bbdbbb0b6c26e7 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -22,6 +22,7 @@ sensitive_parameters= pki_client_pkcs12_password pki_clone_pkcs12_password pki_ds_password + pki_external_pkcs12_password pki_one_time_pin pki_pin pki_replication_password @@ -365,10 +366,13 @@ pki_req_ext_add=False pki_req_ext_oid=1.3.6.1.4.1.311.20.2 pki_req_ext_critical=False pki_req_ext_data=1E0A00530075006200430041 -pki_external_csr_path=%(pki_instance_configuration_path)s/ca_signing.csr +pki_external_csr_path= pki_external_step_two=False -pki_external_ca_cert_chain_path=%(pki_instance_configuration_path)s/external_ca_chain.cert -pki_external_ca_cert_path=%(pki_instance_configuration_path)s/external_ca.cert +pki_external_ca_cert_chain_path= +pki_external_ca_cert_chain_nickname=caSigningCert External CA +pki_external_ca_cert_path= +pki_external_pkcs12_path= +pki_external_pkcs12_password= pki_import_admin_cert=False pki_ocsp_signing_key_algorithm=SHA256withRSA pki_ocsp_signing_key_size=2048 diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index d55a3691d180ede7dd1731b7490957c816bd8a3b..bf592dcd59bf07314b94447d5da7ddbdf0077c8b 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -328,10 +328,11 @@ class PKIInstance(object): return password - def open_nssdb(self): + def open_nssdb(self, token='internal'): return pki.nss.NSSDatabase( directory=self.nssdb_dir, - password=self.get_password('internal')) + token=token, + password=self.get_password(token)) def get_subsystem(self, name): for subsystem in self.subsystems: diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py index 61f04d215ba3eba48b7e18733fd58b29555ced83..9c9b40454a41b42f2c089f045ac9ac662093a409 100644 --- a/base/server/python/pki/server/deployment/pkihelper.py +++ b/base/server/python/pki/server/deployment/pkihelper.py @@ -757,8 +757,7 @@ class ConfigurationFile: # External CA if not self.external_step_two: # External CA (Step 1) - self.confirm_data_exists("pki_external_csr_path") - self.confirm_missing_file("pki_external_csr_path") + # The pki_external_csr_path is optional. # generic extension support in CSR - for external CA if self.add_req_ext: self.confirm_data_exists("pki_req_ext_oid") @@ -766,10 +765,9 @@ class ConfigurationFile: self.confirm_data_exists("pki_req_ext_data") else: # External CA (Step 2) - self.confirm_data_exists("pki_external_ca_cert_chain_path") - self.confirm_file_exists("pki_external_ca_cert_chain_path") - self.confirm_data_exists("pki_external_ca_cert_path") - self.confirm_file_exists("pki_external_ca_cert_path") + # The pki_external_ca_cert_chain_path and + # pki_external_ca_cert_path are optional. + pass elif not self.skip_configuration and self.standalone: if not self.external_step_two: # Stand-alone PKI Admin CSR (Step 1) @@ -3813,17 +3811,7 @@ class ConfigClient: if not isinstance(certs, list): certs = [certs] for cdata in certs: - if (self.subsystem == "CA" and self.external and - not self.external_step_two): - # External CA (Step 1) - if cdata['tag'].lower() == "signing": - # Save 'External CA Signing Certificate' CSR (Step 1) - self.save_system_csr( - cdata['request'], - log.PKI_CONFIG_EXTERNAL_CSR_SAVE, - self.mdict['pki_external_csr_path']) - return - elif self.standalone and not self.external_step_two: + if self.standalone and not self.external_step_two: # Stand-alone PKI (Step 1) if cdata['tag'].lower() == "audit_signing": # Save Stand-alone PKI 'Audit Signing Certificate' CSR @@ -3991,8 +3979,17 @@ class ConfigClient: data.token = self.mdict['pki_token_name'] data.tokenPassword = self.mdict['pki_token_password'] data.subsystemName = self.mdict['pki_subsystem_name'] + + data.external = self.external data.standAlone = self.standalone - data.stepTwo = self.external_step_two + + if self.standalone: + # standalone installation uses two-step process (ticket #1698) + data.stepTwo = self.external_step_two + + else: + # other installations use only one step in the configuration servlet + data.stepTwo = False # Cloning parameters if self.mdict['pki_instance_type'] == "Tomcat": @@ -4122,25 +4119,46 @@ class ConfigClient: self.mdict['pki_req_ext_critical'] cert1.req_ext_data = \ self.mdict['pki_req_ext_data'] - if self.external_step_two: - # External CA (Step 2) or Stand-alone PKI (Step 2) - if not self.subsystem == "CA": - # Stand-alone PKI (Step 2) - cert1 = pki.system.SystemCertData() - cert1.tag = self.mdict['pki_ca_signing_tag'] - # Load the External CA or Stand-alone PKI + + if self.external and self.external_step_two: # external/existing CA step 2 + + # If specified, load the externally-signed CA cert + if self.mdict['pki_external_ca_cert_path']: + self.load_system_cert( + cert1, + log.PKI_CONFIG_EXTERNAL_CA_LOAD, + self.mdict['pki_external_ca_cert_path']) + + # If specified, load the external CA cert chain + if self.mdict['pki_external_ca_cert_chain_path']: + self.load_system_cert_chain( + cert1, + log.PKI_CONFIG_EXTERNAL_CA_CHAIN_LOAD, + self.mdict['pki_external_ca_cert_chain_path']) + + systemCerts.append(cert1) + + elif self.standalone and self.external_step_two: # standalone KRA/OCSP step 2 + + cert1 = pki.system.SystemCertData() + cert1.tag = self.mdict['pki_ca_signing_tag'] + + # Load the stand-alone PKI # 'External CA Signing Certificate' (Step 2) self.load_system_cert( cert1, log.PKI_CONFIG_EXTERNAL_CA_LOAD, self.mdict['pki_external_ca_cert_path']) - # Load the External CA or Stand-alone PKI + + # Load the stand-alone PKI # 'External CA Signing Certificate Chain' (Step 2) self.load_system_cert_chain( cert1, log.PKI_CONFIG_EXTERNAL_CA_CHAIN_LOAD, self.mdict['pki_external_ca_cert_chain_path']) + systemCerts.append(cert1) + elif self.subsystem == "CA": # PKI CA or Subordinate CA systemCerts.append(cert1) diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py index c6e89023560fc92cb9bd451d9b7f05818807da8a..c28fb13b20349a9acd569f0ee26adecb9c16ff98 100644 --- a/base/server/python/pki/server/deployment/scriptlets/configuration.py +++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py @@ -21,13 +21,18 @@ from __future__ import absolute_import import json +import re # PKI Deployment Imports from .. import pkiconfig as config from .. import pkimessages as log from .. import pkiscriptlet -import pki.system + import pki.encoder +import pki.nss +import pki.server +import pki.system +import pki.util # PKI Deployment Configuration Scriptlet @@ -81,6 +86,130 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_client_secmod_database'], password_file=deployer.mdict['pki_client_password_conf']) + instance = pki.server.PKIInstance(deployer.mdict['pki_instance_name']) + instance.load() + + subsystem = instance.get_subsystem(deployer.mdict['pki_subsystem'].lower()) + + token = deployer.mdict['pki_ca_signing_token'] + if token == 'Internal Key Storage Token': + token = 'internal' + + nssdb = instance.open_nssdb(token) + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + step_two = not step_one + + try: + if external and step_one: # external/existing CA step 1 + + key_type = deployer.mdict['pki_ca_signing_key_type'] + key_alg = deployer.mdict['pki_ca_signing_key_algorithm'] + + if key_type == 'rsa': + key_size = int(deployer.mdict['pki_ca_signing_key_size']) + curve = None + + m = re.match(r'(.*)withRSA', key_alg) + if not m: + raise Exception('Invalid key algorithm: %s' % key_alg) + hash_alg = m.group(1) + + elif key_type == 'ec' or key_type == 'ecc': + key_type = 'ec' + key_size = None + curve = deployer.mdict['pki_ca_signing_key_size'] + + m = re.match(r'(.*)withEC', key_alg) + if not m: + raise Exception('Invalid key algorithm: %s' % key_alg) + hash_alg = m.group(1) + + else: + raise Exception('Invalid key type: %s' % key_type) + + # If filename specified, generate CA cert request and + # import it into CS.cfg. + request_file = deployer.mdict['pki_external_csr_path'] + if request_file: + nssdb.create_request( + subject_dn=deployer.mdict['pki_ca_signing_subject_dn'], + request_file=request_file, + key_type=key_type, + key_size=key_size, + curve=curve, + hash_alg=hash_alg) + with open(request_file) as f: + signing_csr = f.read() + signing_csr = pki.nss.convert_csr(signing_csr, 'pem', 'base64') + subsystem.config['ca.signing.certreq'] = signing_csr + + subsystem.save() + + elif external and step_two: # external/existing CA step 2 + + # If specified, import existing CA cert request into CS.cfg. + request_file = deployer.mdict['pki_external_csr_path'] + if request_file: + with open(request_file) as f: + signing_csr = f.read() + signing_csr = pki.nss.convert_csr(signing_csr, 'pem', 'base64') + subsystem.config['ca.signing.certreq'] = signing_csr + + # If specified, import external CA cert into NSS database. + external_ca_cert_chain_nickname = deployer.mdict['pki_external_ca_cert_chain_nickname'] + external_ca_cert_chain_file = deployer.mdict['pki_external_ca_cert_chain_path'] + if external_ca_cert_chain_file: + cert_chain = nssdb.import_cert_chain( + nickname=external_ca_cert_chain_nickname, + cert_chain_file=external_ca_cert_chain_file, + trust_attributes='CT,C,C') + subsystem.config['ca.external_ca_chain.cert'] = cert_chain + + # If specified, import externally-signed CA cert into NSS database. + signing_nickname = deployer.mdict['pki_ca_signing_nickname'] + signing_cert_file = deployer.mdict['pki_external_ca_cert_path'] + if signing_cert_file: + nssdb.add_cert( + nickname=signing_nickname, + cert_file=signing_cert_file, + trust_attributes='CT,C,C') + + # If specified, import CA cert and key from PKCS #12 file into NSS database. + pkcs12_file = deployer.mdict['pki_external_pkcs12_path'] + if pkcs12_file: + pkcs12_password = deployer.mdict['pki_external_pkcs12_password'] + nssdb.import_pkcs12(pkcs12_file, pkcs12_password) + + # Export CA cert from NSS database and import it into CS.cfg. + signing_cert_data = nssdb.get_cert( + nickname=signing_nickname, + output_format='base64') + subsystem.config['ca.signing.nickname'] = signing_nickname + subsystem.config['ca.signing.tokenname'] = deployer.mdict['pki_ca_signing_token'] + subsystem.config['ca.signing.cert'] = signing_cert_data + subsystem.config['ca.signing.cacertnickname'] = signing_nickname + subsystem.config['ca.signing.defaultSigningAlgorithm'] = deployer.mdict['pki_ca_signing_signing_algorithm'] + + subsystem.save() + + else: # self-signed CA + + # To be implemented in ticket #1692. + + # Generate CA cert request. + # Self sign CA cert. + # Import self-signed CA cert into NSS database. + + pass + + finally: + nssdb.close() + + if external and step_one: + return self.rv + # Start/Restart this Tomcat PKI Process # Optionally prepare to enable a java debugger # (e. g. - 'eclipse'): diff --git a/base/server/python/pki/server/deployment/scriptlets/finalization.py b/base/server/python/pki/server/deployment/scriptlets/finalization.py index 56ddf0219d37dc7258e95464aff9ae925456a1a8..3c4f469aced9eec7928cf2c1a27ac43ebe5e1886 100644 --- a/base/server/python/pki/server/deployment/scriptlets/finalization.py +++ b/base/server/python/pki/server/deployment/scriptlets/finalization.py @@ -67,9 +67,15 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): if len(deployer.instance.tomcat_instance_subsystems()) == 1: # Modify contents of 'serverCertNick.conf' (if necessary) deployer.servercertnick_conf.modify() - # Optionally, programmatically 'restart' the configured PKI instance - if config.str2bool(deployer.mdict['pki_restart_configured_instance']): - deployer.systemd.restart() + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + + if not (external and step_one): + # Optionally, programmatically 'restart' the configured PKI instance + if config.str2bool(deployer.mdict['pki_restart_configured_instance']): + deployer.systemd.restart() + # Optionally, 'purge' the entire temporary client infrastructure # including the client NSS security databases and password files # -- 2.4.3 From cfu at redhat.com Tue Nov 24 00:24:51 2015 From: cfu at redhat.com (Christina Fu) Date: Mon, 23 Nov 2015 16:24:51 -0800 Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <56536EEC.5070902@redhat.com> References: <5640FB04.2050706@redhat.com> <564A1134.2010406@redhat.com> <564A8776.9050600@redhat.com> <56536EEC.5070902@redhat.com> Message-ID: <5653AE53.2020201@redhat.com> The fixed areas look good. If tested to work, ACK. Christina On 11/23/2015 11:54 AM, Endi Sukma Dewata wrote: > Thanks for the feedback. New patch attached. > > On 11/16/2015 7:48 PM, Christina Fu wrote: >> 1 in >> base/server/python/pki/server/deployment/scriptlets/configuration.py >> doesn't this just add the leaf cert rather than the whole chain? In >> other words, if your chain contains 2 or more certs, only the leaf subca >> cert is added, isn't it? >> >> + nssdb.add_cert( >> + nickname=external_ca_nickname, >> + cert_file=external_ca_cert_chain_file, >> + trust_attributes='CTu,CTu,CTu') > > Fixed. The new patch now supports PKCS #7 file, a single PEM cert, and > the base-64 PKCS #7 data generated by getCertChain servlet. > >> 2 Also in the same file >> + # If specified, import externally-signed CA cert in NSS database. >> ... >> Shouldn't there be a case when the externally signed ca keys were >> generated on the hsm, you'd then need to import the issued externally >> signed ca cert into the hsm db as well? > > If the externally-signed CA cert is specified in > pki_external_ca_cert_path parameter it will be imported into the NSS > database, regardless whether HSM is used. The code now calls certutil > with -h option to specify the target token. Alternatively, the > certificate can be imported manually before starting step 2. I've > updated the docs: > http://pki.fedoraproject.org/wiki/Installing_with_Externaly-Signed_CA_Certificate > >> 3 base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java >> I"m not seeing the following method being called, yet the getExternal() >> is being called...did I miss something? >> >> + public void setExternal(Boolean external) { >> >> + this.external = external; >> + } > > The external attribute is set in Python in pkihelper.py:3983: > > data.external = self.external > > The value will be sent to the server via REST interface. The > getExternal() will read that value. > >> 4. >> base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java >> + public static void loadCert(Cert cert) throws Exception { >> ... >> + // create certificate record to reserve the serial number in >> internal database >> + ICertRecord record = cr.createCertRecord(serialNo, >> x509CertImpl, meta); >> + cr.addCertificateRecord(record); >> >> In case of an externally signed ca or existing ca, why would you need to >> reserve the serial number or even add in the certificate repository? > > Fixed. This code is actually only needed when importing existing > self-signed CA cert. This way when the code generates the system > certificates it will not conflict with the CA cert's serial number > both in NSS database and in internal LDAP database. For existing > non-self-signed CA cert or externally signed CA cert the code will not > create the LDAP record. > >> 5. >> Finally, please add comments to explain the cases for clarification... >> such as "stand-alone v.s. external; step 1, step 2, etc." For example, >> it seems the "external" could imply "existing" as well in terms of ca >> cert, you might want to put in comment. > > Yes, the "external" code handles both external CA and existing CA > cases. I've added some inline comments. Please let me know if we need > more. > From jmagne at redhat.com Tue Nov 24 00:43:34 2015 From: jmagne at redhat.com (John Magne) Date: Mon, 23 Nov 2015 19:43:34 -0500 (EST) Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <5653AE53.2020201@redhat.com> References: <5640FB04.2050706@redhat.com> <564A1134.2010406@redhat.com> <564A8776.9050600@redhat.com> <56536EEC.5070902@redhat.com> <5653AE53.2020201@redhat.com> Message-ID: <952895973.23084216.1448325814874.JavaMail.zimbra@redhat.com> Looks ok to me, ACK but will defer more strongly to cfu on this one. One quick thing: The routine that creates the cert request doesn't appear to massage the key related params much. For instance if someone would give the RSA key sizes and an ECC curve name, the responsibility to check this would move down to the system call. Not sure this is worth fixing so just making it optional. ----- Original Message ----- From: "Christina Fu" To: "Endi Sukma Dewata" , pki-devel at redhat.com Sent: Monday, November 23, 2015 4:24:51 PM Subject: Re: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. The fixed areas look good. If tested to work, ACK. Christina On 11/23/2015 11:54 AM, Endi Sukma Dewata wrote: > Thanks for the feedback. New patch attached. > > On 11/16/2015 7:48 PM, Christina Fu wrote: >> 1 in >> base/server/python/pki/server/deployment/scriptlets/configuration.py >> doesn't this just add the leaf cert rather than the whole chain? In >> other words, if your chain contains 2 or more certs, only the leaf subca >> cert is added, isn't it? >> >> + nssdb.add_cert( >> + nickname=external_ca_nickname, >> + cert_file=external_ca_cert_chain_file, >> + trust_attributes='CTu,CTu,CTu') > > Fixed. The new patch now supports PKCS #7 file, a single PEM cert, and > the base-64 PKCS #7 data generated by getCertChain servlet. > >> 2 Also in the same file >> + # If specified, import externally-signed CA cert in NSS database. >> ... >> Shouldn't there be a case when the externally signed ca keys were >> generated on the hsm, you'd then need to import the issued externally >> signed ca cert into the hsm db as well? > > If the externally-signed CA cert is specified in > pki_external_ca_cert_path parameter it will be imported into the NSS > database, regardless whether HSM is used. The code now calls certutil > with -h option to specify the target token. Alternatively, the > certificate can be imported manually before starting step 2. I've > updated the docs: > http://pki.fedoraproject.org/wiki/Installing_with_Externaly-Signed_CA_Certificate > >> 3 base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java >> I"m not seeing the following method being called, yet the getExternal() >> is being called...did I miss something? >> >> + public void setExternal(Boolean external) { >> >> + this.external = external; >> + } > > The external attribute is set in Python in pkihelper.py:3983: > > data.external = self.external > > The value will be sent to the server via REST interface. The > getExternal() will read that value. > >> 4. >> base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java >> + public static void loadCert(Cert cert) throws Exception { >> ... >> + // create certificate record to reserve the serial number in >> internal database >> + ICertRecord record = cr.createCertRecord(serialNo, >> x509CertImpl, meta); >> + cr.addCertificateRecord(record); >> >> In case of an externally signed ca or existing ca, why would you need to >> reserve the serial number or even add in the certificate repository? > > Fixed. This code is actually only needed when importing existing > self-signed CA cert. This way when the code generates the system > certificates it will not conflict with the CA cert's serial number > both in NSS database and in internal LDAP database. For existing > non-self-signed CA cert or externally signed CA cert the code will not > create the LDAP record. > >> 5. >> Finally, please add comments to explain the cases for clarification... >> such as "stand-alone v.s. external; step 1, step 2, etc." For example, >> it seems the "external" could imply "existing" as well in terms of ca >> cert, you might want to put in comment. > > Yes, the "external" code handles both external CA and existing CA > cases. I've added some inline comments. Please let me know if we need > more. > _______________________________________________ Pki-devel mailing list Pki-devel at redhat.com https://www.redhat.com/mailman/listinfo/pki-devel From edewata at redhat.com Tue Nov 24 14:20:54 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Tue, 24 Nov 2015 08:20:54 -0600 Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <952895973.23084216.1448325814874.JavaMail.zimbra@redhat.com> References: <5640FB04.2050706@redhat.com> <564A1134.2010406@redhat.com> <564A8776.9050600@redhat.com> <56536EEC.5070902@redhat.com> <5653AE53.2020201@redhat.com> <952895973.23084216.1448325814874.JavaMail.zimbra@redhat.com> Message-ID: <56547246.3050905@redhat.com> On 11/23/2015 6:43 PM, John Magne wrote: > Looks ok to me, ACK but will defer more strongly to cfu on this one. > > One quick thing: > > The routine that creates the cert request doesn't appear to massage the > key related params much. For instance if someone would give the RSA key sizes > and an ECC curve name, the responsibility to check this would move down to the system > call. > > Not sure this is worth fixing so just making it optional. Yes, the code is intentionally doing just the minimal checking for key-related parameters such that if NSS introduces a new behavior (e.g. supporting new curve) PKI will automatically pick it up without any modification. They key size & curve name are passed directly to certutil assuming that NSS will do the validation and will fail if the values aren't valid. It's doing a little bit more checking on the key algorithm because it needs to parse the hash algorithm out of it, but the hash algorithm itself is passed directly to certutil, PKI doesn't validate it. Is this ok? -- Endi S. Dewata From jmagne at redhat.com Tue Nov 24 17:45:06 2015 From: jmagne at redhat.com (John Magne) Date: Tue, 24 Nov 2015 12:45:06 -0500 (EST) Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <56547246.3050905@redhat.com> References: <5640FB04.2050706@redhat.com> <564A1134.2010406@redhat.com> <564A8776.9050600@redhat.com> <56536EEC.5070902@redhat.com> <5653AE53.2020201@redhat.com> <952895973.23084216.1448325814874.JavaMail.zimbra@redhat.com> <56547246.3050905@redhat.com> Message-ID: <1762479946.24001167.1448387106715.JavaMail.zimbra@redhat.com> Sounds fine. Like I said ACK, and my comments were optional, and with your explanation, sounds good. ----- Original Message ----- From: "Endi Sukma Dewata" To: "John Magne" , "Christina Fu" Cc: pki-devel at redhat.com Sent: Tuesday, 24 November, 2015 6:20:54 AM Subject: Re: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. On 11/23/2015 6:43 PM, John Magne wrote: > Looks ok to me, ACK but will defer more strongly to cfu on this one. > > One quick thing: > > The routine that creates the cert request doesn't appear to massage the > key related params much. For instance if someone would give the RSA key sizes > and an ECC curve name, the responsibility to check this would move down to the system > call. > > Not sure this is worth fixing so just making it optional. Yes, the code is intentionally doing just the minimal checking for key-related parameters such that if NSS introduces a new behavior (e.g. supporting new curve) PKI will automatically pick it up without any modification. They key size & curve name are passed directly to certutil assuming that NSS will do the validation and will fail if the values aren't valid. It's doing a little bit more checking on the key algorithm because it needs to parse the hash algorithm out of it, but the hash algorithm itself is passed directly to certutil, PKI doesn't validate it. Is this ok? -- Endi S. Dewata From edewata at redhat.com Wed Nov 25 00:22:03 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Tue, 24 Nov 2015 18:22:03 -0600 Subject: [Pki-devel] [PATCH] 657 Refactored CA certificate generation. In-Reply-To: <1762479946.24001167.1448387106715.JavaMail.zimbra@redhat.com> References: <5640FB04.2050706@redhat.com> <564A1134.2010406@redhat.com> <564A8776.9050600@redhat.com> <56536EEC.5070902@redhat.com> <5653AE53.2020201@redhat.com> <952895973.23084216.1448325814874.JavaMail.zimbra@redhat.com> <56547246.3050905@redhat.com> <1762479946.24001167.1448387106715.JavaMail.zimbra@redhat.com> Message-ID: <5654FF2B.7090104@redhat.com> On 11/23/2015 6:24 PM, Christina Fu wrote: > The fixed areas look good. > If tested to work, ACK. On 11/24/2015 11:45 AM, John Magne wrote: > Sounds fine. Like I said ACK, and my comments were optional, > and with your explanation, sounds good. Thanks! Pushed to master. -- Endi S. Dewata From alee at redhat.com Wed Nov 25 19:50:16 2015 From: alee at redhat.com (Ade Lee) Date: Wed, 25 Nov 2015 14:50:16 -0500 Subject: [Pki-devel] [PATCH 0042] Fix escaping of password fields to prevent interpolation In-Reply-To: <5652F3FC.7040200@redhat.com> References: <5652F370.8090606@redhat.com> <5652F3FC.7040200@redhat.com> Message-ID: <1448481016.22418.0.camel@redhat.com> ACK On Mon, 2015-11-23 at 12:09 +0100, Christian Heimes wrote: > On 2015-11-23 12:07, Christian Heimes wrote: > > Some password and pin fields are missing from the no_interpolation > > list. > > One entry is misspelled. A '%' in password field such as > > pki_clone_pkcs12_password causes an installation error. > > > > https://fedorahosted.org/pki/ticket/1703 > > https://bugzilla.redhat.com/show_bug.cgi?id=1283631 > > And here is the patch file. > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From cheimes at redhat.com Wed Nov 25 19:51:39 2015 From: cheimes at redhat.com (Christian Heimes) Date: Wed, 25 Nov 2015 20:51:39 +0100 Subject: [Pki-devel] [PATCH 0043] sslget must set Host HTTP header Message-ID: <5656114B.9030209@redhat.com> The sslget tool sends a TLS SNI header. Apache doesn't like server name indication without a matching HTTP header. Requests without a Host header are refused with HTTP/1.1 400 Bad Request Hostname example.org provided via SNI, but no hostname provided in HTTP request sslget now sets a Host HTTP header for all requests. https://fedorahosted.org/pki/ticket/1704 -------------- next part -------------- A non-text attachment was scrubbed... Name: pki-cheimes-0043-sslget-must-set-Host-HTTP-header.patch Type: text/x-patch Size: 2310 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 455 bytes Desc: OpenPGP digital signature URL: From alee at redhat.com Wed Nov 25 19:52:39 2015 From: alee at redhat.com (Ade Lee) Date: Wed, 25 Nov 2015 14:52:39 -0500 Subject: [Pki-devel] [PATCH] 0056 Avoid superfluous ConfigStore commit during profile creation In-Reply-To: <20151123060425.GN5336@dhcp-40-8.bne.redhat.com> References: <20151123060425.GN5336@dhcp-40-8.bne.redhat.com> Message-ID: <1448481159.22418.1.camel@redhat.com> ACK On Mon, 2015-11-23 at 16:04 +1000, Fraser Tweedale wrote: > The attached patch is a drive-by improvement during analysis of > https://fedorahosted.org/pki/ticket/1700. > > Thanks, > Fraser > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From cheimes at redhat.com Wed Nov 25 21:04:12 2015 From: cheimes at redhat.com (Christian Heimes) Date: Wed, 25 Nov 2015 22:04:12 +0100 Subject: [Pki-devel] [PATCH 0043] sslget must set Host HTTP header In-Reply-To: <5656114B.9030209@redhat.com> References: <5656114B.9030209@redhat.com> Message-ID: <5656224C.8070203@redhat.com> On 2015-11-25 20:51, Christian Heimes wrote: > The sslget tool sends a TLS SNI header. Apache doesn't like server name > indication without a matching HTTP header. Requests without a Host > header are refused with > > HTTP/1.1 400 Bad Request > Hostname example.org provided via SNI, but no hostname provided in HTTP > request > > sslget now sets a Host HTTP header for all requests. > > https://fedorahosted.org/pki/ticket/1704 Per IRC discussion John has suggested to include the port in the Host header. It's required for non-standard ports. -------------- next part -------------- A non-text attachment was scrubbed... Name: pki-cheimes-0043-2-sslget-must-set-Host-HTTP-header.patch Type: text/x-patch Size: 2399 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 455 bytes Desc: OpenPGP digital signature URL: From edewata at redhat.com Wed Nov 25 21:48:09 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Wed, 25 Nov 2015 15:48:09 -0600 Subject: [Pki-devel] [PATCH] 660 Updated pki-cert and pki-server-subsystem man pages. In-Reply-To: <564B5C64.908@redhat.com> References: <564B5C64.908@redhat.com> Message-ID: <56562C99.7060505@redhat.com> On 11/17/2015 10:57 AM, Endi Sukma Dewata wrote: > The pki-cert and pki-server-subsystem man pages have been updated > to include recent changes. > > https://fedorahosted.org/pki/ticket/456 ACKed by alee. Pushed to master. -- Endi S. Dewata From ftweedal at redhat.com Wed Nov 25 23:47:42 2015 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 26 Nov 2015 09:47:42 +1000 Subject: [Pki-devel] [PATCH] 0056 Avoid superfluous ConfigStore commit during profile creation In-Reply-To: <1448481159.22418.1.camel@redhat.com> References: <20151123060425.GN5336@dhcp-40-8.bne.redhat.com> <1448481159.22418.1.camel@redhat.com> Message-ID: <20151125234742.GE5336@dhcp-40-8.bne.redhat.com> On Wed, Nov 25, 2015 at 02:52:39PM -0500, Ade Lee wrote: > ACK > Thanks; pushed to master (c44d643c8f1f1b34004e8a1c5eedbcb75e46860d) > On Mon, 2015-11-23 at 16:04 +1000, Fraser Tweedale wrote: > > The attached patch is a drive-by improvement during analysis of > > https://fedorahosted.org/pki/ticket/1700. > > > > Thanks, > > Fraser > > _______________________________________________ > > Pki-devel mailing list > > Pki-devel at redhat.com > > https://www.redhat.com/mailman/listinfo/pki-devel From jpazdzio at redhat.com Thu Nov 12 07:34:13 2015 From: jpazdzio at redhat.com (Jan Pazdziora) Date: Thu, 12 Nov 2015 07:34:13 -0000 Subject: [Pki-devel] SPNEGO for Dogtag In-Reply-To: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> References: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> Message-ID: <20151112073411.GC13367@redhat.com> On Thu, Nov 12, 2015 at 04:40:10PM +1000, Fraser Tweedale wrote: > > Just an update on my investigations of doing SPNEGO authn to > Dogtag/Tomcat and a summary of what I think I'll need to do to get > it going. > > Even for a trivial app configuring Tomcat to do SPNEGO is pretty > awful. I made some notes[1] on this and I'll turn that into a blog > post later, once I have a Realm story to go with it (I'm thinking > mod_lookup_identity for Tomcat; more on that below). > > [1] https://github.com/frasertweedale/notes-redhat/blob/master/tomcat.rst > > > The unfortunate case is that the `Authenticator' interface does not > chain, hence we already have our own `SSLAuthenticatorWithFallback' > authenticator valve implementation which we use to fall back to > Basic authentication when there is no client certificate. > > I see two general approaches to the "multiple authentication > options" problem. > > a) a "parallel deployment" in a separate where we host the > app in parallel at '//spnego/...' or > '/spnego//...' and use the `SpnegoAuthenticator' valve. > This is a pretty "heavyweight" approach and I believe sessions from > one do not flow to the other, and a whole separate CMS application > is running. So it does not seem viable. > > b) `SSLAuthenticatorWithFallback' becomes "SSL or SPNEGO or Basic" > valve. The problem here is that protocol-wise there is no > indication on initial request whether client can do SPNEGO or not, > so it is not clear how server should respond. An example of how > this could be resolved is the client providing a '?spnego=1' query > param; ugly but it should work, and w.r.t. REST API the client need > only do it on /rest/account/login and use their session cookie > thereafter, so I don't think it is too onerous. > > So it looks like (b) but if someone can suggest another approach I'm > listening! > > > As for the Realm, the current implementation is a bit unorthodox - > we use our own ProxyRealm class and "inject" a PKIRealm into it. > I'm not sure why this approach is needed becaues PKIRealm does not > need any configuration either on instantiation or afterwards. > > I think the best approach here would be to use CombinedRealm[2] and > add PKIRealm as well as whatever other Realms are needed. For IPA > usage I am leaning toward the mod_lookup_identity approach; given > principal name look up roles (groups) via D-Bus. > > [2] https://tomcat.apache.org/tomcat-7.0-doc/config/realm.html#Combined_Realm_-_org.apache.catalina.realm.CombinedRealm > > Jan: does this approach make sense to you and do you know of any > projects to do this with Tomcat already? I'm a bit confused. Do you try to do the authentication in tomcat or do you try to front-end tomcat with Apache? If you do it in tomcat itself (like the investigation seems to suggest), what is the role of mod_lookup_identity here? -- Jan Pazdziora | adelton at #ipa*, #brno Senior Principal Software Engineer, Identity Management Engineering, Red Hat From jpazdzio at redhat.com Thu Nov 12 10:20:13 2015 From: jpazdzio at redhat.com (Jan Pazdziora) Date: Thu, 12 Nov 2015 10:20:13 -0000 Subject: [Pki-devel] SPNEGO for Dogtag In-Reply-To: <20151112094625.GT5336@dhcp-40-8.bne.redhat.com> References: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> <20151112073411.GC13367@redhat.com> <20151112094625.GT5336@dhcp-40-8.bne.redhat.com> Message-ID: <20151112102010.GE13367@redhat.com> On Thu, Nov 12, 2015 at 07:46:25PM +1000, Fraser Tweedale wrote: > On Thu, Nov 12, 2015 at 08:34:11AM +0100, Jan Pazdziora wrote: > > > > I'm a bit confused. Do you try to do the authentication in tomcat > > or do you try to front-end tomcat with Apache? If you do it in tomcat > > itself (like the investigation seems to suggest), what is the role > > of mod_lookup_identity here? > > No Apache, no mod_lookup_identity. But a Tomcat Realm > implementation that does a lookup of principal info via SSSD via > D-Bus, like what mod_lookup_identity does for Apache. In general, that is what we tell people not to do. The goal is to use external authenticatication and identity operations in frontend server (Apache) and applications / frameworks consuming the results. The benefit of this approach is that you don't have to reimplement things when you say want to support additional protocol -- hopefully, the platform will do it for you in the form of Apache modules. The mod_auth_openidc is a prime example -- ideally, any application that consumes results of external authentication (which was initially done for example to support Kerberos) gets OpenId Connect for free, just by reconfiguring the frontend Apache HTTP Server. -- Jan Pazdziora | adelton at #ipa*, #brno Senior Principal Software Engineer, Identity Management Engineering, Red Hat From jpazdzio at redhat.com Thu Nov 12 11:46:57 2015 From: jpazdzio at redhat.com (Jan Pazdziora) Date: Thu, 12 Nov 2015 11:46:57 -0000 Subject: [Pki-devel] SPNEGO for Dogtag In-Reply-To: <20151112112619.GW5336@dhcp-40-8.bne.redhat.com> References: <20151112064010.GS5336@dhcp-40-8.bne.redhat.com> <20151112073411.GC13367@redhat.com> <20151112094625.GT5336@dhcp-40-8.bne.redhat.com> <20151112102010.GE13367@redhat.com> <20151112112619.GW5336@dhcp-40-8.bne.redhat.com> Message-ID: <20151112114652.GI13367@redhat.com> On Thu, Nov 12, 2015 at 09:26:19PM +1000, Fraser Tweedale wrote: > > I was wondering if you (or others) were aware of any existing > implementation for Tomcat. I'm not. -- Jan Pazdziora | adelton at #ipa*, #brno Senior Principal Software Engineer, Identity Management Engineering, Red Hat