[Pki-devel] [PATCH] Lightweight CAs
Fraser Tweedale
ftweedal at redhat.com
Fri Sep 18 04:58:48 UTC 2015
On Mon, Sep 14, 2015 at 05:25:00PM +1000, Fraser Tweedale wrote:
> The latest lightweight CAs (f.k.a. Sub-CAs) patches are attached.
> This cut has significant changes and additions including:
>
> - CAs are now stored in a flat structure with references to parents
>
> - CAs are now identified by "AuthorityID" (which for now is a UUID
> underneath). Many variables, method names and user-visible
> strings were updated accordingly. Out with "caRef" terminology.
>
> - "Sub-CA" terminology is (mostly) out; "Authority" is in. This is
> to support lightweight CAs that are not descendents of top-level
> CA (which can be implemented later).
>
> - ca-cert-request-submit command and related client / service
> classes were updated to add "authority" parameter
>
> - Some more specific use of exception (including some new exception
> classes) to indicate / catch particular errors.
>
> - More appropriate HTTP status codes return when client has send
> invalid data (400), referenced unknown authority (404) or attempts
> to create an authority with Subject DN already uesd by another
> authority (409 Conflict)
>
> - LDAP entry now gets added before key generation and signing. If
> something goes wrong, the DB entry is removed in the catch block.
>
> There are still notable gaps in functionality that are in progress
> or will be implemented soon:
>
> - Audit log events
> - Resources to enable/disable/delete authority
> - Resources to access cert and pkcs7 chain of authority
> - Keygen params
> - Param to specify token on which to generate authority key
>
Latest patches attached. Along with some minor improvements to the
original patches, three new patches 0048-0050 add ability to enable
and disable lightweight CAs.
Commit messages for the earlier patches have also been updated for
consistency and to include ticket references.
Thanks,
Fraser
-------------- next part --------------
From a8ce408791f67e03191cd0ebb4c623297f0ec11e Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Wed, 28 Jan 2015 02:41:10 -0500
Subject: [PATCH] Lightweight CAs: initial support
This commit adds initial support for "lightweight CAs" - CAs that
inhabit an existing CA instance and share the request queue and
certificate database of the "top-level CA".
We initially support only sub-CAs under the top-level CA - either
direct sub-CAs or nested. The general design will support hosting
unrelated CAs but creation or import of unrelated CAs is not yet
implemented.
Part of: https://fedorahosted.org/pki/ticket/1213
---
base/ca/shared/conf/db.ldif | 5 +
base/ca/src/com/netscape/ca/CAService.java | 53 ++-
.../src/com/netscape/ca/CertificateAuthority.java | 438 ++++++++++++++++++---
base/ca/src/com/netscape/ca/SigningUnit.java | 22 +-
.../dogtagpki/server/ca/rest/AuthorityService.java | 183 +++++++++
.../dogtagpki/server/ca/rest/CAApplication.java | 3 +
.../netscape/certsrv/authority/AuthorityData.java | 109 +++++
.../certsrv/authority/AuthorityResource.java | 36 ++
.../src/com/netscape/certsrv/ca/AuthorityID.java | 36 ++
.../netscape/certsrv/ca/CANotFoundException.java | 12 +
.../src/com/netscape/certsrv/ca/ICAService.java | 11 +-
.../netscape/certsrv/ca/ICertificateAuthority.java | 44 +++
.../certsrv/ca/IssuerUnavailableException.java | 13 +
.../netscape/certsrv/profile/IEnrollProfile.java | 5 +
.../netscape/certsrv/security/ISigningUnit.java | 8 +
.../cms/profile/common/CAEnrollProfile.java | 11 +-
.../netscape/cms/profile/common/EnrollProfile.java | 3 +
.../def/AuthorityKeyIdentifierExtDefault.java | 19 +-
.../netscape/cms/profile/def/CAEnrollDefault.java | 4 +-
.../cms/servlet/cert/EnrollmentProcessor.java | 22 ++
.../com/netscape/cms/servlet/csadmin/CertUtil.java | 38 +-
base/server/share/conf/schema-subCA.ldif | 5 +
base/server/share/conf/schema.ldif | 10 +
23 files changed, 983 insertions(+), 107 deletions(-)
create mode 100644 base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
create mode 100644 base/common/src/com/netscape/certsrv/authority/AuthorityData.java
create mode 100644 base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
create mode 100644 base/common/src/com/netscape/certsrv/ca/AuthorityID.java
create mode 100644 base/common/src/com/netscape/certsrv/ca/CANotFoundException.java
create mode 100644 base/common/src/com/netscape/certsrv/ca/IssuerUnavailableException.java
create mode 100644 base/server/share/conf/schema-subCA.ldif
diff --git a/base/ca/shared/conf/db.ldif b/base/ca/shared/conf/db.ldif
index 8a2e0b07274a83b317fb1ba56e8ef32b96857118..704b8d11be7dcffd7d57fb3ec90c11f3c0ef9cbc 100644
--- a/base/ca/shared/conf/db.ldif
+++ b/base/ca/shared/conf/db.ldif
@@ -164,3 +164,8 @@ dn: ou=certificateProfiles,ou=ca,{rootSuffix}
objectClass: top
objectClass: organizationalUnit
ou: certificateProfiles
+
+dn: ou=authorities,ou=ca,{rootSuffix}
+objectClass: top
+objectClass: organizationalUnit
+ou: authorities
diff --git a/base/ca/src/com/netscape/ca/CAService.java b/base/ca/src/com/netscape/ca/CAService.java
index 36f0bd592e333a276da84662c1e64a2921c5e7d2..a49d641cec839b4dac33fe7a6be49bf86c3560a8 100644
--- a/base/ca/src/com/netscape/ca/CAService.java
+++ b/base/ca/src/com/netscape/ca/CAService.java
@@ -65,7 +65,9 @@ import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.IConfigStore;
import com.netscape.certsrv.base.MetaInfo;
import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.ca.AuthorityID;
import com.netscape.certsrv.ca.ECAException;
+import com.netscape.certsrv.ca.CANotFoundException;
import com.netscape.certsrv.ca.ICAService;
import com.netscape.certsrv.ca.ICRLIssuingPoint;
import com.netscape.certsrv.ca.ICertificateAuthority;
@@ -565,18 +567,15 @@ public class CAService implements ICAService, IService {
/// CA related routines.
///
- public X509CertImpl issueX509Cert(X509CertInfo certi)
- throws EBaseException {
- return issueX509Cert(certi, null, null);
- }
-
/**
* issue cert for enrollment.
*/
- public X509CertImpl issueX509Cert(X509CertInfo certi, String profileId, String rid)
+ public X509CertImpl issueX509Cert(
+ AuthorityID aid, X509CertInfo certi,
+ String profileId, String rid)
throws EBaseException {
CMS.debug("issueX509Cert");
- X509CertImpl certImpl = issueX509Cert("", certi, false, null);
+ X509CertImpl certImpl = issueX509Cert(aid, "", certi, false, null);
CMS.debug("storeX509Cert " + certImpl.getSerialNumber());
storeX509Cert(profileId, rid, certImpl);
@@ -615,9 +614,21 @@ public class CAService implements ICAService, IService {
* renewal is expected to have original cert serial no. in cert info
* field.
*/
- X509CertImpl issueX509Cert(String rid, X509CertInfo certi,
- boolean renewal, BigInteger oldSerialNo)
- throws EBaseException {
+ X509CertImpl issueX509Cert(
+ String rid, X509CertInfo certi,
+ boolean renewal, BigInteger oldSerialNo
+ ) throws EBaseException {
+ return issueX509Cert(null, rid, certi, renewal, oldSerialNo);
+ }
+
+ private X509CertImpl issueX509Cert(
+ AuthorityID aid, String rid, X509CertInfo certi,
+ boolean renewal, BigInteger oldSerialNo
+ ) throws EBaseException {
+ ICertificateAuthority ca = mCA.getCA(aid);
+ if (ca == null)
+ throw new CANotFoundException("No such CA: " + aid);
+
String algname = null;
X509CertImpl cert = null;
@@ -642,7 +653,7 @@ public class CAService implements ICAService, IService {
// set default cert version. If policies added a extensions
// the version would already be set to version 3.
if (certi.get(X509CertInfo.VERSION) == null) {
- certi.set(X509CertInfo.VERSION, mCA.getDefaultCertVersion());
+ certi.set(X509CertInfo.VERSION, ca.getDefaultCertVersion());
}
// set default validity if not set.
@@ -665,7 +676,7 @@ public class CAService implements ICAService, IService {
}
begin = CMS.getCurrentDate();
- end = new Date(begin.getTime() + mCA.getDefaultValidity());
+ end = new Date(begin.getTime() + ca.getDefaultValidity());
certi.set(CertificateValidity.NAME,
new CertificateValidity(begin, end));
}
@@ -705,7 +716,7 @@ public class CAService implements ICAService, IService {
}
Date caNotAfter =
- mCA.getSigningUnit().getCertImpl().getNotAfter();
+ ca.getSigningUnit().getCertImpl().getNotAfter();
if (begin.after(caNotAfter)) {
mCA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_PAST_VALIDITY"));
@@ -714,7 +725,7 @@ public class CAService implements ICAService, IService {
if (end.after(caNotAfter)) {
if (!is_ca) {
- if (!mCA.isEnablePastCATime()) {
+ if (!ca.isEnablePastCATime()) {
end = caNotAfter;
certi.set(CertificateValidity.NAME,
new CertificateValidity(begin, caNotAfter));
@@ -734,7 +745,7 @@ public class CAService implements ICAService, IService {
certi.get(X509CertInfo.ALGORITHM_ID);
if (algor == null || algor.toString().equals(CertInfo.SERIALIZE_ALGOR.toString())) {
- algname = mCA.getSigningUnit().getDefaultAlgorithm();
+ algname = ca.getSigningUnit().getDefaultAlgorithm();
algid = AlgorithmId.get(algname);
certi.set(X509CertInfo.ALGORITHM_ID,
new CertificateAlgorithmId(algid));
@@ -820,16 +831,16 @@ public class CAService implements ICAService, IService {
}
try {
- if (mCA.getIssuerObj() != null) {
+ if (ca.getIssuerObj() != null) {
// this ensures the isserDN has the same encoding as the
// subjectDN of the CA signing cert
CMS.debug("CAService: issueX509Cert: setting issuerDN using exact CA signing cert subjectDN encoding");
certi.set(X509CertInfo.ISSUER,
- mCA.getIssuerObj());
+ ca.getIssuerObj());
} else {
- CMS.debug("CAService: issueX509Cert: mCA.getIssuerObj() is null, creating new CertificateIssuerName");
+ CMS.debug("CAService: issueX509Cert: ca.getIssuerObj() is null, creating new CertificateIssuerName");
certi.set(X509CertInfo.ISSUER,
- new CertificateIssuerName(mCA.getX500Name()));
+ new CertificateIssuerName(ca.getX500Name()));
}
} catch (CertificateException e) {
mCA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_SET_ISSUER", e.toString()));
@@ -861,8 +872,8 @@ public class CAService implements ICAService, IService {
}
}
- CMS.debug("About to mCA.sign cert.");
- cert = mCA.sign(certi, algname);
+ CMS.debug("About to ca.sign cert.");
+ cert = ca.sign(certi, algname);
return cert;
}
diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java
index acf07b9bde2a05f7c62740293a0c66cf92f50771..5df7c77612132d2dc52ca43060d7ba782d9388cc 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
@@ -25,15 +25,20 @@ import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
@@ -53,6 +58,13 @@ import netscape.security.x509.X509CertInfo;
import netscape.security.x509.X509ExtensionException;
import netscape.security.x509.X509Key;
+import netscape.ldap.LDAPAttribute;
+import netscape.ldap.LDAPAttributeSet;
+import netscape.ldap.LDAPConnection;
+import netscape.ldap.LDAPEntry;
+import netscape.ldap.LDAPException;
+import netscape.ldap.LDAPSearchResults;
+
import org.mozilla.jss.CryptoManager;
import org.mozilla.jss.asn1.ASN1Util;
import org.mozilla.jss.asn1.GeneralizedTime;
@@ -60,6 +72,9 @@ import org.mozilla.jss.asn1.INTEGER;
import org.mozilla.jss.asn1.InvalidBERException;
import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
import org.mozilla.jss.asn1.OCTET_STRING;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.KeyPairAlgorithm;
+import org.mozilla.jss.crypto.KeyPairGenerator;
import org.mozilla.jss.crypto.SignatureAlgorithm;
import org.mozilla.jss.crypto.TokenException;
import org.mozilla.jss.pkix.cert.Extension;
@@ -73,15 +88,20 @@ import com.netscape.certsrv.base.IConfigStore;
import com.netscape.certsrv.base.ISubsystem;
import com.netscape.certsrv.base.Nonces;
import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.CANotFoundException;
import com.netscape.certsrv.ca.ECAException;
import com.netscape.certsrv.ca.ICRLIssuingPoint;
import com.netscape.certsrv.ca.ICertificateAuthority;
+import com.netscape.certsrv.ca.IssuerUnavailableException;
import com.netscape.certsrv.dbs.IDBSubsystem;
+import com.netscape.certsrv.dbs.IDBSSession;
import com.netscape.certsrv.dbs.certdb.ICertRecord;
import com.netscape.certsrv.dbs.certdb.ICertificateRepository;
import com.netscape.certsrv.dbs.crldb.ICRLRepository;
import com.netscape.certsrv.dbs.replicadb.IReplicaIDRepository;
import com.netscape.certsrv.ldap.ELdapException;
+import com.netscape.certsrv.ldap.ILdapConnFactory;
import com.netscape.certsrv.logging.ILogger;
import com.netscape.certsrv.ocsp.IOCSPService;
import com.netscape.certsrv.policy.IPolicyProcessor;
@@ -96,6 +116,8 @@ import com.netscape.certsrv.request.IRequestScheduler;
import com.netscape.certsrv.request.IService;
import com.netscape.certsrv.security.ISigningUnit;
import com.netscape.certsrv.util.IStatsSubsystem;
+import com.netscape.cms.servlet.csadmin.CertUtil;
+import com.netscape.cmscore.base.PropConfigStore;
import com.netscape.cmscore.dbs.CRLRepository;
import com.netscape.cmscore.dbs.CertRecord;
import com.netscape.cmscore.dbs.CertificateRepository;
@@ -106,6 +128,7 @@ import com.netscape.cmscore.listeners.ListenerPlugin;
import com.netscape.cmscore.request.RequestSubsystem;
import com.netscape.cmscore.security.KeyCertUtil;
import com.netscape.cmscore.util.Debug;
+import com.netscape.cmsutil.crypto.CryptoUtil;
import com.netscape.cmsutil.ocsp.BasicOCSPResponse;
import com.netscape.cmsutil.ocsp.CertID;
import com.netscape.cmsutil.ocsp.CertStatus;
@@ -123,6 +146,9 @@ import com.netscape.cmsutil.ocsp.SingleResponse;
import com.netscape.cmsutil.ocsp.TBSRequest;
import com.netscape.cmsutil.ocsp.UnknownInfo;
+import org.apache.commons.lang.StringUtils;
+
+
/**
* A class represents a Certificate Authority that is
* responsible for certificate specific operations.
@@ -136,6 +162,12 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
public final static OBJECT_IDENTIFIER OCSP_NONCE = new OBJECT_IDENTIFIER("1.3.6.1.5.5.7.48.1.2");
+ private static final Map<AuthorityID, ICertificateAuthority> caMap = new TreeMap();
+ protected CertificateAuthority topCA = null;
+ protected AuthorityID authorityID = null;
+ protected AuthorityID authorityParentID = null;
+ protected String authorityDescription = null;
+
protected ISubsystem mOwner = null;
protected IConfigStore mConfig = null;
protected ILogger mLogger = CMS.getLogger();
@@ -234,6 +266,30 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
* Constructs a CA subsystem.
*/
public CertificateAuthority() {
+ topCA = this;
+ }
+
+ /**
+ * Construct and initialise a sub-CA
+ */
+ private CertificateAuthority(
+ CertificateAuthority topCA,
+ AuthorityID aid,
+ AuthorityID parentAID,
+ String signingKeyNickname,
+ String authorityDescription
+ ) throws EBaseException {
+ setId(topCA.getId());
+ this.topCA = topCA;
+ this.authorityID = aid;
+ this.authorityParentID = parentAID;
+ this.authorityDescription = authorityDescription;
+ mNickname = signingKeyNickname;
+ init(topCA.mOwner, topCA.mConfig);
+ }
+
+ private boolean isTopCA() {
+ return authorityID == null;
}
/**
@@ -334,8 +390,22 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
mOwner = owner;
mConfig = config;
- // init cert & crl database.
- initCaDatabases();
+ // init cert & crl database
+ initCertDatabase();
+ initCrlDatabase();
+
+ // init replica id repository
+ if (isTopCA()) {
+ String replicaReposDN = mConfig.getString(PROP_REPLICAID_DN, null);
+ if (replicaReposDN == null) {
+ replicaReposDN = "ou=Replica," + getDBSubsystem().getBaseDN();
+ }
+ mReplicaRepot = new ReplicaIDRepository(
+ DBSubsystem.getInstance(), 1, replicaReposDN);
+ CMS.debug("Replica Repot inited");
+ } else {
+ mReplicaRepot = topCA.mReplicaRepot;
+ }
// init signing unit & CA cert.
try {
@@ -358,51 +428,37 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
if (CMS.isPreOpMode())
return;
- // set certificate status to 10 minutes
- mCertRepot.setCertStatusUpdateInterval(
+ /* The top-level CA owns these resources so skip these
+ * steps for sub-CAs.
+ */
+ if (isTopCA()) {
+ /* These methods configure and start threads related to
+ * CertificateRepository. Ideally all of the config would
+ * be pushed into CertificateRepository constructor and a
+ * single 'start' method would start the threads.
+ */
+ // set certificate status to 10 minutes
+ mCertRepot.setCertStatusUpdateInterval(
mRequestQueue.getRequestRepository(),
mConfig.getInteger("certStatusUpdateInterval", 10 * 60),
mConfig.getBoolean("listenToCloneModifications", false));
- mCertRepot.setConsistencyCheck(
+ mCertRepot.setConsistencyCheck(
mConfig.getBoolean("ConsistencyCheck", false));
- mCertRepot.setSkipIfInConsistent(
+ mCertRepot.setSkipIfInConsistent(
mConfig.getBoolean("SkipIfInConsistent", false));
- // set serial number update task to run every 10 minutes
- mCertRepot.setSerialNumberUpdateInterval(
+ // set serial number update task to run every 10 minutes
+ mCertRepot.setSerialNumberUpdateInterval(
mRequestQueue.getRequestRepository(),
mConfig.getInteger("serialNumberUpdateInterval", 10 * 60));
- mService.init(config.getSubStore("connector"));
+ mService.init(config.getSubStore("connector"));
- initMiscellaneousListeners();
-
- // instantiate CRL publisher
- IConfigStore cpStore = null;
-
- mByName = config.getBoolean("byName", true);
-
- cpStore = config.getSubStore("crlPublisher");
- if (cpStore != null && cpStore.size() > 0) {
- String publisherClass = cpStore.getString("class");
-
- if (publisherClass != null) {
- try {
- @SuppressWarnings("unchecked")
- Class<ICRLPublisher> pc = (Class<ICRLPublisher>) Class.forName(publisherClass);
-
- mCRLPublisher = pc.newInstance();
- mCRLPublisher.init(this, cpStore);
- } catch (ClassNotFoundException ee) {
- log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_CA_NO_PUBLISHER", ee.toString()));
- } catch (IllegalAccessException ee) {
- log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_CA_NO_PUBLISHER", ee.toString()));
- } catch (InstantiationException ee) {
- log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_CA_NO_PUBLISHER", ee.toString()));
- }
- }
+ initMiscellaneousListeners();
}
+ initCRLPublisher();
+
// initialize publisher processor (publish remote admin
// rely on this subsystem, so it has to be initialized)
initPublish();
@@ -412,6 +468,9 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
// being functional.
initCRL();
+ if (isTopCA())
+ loadSubCAs();
+
} catch (EBaseException e) {
if (CMS.isPreOpMode())
return;
@@ -420,6 +479,37 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
}
}
+ private void initCRLPublisher() throws EBaseException {
+ // instantiate CRL publisher
+ if (!isTopCA()) {
+ mByName = topCA.mByName;
+ mCRLPublisher = topCA.mCRLPublisher;
+ return;
+ }
+
+ mByName = mConfig.getBoolean("byName", true);
+ IConfigStore cpStore = mConfig.getSubStore("crlPublisher");
+ if (cpStore != null && cpStore.size() > 0) {
+ String publisherClass = cpStore.getString("class");
+
+ if (publisherClass != null) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<ICRLPublisher> pc = (Class<ICRLPublisher>) Class.forName(publisherClass);
+
+ mCRLPublisher = pc.newInstance();
+ mCRLPublisher.init(this, cpStore);
+ } catch (ClassNotFoundException ee) {
+ log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_CA_NO_PUBLISHER", ee.toString()));
+ } catch (IllegalAccessException ee) {
+ log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_CA_NO_PUBLISHER", ee.toString()));
+ } catch (InstantiationException ee) {
+ log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CA_CA_NO_PUBLISHER", ee.toString()));
+ }
+ }
+ }
+ }
+
/**
* return CA's request queue processor
*/
@@ -540,14 +630,11 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
mService.startup();
mRequestQueue.recover();
- // Note that this could be null.
-
- // setup Admin operations
-
- initNotificationListeners();
-
- startPublish();
- // startCRL();
+ if (isTopCA()) {
+ // setup Admin operations
+ initNotificationListeners();
+ startPublish();
+ }
}
/**
@@ -555,6 +642,8 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
* <P>
*/
public void shutdown() {
+ if (!isTopCA()) return; // sub-CAs don't own these resources
+
Enumeration<ICRLIssuingPoint> enums = mCRLIssuePoints.elements();
while (enums.hasMoreElements()) {
CRLIssuingPoint point = (CRLIssuingPoint) enums.nextElement();
@@ -1228,13 +1317,13 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
mIssuerObj = new CertificateIssuerName((X500Name)mSubjectObj.get(CertificateIssuerName.DN_NAME));
}
- mSigningUnit.init(this, caSigningCfg);
+ mSigningUnit.init(this, caSigningCfg, mNickname);
CMS.debug("CA signing unit inited");
// for identrus
IConfigStore CrlStore = mConfig.getSubStore(PROP_CRL_SIGNING_SUBSTORE);
- if (CrlStore != null && CrlStore.size() > 0) {
+ if (isTopCA() && CrlStore != null && CrlStore.size() > 0) {
mCRLSigningUnit = new SigningUnit();
mCRLSigningUnit.init(this, mConfig.getSubStore(PROP_CRL_SIGNING_SUBSTORE));
} else {
@@ -1304,7 +1393,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
IConfigStore OCSPStore = mConfig.getSubStore(PROP_OCSP_SIGNING_SUBSTORE);
- if (OCSPStore != null && OCSPStore.size() > 0) {
+ if (isTopCA() && OCSPStore != null && OCSPStore.size() > 0) {
mOCSPSigningUnit = new SigningUnit();
mOCSPSigningUnit.init(this, mConfig.getSubStore(PROP_OCSP_SIGNING_SUBSTORE));
CMS.debug("Separate OCSP signing unit inited");
@@ -1443,8 +1532,13 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
/**
* init cert & crl database
*/
- private void initCaDatabases()
+ private void initCertDatabase()
throws EBaseException {
+ if (!isTopCA()) {
+ mCertRepot = topCA.mCertRepot;
+ return;
+ }
+
int certdb_inc = mConfig.getInteger(PROP_CERTDB_INC, 5);
String certReposDN = mConfig.getString(PROP_CERT_REPOS_DN, null);
@@ -1471,8 +1565,17 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
mCertRepot.setTransitRecordPageSize(transitRecordPageSize);
CMS.debug("Cert Repot inited");
+ }
- // init crl repot.
+ /**
+ * init cert & crl database
+ */
+ private void initCrlDatabase()
+ throws EBaseException {
+ if (!isTopCA()) {
+ mCRLRepot = topCA.mCRLRepot;
+ return;
+ }
int crldb_inc = mConfig.getInteger(PROP_CRLDB_INC, 5);
@@ -1482,14 +1585,6 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
"ou=crlIssuingPoints, ou=" + getId() + ", " +
getDBSubsystem().getBaseDN());
CMS.debug("CRL Repot inited");
-
- String replicaReposDN = mConfig.getString(PROP_REPLICAID_DN, null);
- if (replicaReposDN == null) {
- replicaReposDN = "ou=Replica," + getDBSubsystem().getBaseDN();
- }
- mReplicaRepot = new ReplicaIDRepository(
- DBSubsystem.getInstance(), 1, replicaReposDN);
- CMS.debug("Replica Repot inited");
}
private void startPublish()
@@ -1513,6 +1608,11 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
private void initPublish()
throws EBaseException {
+ if (!isTopCA()) {
+ mPublisherProcessor = topCA.mPublisherProcessor;
+ return;
+ }
+
IConfigStore c = null;
try {
@@ -1676,6 +1776,15 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
private void initRequestQueue()
throws EBaseException {
+ if (!isTopCA()) {
+ mPolicy = topCA.mPolicy;
+ mService = topCA.mService;
+ mNotify = topCA.mNotify;
+ mPNotify = topCA.mPNotify;
+ mRequestQueue = topCA.mRequestQueue;
+ return;
+ }
+
mPolicy = new CAPolicy();
mPolicy.init(this, mConfig.getSubStore(PROP_POLICY));
CMS.debug("CA policy inited");
@@ -1734,6 +1843,11 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
@SuppressWarnings("unchecked")
private void initCRL()
throws EBaseException {
+ if (!isTopCA()) {
+ mCRLIssuePoints = topCA.mCRLIssuePoints;
+ mMasterCRLIssuePoint = topCA.mMasterCRLIssuePoint;
+ return;
+ }
IConfigStore crlConfig = mConfig.getSubStore(PROP_CRL_SUBSTORE);
if ((crlConfig == null) || (crlConfig.size() <= 0)) {
@@ -1799,6 +1913,63 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
log(ILogger.LL_INFO, "CRL Issuing Points inited");
}
+ /**
+ * Find, instantiate and register sub-CAs.
+ *
+ * This method must only be called by the top-level CA.
+ */
+ private synchronized void loadSubCAs() throws EBaseException {
+ ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory("loadSubCAs");
+ dbFactory.init(CMS.getConfigStore().getSubStore("internaldb"));
+ LDAPConnection conn = dbFactory.getConn();
+
+ String searchDN = "ou=authorities,ou=" + getId()
+ + "," + getDBSubsystem().getBaseDN();
+ LDAPSearchResults results = null;
+ try {
+ results = conn.search(
+ searchDN, LDAPConnection.SCOPE_ONE,
+ "(objectclass=authority)", null, false);
+
+ while (results.hasMoreElements()) {
+ LDAPEntry entry = results.next();
+ LDAPAttribute aidAttr = entry.getAttribute("authorityID");
+ LDAPAttribute parentAIDAttr = entry.getAttribute("authorityParentID");
+ LDAPAttribute nickAttr = entry.getAttribute("authorityKeyNickname");
+ if (aidAttr == null || nickAttr == null)
+ throw new ECAException("Malformed sub-CA object: " + entry.getDN());
+ String keyNick = (String) nickAttr.getStringValues().nextElement();
+
+ AuthorityID aid = new AuthorityID((String)
+ aidAttr.getStringValues().nextElement());
+ AuthorityID parentAID = null;
+ if (parentAIDAttr != null)
+ parentAID = new AuthorityID((String)
+ parentAIDAttr.getStringValues().nextElement());
+
+ String desc = null;
+ LDAPAttribute descAttr = entry.getAttribute("description");
+ if (descAttr != null)
+ desc = (String) descAttr.getStringValues().nextElement();
+
+ CertificateAuthority subCA = new CertificateAuthority(
+ this, aid, parentAID, keyNick, desc);
+ caMap.put(aid, subCA);
+ }
+ } catch (LDAPException e) {
+ if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
+ CMS.debug(
+ "Missing lightweight CAs container '" + searchDN
+ + "'. Disabling lightweight CAs.");
+ } else {
+ throw new ECAException("Failed to execute LDAP search for lightweight CAs: " + e);
+ }
+ } finally {
+ dbFactory.returnConn(conn);
+ dbFactory.reset();
+ }
+ }
+
public String getOfficialName() {
return OFFICIAL_NAME;
}
@@ -2083,4 +2254,159 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
return new SingleResponse(cid, certStatus, thisUpdate, nextUpdate);
}
+
+ /**
+ * Enumerate all sub-CAs.
+ */
+ public synchronized List<ICertificateAuthority> getCAs() {
+ List<ICertificateAuthority> cas = new ArrayList();
+ for (ICertificateAuthority ca : caMap.values()) {
+ cas.add(ca);
+ }
+ return cas;
+ }
+
+ /**
+ * Get the sub-CA by ID.
+ *
+ * @param aid The ID of the CA to retrieve, or null
+ * to retreive the top-level CA.
+ *
+ * @return the authority, or null if not found
+ */
+ public ICertificateAuthority getCA(AuthorityID aid) {
+ return aid == null ? topCA : caMap.get(aid);
+ }
+
+ public AuthorityID getAuthorityID() {
+ return authorityID;
+ }
+
+ public AuthorityID getAuthorityParentID() {
+ return authorityParentID;
+ }
+
+ public String getAuthorityDescription() {
+ return authorityDescription;
+ }
+
+ /**
+ * Create a new sub-CA.
+ *
+ * @param subjectDN Subject DN for new CA
+ * @param parentAID ID of parent CA, or null to create
+ * sub-CA immediately below top-level CA
+ * @param description Optional string description of CA
+ */
+ public ICertificateAuthority createCA(
+ String subjectDN, AuthorityID parentAID,
+ String description)
+ throws EBaseException {
+ ICertificateAuthority parentCA = getCA(parentAID);
+ if (parentCA == null)
+ throw new CANotFoundException(
+ "Parent CA \"" + parentAID + "\" does not exist");
+
+ ICertificateAuthority subCA = parentCA.createSubCA(
+ subjectDN, description);
+ synchronized (this) {
+ caMap.put(subCA.getAuthorityID(), subCA);
+ }
+ return subCA;
+ }
+
+ /**
+ * Create a new sub-CA IMMEDIATELY beneath this one.
+ *
+ * This method DOES NOT add the new CA to caMap; it is the
+ * caller's responsibility.
+ */
+ public ICertificateAuthority createSubCA(
+ String subjectDN, String description)
+ throws EBaseException {
+
+ // check uniqueness of DN
+ X500Name subjectX500Name = null;
+ try {
+ subjectX500Name = new X500Name(subjectDN);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ "Invalid Subject DN: " + subjectDN);
+ }
+ for (ICertificateAuthority ca : caMap.values()) {
+ if (ca.getX500Name().equals(subjectX500Name))
+ throw new IssuerUnavailableException(
+ "CA with Subject DN '" + subjectDN + "' already exists");
+ }
+
+ // generate authority ID and nickname
+ AuthorityID aid = new AuthorityID();
+ String aidString = aid.toString();
+ String nickname = topCA.getNickname() + " " + aidString;
+
+ // build database entry
+ String dn = "cn=" + aidString + ",ou=authorities,ou="
+ + getId() + "," + getDBSubsystem().getBaseDN();
+ CMS.debug("createSubCA: DN = " + dn);
+ LDAPAttribute[] attrs = {
+ new LDAPAttribute("objectclass", "authority"),
+ new LDAPAttribute("cn", aidString),
+ new LDAPAttribute("authorityID", aidString),
+ new LDAPAttribute("authorityKeyNickname", nickname)
+ };
+ LDAPAttributeSet attrSet = new LDAPAttributeSet(attrs);
+ if (this.authorityID != null)
+ attrSet.add(new LDAPAttribute(
+ "authorityParentID", this.authorityID.toString()));
+ if (description != null)
+ attrSet.add(new LDAPAttribute("description", description));
+ LDAPEntry ldapEntry = new LDAPEntry(dn, attrSet);
+
+ // connect to database
+ ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory("createSubCA");
+ dbFactory.init(CMS.getConfigStore().getSubStore("internaldb"));
+ LDAPConnection conn = dbFactory.getConn();
+
+ try {
+ // add entry to database
+ conn.add(ldapEntry);
+
+ try {
+ // Generate sub-CA signing key
+ CryptoManager cryptoManager = CryptoManager.getInstance();
+ // TODO read PROP_TOKEN_NAME config
+ CryptoToken token = cryptoManager.getInternalKeyStorageToken();
+ // TODO algorithm parameter
+ KeyPairGenerator gen = token.getKeyPairGenerator(KeyPairAlgorithm.RSA);
+ gen.initialize(2048);
+ KeyPair keypair = gen.genKeyPair();
+ PublicKey pub = keypair.getPublic();
+ X509Key x509key = CryptoUtil.convertPublicKeyToX509Key(pub);
+
+ // Sign certificate
+ String algName = mSigningUnit.getDefaultAlgorithm();
+ IConfigStore cs = new PropConfigStore("cs");
+ cs.put(".profile", "caCert.profile");
+ cs.put(".dn", subjectDN);
+ cs.put(".keyalgorithm", algName);
+ X509CertImpl cert =
+ CertUtil.createLocalCertWithCA(cs, x509key, "", "", "local", this);
+
+ // Add certificate to nssdb
+ cryptoManager.importCertPackage(cert.getEncoded(), nickname);
+ } catch (Exception e) {
+ // something went wrong; delete just-added entry
+ conn.delete(dn);
+ throw new ECAException("Error creating sub-CA certificate: " + e);
+ }
+ } catch (LDAPException e) {
+ throw new EBaseException("Error adding sub-CA entry to database: " + e);
+ } finally {
+ dbFactory.returnConn(conn);
+ dbFactory.reset();
+ }
+
+ return new CertificateAuthority(
+ topCA, aid, this.authorityID, nickname, description);
+ }
}
diff --git a/base/ca/src/com/netscape/ca/SigningUnit.java b/base/ca/src/com/netscape/ca/SigningUnit.java
index 2466fb652a46a3b5faede616cb397d18e592f5a0..0410bd2909bc71ca7d7443b3c9db0b085d62eaea 100644
--- a/base/ca/src/com/netscape/ca/SigningUnit.java
+++ b/base/ca/src/com/netscape/ca/SigningUnit.java
@@ -123,16 +123,14 @@ public final class SigningUnit implements ISigningUnit {
return mConfig.getString(PROP_TOKEN_NAME);
}
- public String getNickName() throws EBaseException {
- try {
- return mConfig.getString(PROP_RENAMED_CERT_NICKNAME);
- } catch (EBaseException e) {
- return mConfig.getString(PROP_CERT_NICKNAME);
- }
- }
public void init(ISubsystem owner, IConfigStore config)
throws EBaseException {
+ init(owner, config, null);
+ }
+
+ public void init(ISubsystem owner, IConfigStore config, String nickname)
+ throws EBaseException {
mOwner = owner;
mConfig = config;
@@ -140,7 +138,15 @@ public final class SigningUnit implements ISigningUnit {
try {
mManager = CryptoManager.getInstance();
- mNickname = getNickName();
+ if (nickname == null) {
+ try {
+ mNickname = mConfig.getString(PROP_RENAMED_CERT_NICKNAME);
+ } catch (EBaseException e) {
+ mNickname = mConfig.getString(PROP_CERT_NICKNAME);
+ }
+ } else {
+ mNickname = nickname;
+ }
tokenname = config.getString(PROP_TOKEN_NAME);
if (tokenname.equalsIgnoreCase(Constants.PR_INTERNAL_TOKEN) ||
diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ca21892d441e4b0e611d117e4dd490df05cf6e6
--- /dev/null
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
@@ -0,0 +1,183 @@
+//--- BEGIN COPYRIGHT BLOCK ---
+//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.
+//
+//(C) 2015 Red Hat, Inc.
+//All rights reserved.
+//--- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.ca.rest;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.commons.lang.StringUtils;
+import org.jboss.resteasy.plugins.providers.atom.Link;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.base.BadRequestException;
+import com.netscape.certsrv.base.ConflictingOperationException;
+import com.netscape.certsrv.base.ResourceNotFoundException;
+import com.netscape.certsrv.base.UnauthorizedException;
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.CANotFoundException;
+import com.netscape.certsrv.ca.ICertificateAuthority;
+import com.netscape.certsrv.ca.IssuerUnavailableException;
+import com.netscape.certsrv.common.NameValuePairs;
+import com.netscape.certsrv.common.OpDef;
+import com.netscape.certsrv.common.ScopeDef;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.certsrv.authority.AuthorityResource;
+import com.netscape.cms.realm.PKIPrincipal;
+import com.netscape.cms.servlet.base.PKIService;
+
+/**
+ * @author ftweedal
+ */
+public class AuthorityService extends PKIService implements AuthorityResource {
+
+ ICertificateAuthority topCA;
+
+ public AuthorityService() {
+ topCA = (ICertificateAuthority) CMS.getSubsystem("ca");
+ }
+
+ @Context
+ private UriInfo uriInfo;
+
+ @Context
+ private HttpHeaders headers;
+
+ @Context
+ private Request request;
+
+ @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";
+ */
+
+ @Override
+ public Response listCAs() {
+ List<AuthorityData> results = new ArrayList();
+ for (ICertificateAuthority ca : topCA.getCAs())
+ results.add(readAuthorityData(ca));
+
+ GenericEntity entity = new GenericEntity<List<AuthorityData>>(results) {};
+ return Response.ok(entity).build();
+ }
+
+ @Override
+ public Response getCA(String aidString) {
+ AuthorityID aid;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad CA ID: " + aidString);
+ }
+
+ ICertificateAuthority ca = topCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+
+ return createOKResponse(readAuthorityData(ca));
+ }
+
+ @Override
+ public Response createCA(AuthorityData data) {
+ AuthorityID parentAID = null;
+ String parentAIDString = data.getParentID();
+ if (parentAIDString != null) {
+ try {
+ parentAID = new AuthorityID(parentAIDString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad CA ID: " + parentAIDString);
+ }
+ }
+
+ try {
+ ICertificateAuthority subCA = topCA.createCA(
+ data.getDN(), parentAID, data.getDescription());
+ return createOKResponse(readAuthorityData(subCA));
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException(e.toString());
+ } catch (CANotFoundException e) {
+ throw new ResourceNotFoundException(e.toString());
+ } catch (IssuerUnavailableException e) {
+ throw new ConflictingOperationException(e.toString());
+ } catch (Exception e) {
+ CMS.debug(e);
+ throw new PKIException("Error creating sub-CA: " + e.toString());
+ }
+ }
+
+ private static AuthorityData readAuthorityData(ICertificateAuthority ca)
+ throws PKIException {
+ String dn;
+ try {
+ dn = ca.getX500Name().toLdapDNString();
+ } catch (IOException e) {
+ throw new PKIException("Error reading CA data: could not determine Issuer DN");
+ }
+
+ AuthorityID parentAID = ca.getAuthorityParentID();
+ return new AuthorityData(
+ dn,
+ ca.getAuthorityID().toString(),
+ parentAID != null ? parentAID.toString() : null,
+ ca.getAuthorityDescription()
+ );
+ }
+
+ /* TODO work out what audit messages are needed
+ public void auditProfileChangeState(String profileId, String op, String status) {
+ String msg = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_CERT_PROFILE_APPROVAL,
+ auditor.getSubjectID(),
+ status,
+ profileId,
+ op);
+ auditor.log(msg);
+ }
+
+ public void auditProfileChange(String scope, String type, String id, String status, Map<String, String> 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/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java b/base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java
index 16eae7877059c7dc42479276b3111db1ce7f582d..235ea105bef0c738bccd53276a744b95a76f0627 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java
@@ -34,6 +34,9 @@ public class CAApplication extends Application {
// installer
classes.add(CAInstallerService.class);
+ // sub-ca management
+ classes.add(AuthorityService.class);
+
// certs and requests
classes.add(CertService.class);
classes.add(CertRequestService.class);
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityData.java b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java
new file mode 100644
index 0000000000000000000000000000000000000000..64f2af83720311e3eac0ee7a9197a05ff0e7198a
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java
@@ -0,0 +1,109 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// 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.
+//
+// (C) 2015 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+/**
+ * @author ftweedal
+ */
+package com.netscape.certsrv.authority;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.jboss.resteasy.plugins.providers.atom.Link;
+
+ at XmlRootElement(name = "ca")
+ at XmlAccessorType(XmlAccessType.FIELD)
+public class AuthorityData {
+
+ public static Marshaller marshaller;
+ public static Unmarshaller unmarshaller;
+
+ static {
+ try {
+ marshaller = JAXBContext.newInstance(AuthorityData.class).createMarshaller();
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ unmarshaller = JAXBContext.newInstance(AuthorityData.class).createUnmarshaller();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ @XmlAttribute
+ protected String aid;
+
+ public String getID() {
+ return aid;
+ }
+
+
+ @XmlAttribute
+ protected String parentAID;
+
+ public String getParentID() {
+ return parentAID;
+ }
+
+
+ @XmlAttribute
+ protected String dn;
+
+ public String getDN() {
+ return dn;
+ }
+
+
+ @XmlAttribute
+ protected String description;
+
+ public String getDescription() {
+ return description;
+ }
+
+
+ protected Link link;
+
+ public Link getLink() {
+ return link;
+ }
+
+ public void setLink(Link link) {
+ this.link = link;
+ }
+
+ protected AuthorityData() {
+ }
+
+ public AuthorityData(
+ String dn, String aid, String parentAID,
+ String description) {
+ this.dn = dn;
+ this.aid = aid;
+ this.parentAID = parentAID;
+ this.description = description;
+ }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..817885687b04d1b10ea35dca9a1942fe5ce201f4
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
@@ -0,0 +1,36 @@
+package com.netscape.certsrv.authority;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.ClientResponseType;
+
+import com.netscape.certsrv.acls.ACLMapping;
+import com.netscape.certsrv.authentication.AuthMethodMapping;
+
+ at Path("authorities")
+public interface AuthorityResource {
+
+ @GET
+ public Response listCAs();
+ /*
+ @QueryParam("start") Integer start,
+ @QueryParam("size") Integer size);
+ */
+
+ @GET
+ @Path("{id}")
+ @ClientResponseType(entityType=AuthorityData.class)
+ public Response getCA(@PathParam("id") String caIDString);
+
+ @POST
+ @ClientResponseType(entityType=AuthorityData.class)
+ //@ACLMapping("certs")
+ //@AuthMethodMapping("certs")
+ public Response createCA(AuthorityData data);
+
+}
diff --git a/base/common/src/com/netscape/certsrv/ca/AuthorityID.java b/base/common/src/com/netscape/certsrv/ca/AuthorityID.java
new file mode 100644
index 0000000000000000000000000000000000000000..86f3870ab8cec700ab27cd6a8555e8e8d6e84c01
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/ca/AuthorityID.java
@@ -0,0 +1,36 @@
+package com.netscape.certsrv.ca;
+
+import java.util.UUID;
+
+/**
+ * Identifier for a CertificateAuthority.
+ */
+public class AuthorityID implements Comparable {
+
+ protected UUID uuid;
+
+ /**
+ * Parse a AuthorityID from the given string
+ */
+ public AuthorityID(String s) {
+ uuid = UUID.fromString(s);
+ }
+
+ /**
+ * Construct a random AuthorityID
+ */
+ public AuthorityID() {
+ uuid = UUID.randomUUID();
+ }
+
+ public String toString() {
+ return uuid.toString();
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof AuthorityID)
+ return uuid.compareTo(((AuthorityID) o).uuid);
+ throw new ClassCastException("Invalid comparsion operand for AuthorityID");
+ }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/ca/CANotFoundException.java b/base/common/src/com/netscape/certsrv/ca/CANotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7574d4243d9941e30aba506b4ad5fd7b8394386
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/ca/CANotFoundException.java
@@ -0,0 +1,12 @@
+package com.netscape.certsrv.ca;
+
+/**
+ * Exception to throw when a (sub-)CA cannot be found.
+ */
+public class CANotFoundException extends ECAException {
+
+ public CANotFoundException(String msgFormat) {
+ super(msgFormat);
+ }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/ca/ICAService.java b/base/common/src/com/netscape/certsrv/ca/ICAService.java
index 1d179e07692eee2f32780b33489975a571670685..a4b4a63038872fbf6d97cfc3fbcadce5234208a6 100644
--- a/base/common/src/com/netscape/certsrv/ca/ICAService.java
+++ b/base/common/src/com/netscape/certsrv/ca/ICAService.java
@@ -23,6 +23,7 @@ import netscape.security.x509.X509CertInfo;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.ca.AuthorityID;
import com.netscape.certsrv.connector.IConnector;
import com.netscape.certsrv.request.IRequest;
@@ -59,13 +60,15 @@ public interface ICAService {
* Issues certificate base on enrollment information,
* creates certificate record, and stores all necessary data.
*
+ * @param caID CA ID
* @param certi information obtain from revocation request
+ * @param profileId Name of profile used
+ * @param rid Request ID
* @exception EBaseException failed to issue certificate or create certificate record
*/
- public X509CertImpl issueX509Cert(X509CertInfo certi)
- throws EBaseException;
-
- public X509CertImpl issueX509Cert(X509CertInfo certi, String profileId, String rid)
+ public X509CertImpl issueX509Cert(
+ AuthorityID aid, X509CertInfo certi,
+ String profileId, String rid)
throws EBaseException;
/**
diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
index f87f15420b3ea6e02e5ce47b5c350e86f67ccc9f..d707bf848ba0973c2a817c45abeba8219bff574f 100644
--- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
+++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
@@ -18,6 +18,7 @@
package com.netscape.certsrv.ca;
import java.util.Enumeration;
+import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
@@ -515,4 +516,47 @@ public interface ICertificateAuthority extends ISubsystem {
public CertificateIssuerName getIssuerObj();
public CertificateSubjectName getSubjectObj();
+
+ /**
+ * Enumerate all sub-CA handles.
+ */
+ public List<ICertificateAuthority> getCAs();
+
+ /**
+ * Get the CA ID of this CA. Returns null for the top-level CA.
+ */
+ public AuthorityID getAuthorityID();
+
+ /**
+ * Get the CA ID of this CA's parent CA. Returns null for
+ * authorities signed by the top-level CA.
+ */
+ public AuthorityID getAuthorityParentID();
+
+ /**
+ * Return CA description. May be null.
+ */
+ public String getAuthorityDescription();
+
+ /**
+ * Get the CA by ID. Returns null if CA not found.
+ */
+ public ICertificateAuthority getCA(AuthorityID aid);
+
+ /**
+ * Create a new sub-CA under the specified parent CA.
+ */
+ public ICertificateAuthority createCA(
+ String dn, AuthorityID parentAID, String desc)
+ throws EBaseException;
+
+ /**
+ * Create a new sub-CA IMMEDIATELY beneath this one.
+ *
+ * This method DOES NOT add the new CA to caMap; it is the
+ * caller's responsibility.
+ */
+ public ICertificateAuthority createSubCA(
+ String dn, String desc)
+ throws EBaseException;
}
diff --git a/base/common/src/com/netscape/certsrv/ca/IssuerUnavailableException.java b/base/common/src/com/netscape/certsrv/ca/IssuerUnavailableException.java
new file mode 100644
index 0000000000000000000000000000000000000000..541df308d413676da51e5a1bce27232464118920
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/ca/IssuerUnavailableException.java
@@ -0,0 +1,13 @@
+package com.netscape.certsrv.ca;
+
+/**
+ * Exception to throw during CA creation when requested CA
+ * (issuer DN) already exists.
+ */
+public class IssuerUnavailableException extends ECAException {
+
+ public IssuerUnavailableException(String msgFormat) {
+ super(msgFormat);
+ }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java b/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
index 69a39d7e23232a1a0cc6e2fe98305d452234bb8c..a861a2e73a2e361971f010f63bd0ca615ba08e80 100644
--- a/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
+++ b/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
@@ -175,6 +175,11 @@ public interface IEnrollProfile extends IProfile {
public static final String REQUEST_ALGORITHM_PARAMS = "req_algorithm_params";
/**
+ * ID of requested certificate authority (unused for top-level CA)
+ */
+ public static final String REQUEST_AUTHORITY_ID = "req_authority_id";
+
+ /**
* Set Default X509CertInfo in the request.
*
* @param request profile-based certificate request.
diff --git a/base/common/src/com/netscape/certsrv/security/ISigningUnit.java b/base/common/src/com/netscape/certsrv/security/ISigningUnit.java
index 34d2a5109170c560b5a449d08f43eeeda5035b88..75b45bb8b31cde22881e0ddc310c0bdb51a66338 100644
--- a/base/common/src/com/netscape/certsrv/security/ISigningUnit.java
+++ b/base/common/src/com/netscape/certsrv/security/ISigningUnit.java
@@ -17,6 +17,7 @@
// --- END COPYRIGHT BLOCK ---
package com.netscape.certsrv.security;
+import java.security.PrivateKey;
import java.security.PublicKey;
import netscape.security.x509.X509CertImpl;
@@ -161,4 +162,11 @@ public interface ISigningUnit {
* @return public key
*/
public PublicKey getPublicKey();
+
+ /**
+ * Retrieves the public key associated in this unit.
+ *
+ * @return public key
+ */
+ public PrivateKey getPrivateKey();
}
diff --git a/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java
index d0bfdb8a64ee857a3f5ff544e41de905b4660f55..53edca3a93c28a4fdd6c476bbdd2dc3d83869505 100644
--- a/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java
+++ b/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java
@@ -29,6 +29,7 @@ import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.authority.IAuthority;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.ca.AuthorityID;
import com.netscape.certsrv.ca.ICAService;
import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.connector.IConnector;
@@ -95,8 +96,8 @@ public class CAEnrollProfile extends EnrollProfile {
CMS.debug("CAEnrollProfile: execute reqId=" +
request.getRequestId().toString());
ICertificateAuthority ca = (ICertificateAuthority) getAuthority();
+
ICAService caService = (ICAService) ca.getCAService();
-
if (caService == null) {
throw new EProfileException("No CA Service");
}
@@ -190,9 +191,13 @@ public class CAEnrollProfile extends EnrollProfile {
if (setId != null) {
sc.put("profileSetId", setId);
}
+ AuthorityID aid = null;
+ String aidString = request.getExtDataInString(REQUEST_AUTHORITY_ID);
+ if (aidString != null)
+ aid = new AuthorityID(aidString);
try {
- theCert = caService.issueX509Cert(info, getId() /* profileId */,
- id /* requestId */);
+ theCert = caService.issueX509Cert(
+ aid, info, getId() /* profileId */, id /* requestId */);
} catch (EBaseException e) {
CMS.debug(e.toString());
diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
index fe3b424a4b8e13215d4029d328d4a1e280be63ff..523e0117a55567d2f807dd3eb2e69c48d7eb7344 100644
--- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
+++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
@@ -190,6 +190,9 @@ public abstract class EnrollProfile extends BasicProfile
if (locale != null) {
result[i].setExtData(REQUEST_LOCALE, locale.getLanguage());
}
+
+ // set requested CA
+ result[i].setExtData(REQUEST_AUTHORITY_ID, ctx.get(REQUEST_AUTHORITY_ID));
}
return result;
}
diff --git a/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java
index 095f8bb5ffa2f950b58c868a6daee99991a80daa..54cfafed22a0654dd993c9c67f247eefac09c1a3 100644
--- a/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java
+++ b/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java
@@ -26,8 +26,12 @@ import netscape.security.x509.PKIXExtensions;
import netscape.security.x509.X509CertInfo;
import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.profile.EProfileException;
+import com.netscape.certsrv.profile.IEnrollProfile;
import com.netscape.certsrv.profile.IProfile;
import com.netscape.certsrv.property.Descriptor;
import com.netscape.certsrv.property.EPropertyException;
@@ -161,18 +165,27 @@ public class AuthorityKeyIdentifierExtDefault extends CAEnrollDefault {
*/
public void populate(IRequest request, X509CertInfo info)
throws EProfileException {
- AuthorityKeyIdentifierExtension ext = createExtension(info);
+ ICertificateAuthority ca = (ICertificateAuthority)
+ CMS.getSubsystem(CMS.SUBSYSTEM_CA);
+ String aidString = request.getExtDataInString(
+ IEnrollProfile.REQUEST_AUTHORITY_ID);
+ if (aidString != null)
+ ca = ca.getCA(new AuthorityID(aidString));
+ if (ca == null)
+ throw new EProfileException("Could not reach requested CA");
+ AuthorityKeyIdentifierExtension ext = createExtension(ca, info);
addExtension(PKIXExtensions.AuthorityKey_Id.toString(), ext, info);
}
- public AuthorityKeyIdentifierExtension createExtension(X509CertInfo info) {
+ public AuthorityKeyIdentifierExtension createExtension(
+ ICertificateAuthority ca, X509CertInfo info) {
KeyIdentifier kid = null;
String localKey = getConfig("localKey");
if (localKey != null && localKey.equals("true")) {
kid = getKeyIdentifier(info);
} else {
- kid = getCAKeyIdentifier();
+ kid = getCAKeyIdentifier(ca);
}
if (kid == null)
diff --git a/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java
index 1d1d05ed55ef30114781521ac607eae118546250..696830ead842767892f77bd8f8c9ea6f667225aa 100644
--- a/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java
+++ b/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java
@@ -68,9 +68,7 @@ public abstract class CAEnrollDefault extends EnrollDefault {
return null;
}
- public KeyIdentifier getCAKeyIdentifier() {
- ICertificateAuthority ca = (ICertificateAuthority)
- CMS.getSubsystem(CMS.SUBSYSTEM_CA);
+ public KeyIdentifier getCAKeyIdentifier(ICertificateAuthority ca) {
X509CertImpl caCert = ca.getCACert();
if (caCert == null) {
// during configuration, we dont have the CA certificate
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
index 960f997cd4badd18bdd25393e9175fc935d52edb..7e358b87f35b8aef2d3ef9de3f8dfd4c7a2b7053 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
@@ -30,6 +30,10 @@ import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.EPropertyNotFound;
import com.netscape.certsrv.base.SessionContext;
import com.netscape.certsrv.cert.CertEnrollmentRequest;
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.CANotFoundException;
+import com.netscape.certsrv.ca.ICertificateAuthority;
+import com.netscape.certsrv.profile.IEnrollProfile;
import com.netscape.certsrv.profile.IProfile;
import com.netscape.certsrv.profile.IProfileAuthenticator;
import com.netscape.certsrv.profile.IProfileContext;
@@ -146,6 +150,24 @@ public class EnrollmentProcessor extends CertProcessor {
}
IProfileContext ctx = profile.createContext();
+
+ // Insert AuthorityID into request context
+ //
+ String aidString = request.getParameter("authority");
+ if (aidString != null) {
+ AuthorityID aid = null;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestDataException("EnrollmentProcessor: invalid AuthorityID: " + aidString);
+ }
+ ICertificateAuthority ca = (ICertificateAuthority)
+ CMS.getSubsystem(CMS.SUBSYSTEM_CA);
+ if (ca.getCA(aid) == null)
+ throw new CANotFoundException("CA not found: " + aidString);
+ ctx.set(IEnrollProfile.REQUEST_AUTHORITY_ID, aidString);
+ }
+
CMS.debug("EnrollmentProcessor: set Inputs into profile Context");
setInputsIntoContext(data, profile, ctx);
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java
index 36b0e4d0d44ec8987856ebaaa3f4919c4a3f7071..c0729d88100e64d06c099bc4f1d73a14bcb9918f 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java
@@ -434,8 +434,19 @@ public class CertUtil {
(signingKeyType.equals("dsa") && algorithm.contains("DSA")));
}
+ public static X509CertImpl createLocalCertWithCA(IConfigStore config, X509Key x509key,
+ String prefix, String certTag, String type, ICertificateAuthority ca) throws IOException {
+ return createLocalCert(config, x509key, prefix, certTag, type, ca, null);
+ }
+
public static X509CertImpl createLocalCert(IConfigStore config, X509Key x509key,
String prefix, String certTag, String type, Context context) throws IOException {
+ return createLocalCert(config, x509key, prefix, certTag, type, null, context);
+ }
+
+ public static X509CertImpl createLocalCert(IConfigStore config, X509Key x509key,
+ String prefix, String certTag, String type,
+ ICertificateAuthority ca, Context context) throws IOException {
CMS.debug("Creating local certificate... certTag=" + certTag);
String profile = null;
@@ -446,13 +457,14 @@ public class CertUtil {
}
X509CertImpl cert = null;
- ICertificateAuthority ca = null;
ICertificateRepository cr = null;
RequestId reqId = null;
String profileId = null;
IRequestQueue queue = null;
IRequest req = null;
+ boolean caProvided = ca != null;
+
try {
Boolean injectSAN = config.getBoolean(
"service.injectSAN", false);
@@ -468,7 +480,8 @@ public class CertUtil {
} else {
keyAlgorithm = config.getString(prefix + certTag + ".keyalgorithm");
}
- ca = (ICertificateAuthority) CMS.getSubsystem(
+ if (!caProvided)
+ ca = (ICertificateAuthority) CMS.getSubsystem(
ICertificateAuthority.ID);
cr = ca.getCertificateRepository();
BigInteger serialNo = cr.getNextSerialNumber();
@@ -496,9 +509,9 @@ public class CertUtil {
}
CMS.debug("Cert Template: " + info.toString());
- String instanceRoot = config.getString("instanceRoot");
+ String instanceRoot = CMS.getConfigStore().getString("instanceRoot");
- String configurationRoot = config.getString("configurationRoot");
+ String configurationRoot = CMS.getConfigStore().getString("configurationRoot");
CertInfoProfile processor = new CertInfoProfile(
instanceRoot + configurationRoot + profile);
@@ -541,11 +554,18 @@ public class CertUtil {
processor.populate(req, info);
- String caPriKeyID = config.getString(
- prefix + "signing" + ".privkey.id");
- byte[] keyIDb = CryptoUtil.string2byte(caPriKeyID);
- PrivateKey caPrik = CryptoUtil.findPrivateKeyFromID(
- keyIDb);
+ PrivateKey caPrik = null;
+ if (caProvided) {
+ java.security.PrivateKey pk = ca.getSigningUnit().getPrivateKey();
+ if (!(pk instanceof PrivateKey))
+ throw new IOException("CA Private key must be a JSS PrivateKey");
+ caPrik = (PrivateKey) pk;
+ } else {
+ String caPriKeyID = config.getString(
+ prefix + "signing" + ".privkey.id");
+ byte[] keyIDb = CryptoUtil.string2byte(caPriKeyID);
+ caPrik = CryptoUtil.findPrivateKeyFromID(keyIDb);
+ }
if (caPrik == null) {
CMS.debug("CertUtil::createSelfSignedCert() - "
diff --git a/base/server/share/conf/schema-subCA.ldif b/base/server/share/conf/schema-subCA.ldif
new file mode 100644
index 0000000000000000000000000000000000000000..d03ca70ab466a6229f0efbbe2df6c587d8d5ea5c
--- /dev/null
+++ b/base/server/share/conf/schema-subCA.ldif
@@ -0,0 +1,5 @@
+dn: cn=schema
+attributeTypes: ( authorityID-oid NAME 'authorityID' DESC 'Authority ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
+attributeTypes: ( authorityKeyNickname-oid NAME 'authorityKeyNickname' DESC 'Authority key nickname' SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 X-ORIGIN 'user-defined' )
+attributeTypes: ( authorityParentID-oid NAME 'authorityParentID' DESC 'Authority Parent ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
+objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname ) MAY ( authorityParentID $ description ) X-ORIGIN 'user defined' )
diff --git a/base/server/share/conf/schema.ldif b/base/server/share/conf/schema.ldif
index 475758c5d66bf681e589995505a561bf4e4c40ef..3a692cac9370e9bb115a35fd0fe56be1d49b9ce9 100644
--- a/base/server/share/conf/schema.ldif
+++ b/base/server/share/conf/schema.ldif
@@ -667,3 +667,13 @@ dn: cn=schema
changetype: modify
add: objectClasses
objectClasses: ( certProfile-oid NAME 'certProfile' DESC 'Certificate profile' SUP top STRUCTURAL MUST cn MAY ( classId $ certProfileConfig ) X-ORIGIN 'user defined' )
+
+dn: cn=schema
+changetype: modify
+add: attributeTypes
+attributeTypes: ( authorityID-oid NAME 'authorityID' DESC 'Authority ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
+attributeTypes: ( authorityKeyNickname-oid NAME 'authorityKeyNickname' DESC 'Authority key nickname' SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 X-ORIGIN 'user-defined' )
+attributeTypes: ( authorityParentID-oid NAME 'authorityParentID' DESC 'Authority Parent ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
+-
+add: objectClasses
+objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname ) MAY ( authorityParentID $ description ) X-ORIGIN 'user defined' )
--
2.4.3
-------------- next part --------------
From 5253ab958469de9e99e921ff7d04d7a5d20a4bd2 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Wed, 10 Jun 2015 03:02:35 -0400
Subject: [PATCH] Lightweight CAs: add ca-authority CLI
Add CLI commands for creating, listing and showing lightweight CAs.
Part of: https://fedorahosted.org/pki/ticket/1213
---
.../certsrv/authority/AuthorityClient.java | 57 ++++++++++++++
.../src/com/netscape/certsrv/ca/CAClient.java | 3 +-
.../netscape/cmstools/authority/AuthorityCLI.java | 56 ++++++++++++++
.../cmstools/authority/AuthorityCreateCLI.java | 86 ++++++++++++++++++++++
.../cmstools/authority/AuthorityFindCLI.java | 62 ++++++++++++++++
.../cmstools/authority/AuthorityShowCLI.java | 57 ++++++++++++++
.../src/com/netscape/cmstools/cli/CACLI.java | 2 +
7 files changed, 322 insertions(+), 1 deletion(-)
create mode 100644 base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
create mode 100644 base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
create mode 100644 base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java
create mode 100644 base/java-tools/src/com/netscape/cmstools/authority/AuthorityFindCLI.java
create mode 100644 base/java-tools/src/com/netscape/cmstools/authority/AuthorityShowCLI.java
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java b/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ad549c7d4bf85513aa64911fdf1c71c5ac4fc21
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
@@ -0,0 +1,57 @@
+//--- BEGIN COPYRIGHT BLOCK ---
+//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.
+//
+//(C) 2015 Red Hat, Inc.
+//All rights reserved.
+//--- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.authority;
+
+import java.net.URISyntaxException;
+import java.util.List;
+
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+
+import com.netscape.certsrv.client.Client;
+import com.netscape.certsrv.client.PKIClient;
+
+/**
+ * @author Fraser Tweedale <ftweedal at redhat.com>
+ */
+public class AuthorityClient extends Client {
+
+ public AuthorityResource proxy;
+
+ public AuthorityClient(PKIClient client, String subsystem) throws URISyntaxException {
+ super(client, subsystem, "authority");
+ proxy = createProxy(AuthorityResource.class);
+ }
+
+ public List<AuthorityData> listCAs() {
+ Response response = proxy.listCAs();
+ GenericType<List<AuthorityData>> type = new GenericType<List<AuthorityData>>() {};
+ return client.getEntity(response, type);
+ }
+
+ public AuthorityData getCA(String caIDString) {
+ Response response = proxy.getCA(caIDString);
+ return client.getEntity(response, AuthorityData.class);
+ }
+
+ public AuthorityData createCA(AuthorityData data) {
+ Response response = proxy.createCA(data);
+ return client.getEntity(response, AuthorityData.class);
+ }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/ca/CAClient.java b/base/common/src/com/netscape/certsrv/ca/CAClient.java
index e1a0a8c02f8a840acbdea924c164020b88557fc4..1fbd2a0b286ed09854373846510c392c5202307a 100644
--- a/base/common/src/com/netscape/certsrv/ca/CAClient.java
+++ b/base/common/src/com/netscape/certsrv/ca/CAClient.java
@@ -26,6 +26,7 @@ import com.netscape.certsrv.group.GroupClient;
import com.netscape.certsrv.profile.ProfileClient;
import com.netscape.certsrv.selftests.SelfTestClient;
import com.netscape.certsrv.user.UserClient;
+import com.netscape.certsrv.authority.AuthorityClient;
public class CAClient extends SubsystemClient {
@@ -35,7 +36,7 @@ public class CAClient extends SubsystemClient {
}
public void init() throws URISyntaxException {
-
+ addClient(new AuthorityClient(client, name));
addClient(new CertClient(client, name));
addClient(new GroupClient(client, name));
addClient(new ProfileClient(client, name));
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2b630d32f9b8fc12792d14c84e90e12f7c23f4d
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
@@ -0,0 +1,56 @@
+package com.netscape.cmstools.authority;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.net.URI;
+import java.util.Locale;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.certsrv.authority.AuthorityClient;
+import com.netscape.certsrv.cert.CertEnrollmentRequest;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class AuthorityCLI extends CLI {
+
+ public AuthorityClient authorityClient;
+
+ public AuthorityCLI(CLI parent) {
+ super("authority", "CA management commands", parent);
+
+ addModule(new AuthorityFindCLI(this));
+ addModule(new AuthorityShowCLI(this));
+ addModule(new AuthorityCreateCLI(this));
+ }
+
+ public String getFullName() {
+ if (parent instanceof MainCLI) {
+ // do not include MainCLI's name
+ return name;
+ } else {
+ return parent.getFullName() + "-" + name;
+ }
+ }
+
+ public void execute(String[] args) throws Exception {
+ client = parent.getClient();
+ authorityClient = new AuthorityClient(client, "ca");
+ super.execute(args);
+ }
+
+ protected static void printAuthorityData(AuthorityData data) {
+ System.out.println(" Issuer DN: " + data.getDN());
+ System.out.println(" ID: " + data.getID());
+ System.out.println(" Parent ID: " + data.getParentID());
+ String desc = data.getDescription();
+ if (desc != null)
+ System.out.println(" Description: " + desc);
+ }
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..19329cbba0fac3f8c9722cc6854cbeaf6a31c75c
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java
@@ -0,0 +1,86 @@
+package com.netscape.cmstools.authority;
+
+import java.util.Arrays;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.ParseException;
+
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class AuthorityCreateCLI extends CLI {
+
+ public AuthorityCLI authorityCLI;
+
+ public AuthorityCreateCLI(AuthorityCLI authorityCLI) {
+ super("create", "Create CAs", authorityCLI);
+ this.authorityCLI = authorityCLI;
+
+ Option optParent = new Option(null, "parent", true, "ID of parent CA");
+ optParent.setArgName("id");
+ options.addOption(optParent);
+
+ Option optDesc = new Option(null, "desc", true, "Optional description");
+ optDesc.setArgName("string");
+ options.addOption(optDesc);
+ }
+
+ public void printHelp() {
+ formatter.printHelp(getFullName() + " <dn>", options);
+ }
+
+ public void execute(String[] args) throws Exception {
+ // Always check for "--help" prior to parsing
+ if (Arrays.asList(args).contains("--help")) {
+ // Display usage
+ printHelp();
+ System.exit(0);
+ }
+
+ CommandLine cmd = null;
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.err.println("Error: " + e.getMessage());
+ printHelp();
+ System.exit(-1);
+ }
+
+ String[] cmdArgs = cmd.getArgs();
+ if (cmdArgs.length != 1) {
+ if (cmdArgs.length < 1)
+ System.err.println("No DN specified.");
+ else
+ System.err.println("Too many arguments.");
+ printHelp();
+ System.exit(-1);
+ }
+
+ String parentAIDString = null;
+ if (cmd.hasOption("parent")) {
+ parentAIDString = cmd.getOptionValue("parent");
+ try {
+ new AuthorityID(parentAIDString);
+ } catch (IllegalArgumentException e) {
+ System.err.println("Bad CA ID: " + parentAIDString);
+ printHelp();
+ System.exit(-1);
+ }
+ }
+
+ String desc = null;
+ if (cmd.hasOption("desc"))
+ desc = cmd.getOptionValue("desc");
+
+ String dn = cmdArgs[0];
+ AuthorityData data = new AuthorityData(
+ dn, null, parentAIDString, desc);
+ AuthorityData newData = authorityCLI.authorityClient.createCA(data);
+ AuthorityCLI.printAuthorityData(newData);
+ }
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityFindCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityFindCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a5684671d6a778146de183a0d122aaa58c45d8d
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityFindCLI.java
@@ -0,0 +1,62 @@
+package com.netscape.cmstools.authority;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.ParseException;
+
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class AuthorityFindCLI extends CLI {
+
+ public AuthorityCLI authorityCLI;
+
+ public AuthorityFindCLI(AuthorityCLI authorityCLI) {
+ super("find", "Find CAs", authorityCLI);
+ this.authorityCLI = authorityCLI;
+ }
+
+ public void printHelp() {
+ formatter.printHelp(getFullName(), options);
+ }
+
+ public void execute(String[] args) throws Exception {
+ // Always check for "--help" prior to parsing
+ if (Arrays.asList(args).contains("--help")) {
+ // Display usage
+ printHelp();
+ System.exit(0);
+ }
+
+ CommandLine cmd = null;
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.err.println("Error: " + e.getMessage());
+ printHelp();
+ System.exit(-1);
+ }
+
+ List<AuthorityData> datas = authorityCLI.authorityClient.listCAs();
+
+ MainCLI.printMessage(datas.size() + " entries matched");
+ if (datas.size() == 0) return;
+
+ boolean first = true;
+ for (AuthorityData data : datas) {
+ if (first)
+ first = false;
+ else
+ System.out.println();
+ AuthorityCLI.printAuthorityData(data);
+ }
+
+ MainCLI.printMessage("Number of entries returned " + datas.size());
+ }
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityShowCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityShowCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..a252f3001ae2581f770e58e68a077eb909a5490b
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityShowCLI.java
@@ -0,0 +1,57 @@
+package com.netscape.cmstools.authority;
+
+import java.util.Arrays;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.ParseException;
+
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class AuthorityShowCLI extends CLI {
+
+ public AuthorityCLI authorityCLI;
+
+ public AuthorityShowCLI(AuthorityCLI authorityCLI) {
+ super("show", "Show CAs", authorityCLI);
+ this.authorityCLI = authorityCLI;
+ }
+
+ public void printHelp() {
+ formatter.printHelp(getFullName() + " <ID>", options);
+ }
+
+ public void execute(String[] args) throws Exception {
+ // Always check for "--help" prior to parsing
+ if (Arrays.asList(args).contains("--help")) {
+ // Display usage
+ printHelp();
+ System.exit(0);
+ }
+
+ CommandLine cmd = null;
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.err.println("Error: " + e.getMessage());
+ printHelp();
+ System.exit(-1);
+ }
+
+ String[] cmdArgs = cmd.getArgs();
+
+ if (cmdArgs.length < 1) {
+ System.err.println("Error: No ID specified.");
+ printHelp();
+ System.exit(-1);
+ }
+
+ String caIDString = cmdArgs[0];
+ AuthorityData data = authorityCLI.authorityClient.getCA(caIDString);
+ AuthorityCLI.printAuthorityData(data);
+ }
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java b/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java
index 17fb4866f38f05f7ead02b6145ef7d09140a90c5..5c41f00c2eb6e393cc95d3b174cb14eefc7307ae 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java
@@ -20,6 +20,7 @@ package com.netscape.cmstools.cli;
import com.netscape.certsrv.ca.CAClient;
import com.netscape.certsrv.client.Client;
+import com.netscape.cmstools.authority.AuthorityCLI;
import com.netscape.cmstools.cert.CertCLI;
import com.netscape.cmstools.group.GroupCLI;
import com.netscape.cmstools.profile.ProfileCLI;
@@ -37,6 +38,7 @@ public class CACLI extends SubsystemCLI {
public CACLI(CLI parent) {
super("ca", "CA management commands", parent);
+ addModule(new AuthorityCLI(this));
addModule(new CertCLI(this));
addModule(new GroupCLI(this));
addModule(new KRAConnectorCLI(this));
--
2.4.3
-------------- next part --------------
From 74f9abbe2cff23aa3a1cd1a3ac8689f8f6efb792 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 1 Sep 2015 09:57:42 -0400
Subject: [PATCH] Lightweight CAs: REST cert request param to specify authority
Add the optional "ca" query parameter for REST cert request
submission. Also update the ca-cert-request-submit CLI command with
an option to provide an AuthorityID.
Part of: https://fedorahosted.org/pki/ticket/1213
---
.../src/com/netscape/cms/servlet/test/CATest.java | 4 ++--
.../dogtagpki/server/ca/rest/CertRequestService.java | 11 ++++++++++-
.../src/com/netscape/certsrv/cert/CertClient.java | 7 +++++--
.../netscape/certsrv/cert/CertRequestResource.java | 4 +++-
.../netscape/cmstools/cert/CertRequestSubmitCLI.java | 20 +++++++++++++++++++-
.../cmstools/client/ClientCertRequestCLI.java | 2 +-
6 files changed, 40 insertions(+), 8 deletions(-)
diff --git a/base/ca/functional/src/com/netscape/cms/servlet/test/CATest.java b/base/ca/functional/src/com/netscape/cms/servlet/test/CATest.java
index 15023cad939abb11927abc64fe5916e04cb65661..5876c57f985caa38ad5895f4368113620370910d 100644
--- a/base/ca/functional/src/com/netscape/cms/servlet/test/CATest.java
+++ b/base/ca/functional/src/com/netscape/cms/servlet/test/CATest.java
@@ -288,7 +288,7 @@ public class CATest {
private static void enrollAndApproveCertRequest(CertClient client, CertEnrollmentRequest data) {
CertRequestInfos reqInfo = null;
try {
- reqInfo = client.enrollRequest(data);
+ reqInfo = client.enrollRequest(data, null);
} catch (Exception e) {
e.printStackTrace();
log(e.toString());
@@ -308,7 +308,7 @@ public class CATest {
private static void enrollCertRequest(CertClient client, CertEnrollmentRequest data) {
CertRequestInfos reqInfo = null;
try {
- reqInfo = client.enrollRequest(data);
+ reqInfo = client.enrollRequest(data, null);
} catch (Exception e) {
e.printStackTrace();
log(e.toString());
diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
index 95f1f4c20086ddb45846f65b1db157bff238708a..654d814d8a963892a6b39a1f77745e1071a5408d 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
@@ -40,7 +40,9 @@ import com.netscape.certsrv.base.BadRequestDataException;
import com.netscape.certsrv.base.BadRequestException;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.base.ResourceNotFoundException;
import com.netscape.certsrv.base.UnauthorizedException;
+import com.netscape.certsrv.ca.CANotFoundException;
import com.netscape.certsrv.cert.CertEnrollmentRequest;
import com.netscape.certsrv.cert.CertRequestInfo;
import com.netscape.certsrv.cert.CertRequestInfos;
@@ -113,7 +115,11 @@ public class CertRequestService extends PKIService implements CertRequestResourc
}
@Override
- public Response enrollCert(CertEnrollmentRequest data) {
+ public Response enrollCert(CertEnrollmentRequest data, String aidString) {
+ // Ignore the aidString param; it is pulled out of the
+ // servletRequest that is passed to CertRequestDAO,
+ // but is included in the signature so that clients
+ // can easily provide it via @QueryParam
if (data == null) {
CMS.debug("enrollCert: data is null");
@@ -137,6 +143,9 @@ public class CertRequestService extends PKIService implements CertRequestResourc
} catch (BadRequestDataException e) {
CMS.debug("enrollCert: bad request data: " + e);
throw new BadRequestException(e.toString());
+ } catch (CANotFoundException e) {
+ CMS.debug("enrollCert: unknown CA: " + e);
+ throw new ResourceNotFoundException(e.toString());
} catch (EBaseException e) {
throw new PKIException(e);
} catch (Exception e) {
diff --git a/base/common/src/com/netscape/certsrv/cert/CertClient.java b/base/common/src/com/netscape/certsrv/cert/CertClient.java
index 42b04b7021f0063894c340c177915d799b621ddd..211711b3da64e5125beee000759c2f2926d85e86 100644
--- a/base/common/src/com/netscape/certsrv/cert/CertClient.java
+++ b/base/common/src/com/netscape/certsrv/cert/CertClient.java
@@ -21,6 +21,7 @@ import java.net.URISyntaxException;
import javax.ws.rs.core.Response;
+import com.netscape.certsrv.ca.AuthorityID;
import com.netscape.certsrv.client.Client;
import com.netscape.certsrv.client.PKIClient;
import com.netscape.certsrv.client.SubsystemClient;
@@ -85,8 +86,10 @@ public class CertClient extends Client {
return client.getEntity(response, CertRequestInfo.class);
}
- public CertRequestInfos enrollRequest(CertEnrollmentRequest data) {
- Response response = certRequestClient.enrollCert(data);
+ public CertRequestInfos enrollRequest(
+ CertEnrollmentRequest data, AuthorityID aid) {
+ String aidString = aid != null ? aid.toString() : null;
+ Response response = certRequestClient.enrollCert(data, aidString);
return client.getEntity(response, CertRequestInfos.class);
}
diff --git a/base/common/src/com/netscape/certsrv/cert/CertRequestResource.java b/base/common/src/com/netscape/certsrv/cert/CertRequestResource.java
index 7f08b4af392e3e56419abdad7cb66bd191688222..b877b681ccf905b4da2949fe04ec21e8a6407bba 100644
--- a/base/common/src/com/netscape/certsrv/cert/CertRequestResource.java
+++ b/base/common/src/com/netscape/certsrv/cert/CertRequestResource.java
@@ -37,7 +37,9 @@ public interface CertRequestResource {
@POST
@Path("certrequests")
@ClientResponseType(entityType=CertRequestInfos.class)
- public Response enrollCert(CertEnrollmentRequest data);
+ public Response enrollCert(
+ CertEnrollmentRequest data,
+ @QueryParam("authority") String caIDString);
/**
* Used to retrieve cert request info for a specific request
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 608490bb73d7df482d87e67e9c15322ddc2e5f5a..e46079406bfbd1dbd47e32567b52dde85f181233 100644
--- a/base/java-tools/src/com/netscape/cmstools/cert/CertRequestSubmitCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cert/CertRequestSubmitCLI.java
@@ -8,8 +8,10 @@ import java.util.Scanner;
import javax.xml.bind.JAXBException;
import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
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.cmstools.cli.CLI;
@@ -22,6 +24,10 @@ public class CertRequestSubmitCLI extends CLI {
public CertRequestSubmitCLI(CertCLI certCLI) {
super("request-submit", "Submit certificate request", certCLI);
this.certCLI = certCLI;
+
+ Option optCA = new Option(null, "authority", true, "Authority ID (omit for top-level CA)");
+ optCA.setArgName("id");
+ options.addOption(optCA);
}
public void printHelp() {
@@ -55,9 +61,21 @@ public class CertRequestSubmitCLI extends CLI {
System.exit(-1);
}
+ AuthorityID aid = null;
+ if (cmd.hasOption("authority")) {
+ String aidString = cmd.getOptionValue("authority");
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ System.err.println("Bad AuthorityID: " + aidString);
+ printHelp();
+ System.exit(-1);
+ }
+ }
+
try {
CertEnrollmentRequest erd = getEnrollmentRequest(cmdArgs[0]);
- CertRequestInfos cri = certCLI.certClient.enrollRequest(erd);
+ CertRequestInfos cri = certCLI.certClient.enrollRequest(erd, aid);
MainCLI.printMessage("Submitted certificate request");
CertCLI.printCertRequestInfos(cri);
diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java
index e6bd0d98120295ef8e798925f4e9aceb3a0d43f6..13b8c632f9b6d3fce96fb07547852bdef552873d 100644
--- a/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertRequestCLI.java
@@ -283,7 +283,7 @@ public class ClientCertRequestCLI extends CLI {
System.out.println("Sending certificate request.");
}
- CertRequestInfos infos = certClient.enrollRequest(request);
+ CertRequestInfos infos = certClient.enrollRequest(request, null);
MainCLI.printMessage("Submitted certificate request");
CertCLI.printCertRequestInfos(infos);
--
2.4.3
-------------- next part --------------
From 1a0e1d4cb45c611b0a9cdab38ac55e1d9a1c01af Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 15 Sep 2015 23:36:58 -0400
Subject: [PATCH 48/50] Lightweight CAs: add 'authorityEnabled' attribute
Add the 'authortyEnabled' attribute for lightweight CAs and throw
new exception CADisabledException when request submission or signing
operations are attempted on a disabled authority.
Part of: https://fedorahosted.org/pki/ticket/1604
---
.../src/com/netscape/ca/CertificateAuthority.java | 30 +++++++++++++++++++---
.../server/ca/rest/CertRequestService.java | 8 ++++++
.../netscape/certsrv/ca/CADisabledException.java | 13 ++++++++++
.../cms/servlet/cert/EnrollmentProcessor.java | 6 ++++-
.../cms/servlet/cert/RequestProcessor.java | 30 +++++++++++++++++++++-
base/server/share/conf/schema-subCA.ldif | 3 ++-
base/server/share/conf/schema.ldif | 3 ++-
7 files changed, 85 insertions(+), 8 deletions(-)
create mode 100644 base/common/src/com/netscape/certsrv/ca/CADisabledException.java
diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java
index 5df7c77612132d2dc52ca43060d7ba782d9388cc..05ab2a6024ab311b80cfd3ef026d77691169aac8 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
@@ -89,6 +89,7 @@ import com.netscape.certsrv.base.ISubsystem;
import com.netscape.certsrv.base.Nonces;
import com.netscape.certsrv.base.PKIException;
import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.CADisabledException;
import com.netscape.certsrv.ca.CANotFoundException;
import com.netscape.certsrv.ca.ECAException;
import com.netscape.certsrv.ca.ICRLIssuingPoint;
@@ -167,6 +168,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
protected AuthorityID authorityID = null;
protected AuthorityID authorityParentID = null;
protected String authorityDescription = null;
+ protected boolean authorityEnabled = true;
protected ISubsystem mOwner = null;
protected IConfigStore mConfig = null;
@@ -277,13 +279,15 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
AuthorityID aid,
AuthorityID parentAID,
String signingKeyNickname,
- String authorityDescription
+ String authorityDescription,
+ boolean authorityEnabled
) throws EBaseException {
setId(topCA.getId());
this.topCA = topCA;
this.authorityID = aid;
this.authorityParentID = parentAID;
this.authorityDescription = authorityDescription;
+ this.authorityEnabled = authorityEnabled;
mNickname = signingKeyNickname;
init(topCA.mOwner, topCA.mConfig);
}
@@ -292,6 +296,11 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
return authorityID == null;
}
+ private void ensureEnabled() throws CADisabledException {
+ if (!authorityEnabled)
+ throw new CADisabledException("Authority is disabled");
+ }
+
/**
* Retrieves subsystem identifier.
*/
@@ -1056,6 +1065,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
public X509CRLImpl sign(X509CRLImpl crl, String algname)
throws EBaseException {
+ ensureEnabled();
X509CRLImpl signedcrl = null;
IStatsSubsystem statsSub = (IStatsSubsystem) CMS.getSubsystem("stats");
@@ -1128,6 +1138,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
public X509CertImpl sign(X509CertInfo certInfo, String algname)
throws EBaseException {
+ ensureEnabled();
X509CertImpl signedcert = null;
@@ -1212,6 +1223,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
public byte[] sign(byte[] data, String algname)
throws EBaseException {
+ ensureEnabled();
return mSigningUnit.sign(data, algname);
}
@@ -1952,8 +1964,16 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
if (descAttr != null)
desc = (String) descAttr.getStringValues().nextElement();
+ boolean enabled = true;
+ LDAPAttribute enabledAttr = entry.getAttribute("authorityEnabled");
+ if (enabledAttr != null) {
+ String enabledString = (String)
+ enabledAttr.getStringValues().nextElement();
+ enabled = enabledString.equalsIgnoreCase("TRUE");
+ }
+
CertificateAuthority subCA = new CertificateAuthority(
- this, aid, parentAID, keyNick, desc);
+ this, aid, parentAID, keyNick, desc, enabled);
caMap.put(aid, subCA);
}
} catch (LDAPException e) {
@@ -2131,6 +2151,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
}
private BasicOCSPResponse sign(ResponseData rd) throws EBaseException {
+ ensureEnabled();
try (DerOutputStream out = new DerOutputStream()) {
DerOutputStream tmp = new DerOutputStream();
@@ -2352,7 +2373,8 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
new LDAPAttribute("objectclass", "authority"),
new LDAPAttribute("cn", aidString),
new LDAPAttribute("authorityID", aidString),
- new LDAPAttribute("authorityKeyNickname", nickname)
+ new LDAPAttribute("authorityKeyNickname", nickname),
+ new LDAPAttribute("authorityEnabled", "TRUE")
};
LDAPAttributeSet attrSet = new LDAPAttributeSet(attrs);
if (this.authorityID != null)
@@ -2407,6 +2429,6 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
}
return new CertificateAuthority(
- topCA, aid, this.authorityID, nickname, description);
+ topCA, aid, this.authorityID, nickname, description, true);
}
}
diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
index 654d814d8a963892a6b39a1f77745e1071a5408d..f3ded9a207230ef455b1ae49866c8b5069acfb26 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
@@ -38,10 +38,12 @@ import com.netscape.certsrv.authentication.EAuthException;
import com.netscape.certsrv.authorization.EAuthzException;
import com.netscape.certsrv.base.BadRequestDataException;
import com.netscape.certsrv.base.BadRequestException;
+import com.netscape.certsrv.base.ConflictingOperationException;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.PKIException;
import com.netscape.certsrv.base.ResourceNotFoundException;
import com.netscape.certsrv.base.UnauthorizedException;
+import com.netscape.certsrv.ca.CADisabledException;
import com.netscape.certsrv.ca.CANotFoundException;
import com.netscape.certsrv.cert.CertEnrollmentRequest;
import com.netscape.certsrv.cert.CertRequestInfo;
@@ -143,6 +145,9 @@ public class CertRequestService extends PKIService implements CertRequestResourc
} catch (BadRequestDataException e) {
CMS.debug("enrollCert: bad request data: " + e);
throw new BadRequestException(e.toString());
+ } catch (CADisabledException e) {
+ CMS.debug("enrollCert: CA disabled: " + e);
+ throw new ConflictingOperationException(e.toString());
} catch (CANotFoundException e) {
CMS.debug("enrollCert: unknown CA: " + e);
throw new ResourceNotFoundException(e.toString());
@@ -219,6 +224,9 @@ public class CertRequestService extends PKIService implements CertRequestResourc
} catch (BadRequestDataException e) {
CMS.debug("changeRequestState: bad request data: " + e);
throw new BadRequestException(e.toString());
+ } catch (CADisabledException e) {
+ CMS.debug("changeRequestState: CA disabled: " + e);
+ throw new ConflictingOperationException(e.toString());
} catch (EPropertyException e) {
CMS.debug("changeRequestState: execution error " + e);
throw new PKIException(CMS.getUserMessage(getLocale(headers),
diff --git a/base/common/src/com/netscape/certsrv/ca/CADisabledException.java b/base/common/src/com/netscape/certsrv/ca/CADisabledException.java
new file mode 100644
index 0000000000000000000000000000000000000000..df5fdf809f716202c38287e670a88e55eb93ce75
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/ca/CADisabledException.java
@@ -0,0 +1,13 @@
+package com.netscape.certsrv.ca;
+
+/**
+ * Exception to throw when a (sub-)CA cannot perform an operation
+ * because it is disabled.
+ */
+public class CADisabledException extends ECAException {
+
+ public CADisabledException(String msgFormat) {
+ super(msgFormat);
+ }
+
+}
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
index 7e358b87f35b8aef2d3ef9de3f8dfd4c7a2b7053..5e22fc986dab621191c3157df5d0ef9a842eea34 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
@@ -31,6 +31,7 @@ import com.netscape.certsrv.base.EPropertyNotFound;
import com.netscape.certsrv.base.SessionContext;
import com.netscape.certsrv.cert.CertEnrollmentRequest;
import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.CADisabledException;
import com.netscape.certsrv.ca.CANotFoundException;
import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.profile.IEnrollProfile;
@@ -163,8 +164,11 @@ public class EnrollmentProcessor extends CertProcessor {
}
ICertificateAuthority ca = (ICertificateAuthority)
CMS.getSubsystem(CMS.SUBSYSTEM_CA);
- if (ca.getCA(aid) == null)
+ ca = ca.getCA(aid);
+ if (ca == null)
throw new CANotFoundException("CA not found: " + aidString);
+ if (!ca.getAuthorityEnabled())
+ throw new CADisabledException("CA not enabled: " + aidString);
ctx.set(IEnrollProfile.REQUEST_AUTHORITY_ID, aidString);
}
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java
index 2826f477e358a5e16657e985d7f13079cdb14a33..df318aa93c95c91c7885360654daf2d4a7da77c6 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java
@@ -36,6 +36,10 @@ import com.netscape.certsrv.base.BadRequestException;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.EPropertyNotFound;
import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.CADisabledException;
+import com.netscape.certsrv.ca.CANotFoundException;
+import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.cert.CertReviewResponse;
import com.netscape.certsrv.logging.ILogger;
import com.netscape.certsrv.profile.EDeferException;
@@ -346,11 +350,35 @@ public class RequestProcessor extends CertProcessor {
* occurred
*/
private void approveRequest(IRequest req, CertReviewResponse data, IProfile profile, Locale locale)
- throws EProfileException {
+ throws EBaseException {
String auditMessage = null;
String auditSubjectID = auditSubjectID();
String auditRequesterID = auditRequesterID(req);
+ // ensure target CA is enabled
+ String aidString = req.getExtDataInString(IEnrollProfile.REQUEST_AUTHORITY_ID);
+ if (aidString != null) {
+ AuthorityID aid = null;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ // request already accepted; shouldn't happen
+ throw new BadRequestDataException("Invalid AuthorityID in request data");
+ }
+ ICertificateAuthority ca = (ICertificateAuthority)
+ CMS.getSubsystem("ca");
+ if (ca == null)
+ // shouldn't happen
+ throw new CANotFoundException("Could not get top-level CA"); // shouldn't happen
+ ca = ca.getCA(aid);
+ if (ca == null)
+ // request already accepted; shouldn't happen
+ throw new CANotFoundException("Unknown CA: " + aidString);
+ if (!ca.getAuthorityEnabled())
+ // authority disabled after request was submitted
+ throw new CADisabledException("CA '" + aidString + "' is disabled");
+ }
+
try {
profile.execute(req);
req.setRequestStatus(RequestStatus.COMPLETE);
diff --git a/base/server/share/conf/schema-subCA.ldif b/base/server/share/conf/schema-subCA.ldif
index d03ca70ab466a6229f0efbbe2df6c587d8d5ea5c..762fc99abe87419da4a6bc79888a800c49219afa 100644
--- a/base/server/share/conf/schema-subCA.ldif
+++ b/base/server/share/conf/schema-subCA.ldif
@@ -2,4 +2,5 @@ dn: cn=schema
attributeTypes: ( authorityID-oid NAME 'authorityID' DESC 'Authority ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
attributeTypes: ( authorityKeyNickname-oid NAME 'authorityKeyNickname' DESC 'Authority key nickname' SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 X-ORIGIN 'user-defined' )
attributeTypes: ( authorityParentID-oid NAME 'authorityParentID' DESC 'Authority Parent ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
-objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname ) MAY ( authorityParentID $ description ) X-ORIGIN 'user defined' )
+attributeTypes: ( authorityEnabled-oid NAME 'authorityEnabled' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 X-ORIGIN 'user defined' )
+objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname $ authorityEnabled ) MAY ( authorityParentID $ description ) X-ORIGIN 'user defined' )
diff --git a/base/server/share/conf/schema.ldif b/base/server/share/conf/schema.ldif
index 3a692cac9370e9bb115a35fd0fe56be1d49b9ce9..72d902713500192b75038ef2c980b4e4ee9bb3b8 100644
--- a/base/server/share/conf/schema.ldif
+++ b/base/server/share/conf/schema.ldif
@@ -674,6 +674,7 @@ add: attributeTypes
attributeTypes: ( authorityID-oid NAME 'authorityID' DESC 'Authority ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
attributeTypes: ( authorityKeyNickname-oid NAME 'authorityKeyNickname' DESC 'Authority key nickname' SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 X-ORIGIN 'user-defined' )
attributeTypes: ( authorityParentID-oid NAME 'authorityParentID' DESC 'Authority Parent ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' )
+attributeTypes: ( authorityEnabled-oid NAME 'authorityEnabled' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 X-ORIGIN 'user defined' )
-
add: objectClasses
-objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname ) MAY ( authorityParentID $ description ) X-ORIGIN 'user defined' )
+objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname $ authorityEnabled ) MAY ( authorityParentID $ description ) X-ORIGIN 'user defined' )
--
2.4.3
-------------- next part --------------
From 13e7f4077da724c22cde97a8e0f1e96ba23fa636 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Thu, 17 Sep 2015 02:26:35 -0400
Subject: [PATCH 49/50] Lightweight CAs: add ability to modify attributes
Add the ability to modify lightweight CA attributes, including
enabling and disabling the CA (API and server aspects only).
Part of: https://fedorahosted.org/pki/ticket/1604
---
.../src/com/netscape/ca/CertificateAuthority.java | 75 ++++++++++++++++++++++
.../dogtagpki/server/ca/rest/AuthorityService.java | 37 +++++++++++
.../netscape/certsrv/authority/AuthorityData.java | 11 +++-
.../certsrv/authority/AuthorityResource.java | 31 +++++++++
.../netscape/certsrv/ca/ICertificateAuthority.java | 14 ++++
.../cmstools/authority/AuthorityCreateCLI.java | 2 +-
6 files changed, 168 insertions(+), 2 deletions(-)
diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java
index 05ab2a6024ab311b80cfd3ef026d77691169aac8..efaed10c76ee0f6b415ec220ae8b54008515c38d 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
@@ -63,6 +63,8 @@ import netscape.ldap.LDAPAttributeSet;
import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPException;
+import netscape.ldap.LDAPModification;
+import netscape.ldap.LDAPModificationSet;
import netscape.ldap.LDAPSearchResults;
import org.mozilla.jss.CryptoManager;
@@ -301,6 +303,10 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
throw new CADisabledException("Authority is disabled");
}
+ public boolean getAuthorityEnabled() {
+ return authorityEnabled;
+ }
+
/**
* Retrieves subsystem identifier.
*/
@@ -2431,4 +2437,73 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
return new CertificateAuthority(
topCA, aid, this.authorityID, nickname, description, true);
}
+
+ /**
+ * Update lightweight authority attributes.
+ *
+ * Pass null values to exclude an attribute from the update.
+ *
+ * If a passed value matches the current value, it is excluded
+ * from the update.
+ *
+ * To remove optional string values, pass the empty string.
+ */
+ public void modifyAuthority(Boolean enabled, String desc)
+ throws EBaseException {
+ if (isTopCA()) return;
+
+ LDAPModificationSet mods = new LDAPModificationSet();
+
+ boolean nextEnabled = authorityEnabled;
+ if (enabled != null && enabled.booleanValue() != authorityEnabled) {
+ mods.add(
+ LDAPModification.REPLACE,
+ new LDAPAttribute("authorityEnabled", enabled ? "TRUE" : "FALSE"));
+ nextEnabled = enabled;
+ }
+
+ String nextDesc = authorityDescription;
+ if (desc != null) {
+ if (!desc.isEmpty() && authorityDescription != null
+ && !desc.equals(authorityDescription)) {
+ mods.add(
+ LDAPModification.REPLACE,
+ new LDAPAttribute("description", desc));
+ nextDesc = desc;
+ } else if (desc.isEmpty() && authorityDescription != null) {
+ mods.add(
+ LDAPModification.DELETE,
+ new LDAPAttribute("description", authorityDescription));
+ nextDesc = null;
+ } else if (!desc.isEmpty() && authorityDescription == null) {
+ mods.add(
+ LDAPModification.ADD,
+ new LDAPAttribute("description", desc));
+ nextDesc = desc;
+ }
+ }
+
+ if (mods.size() > 0) {
+ String dn = "cn=" + authorityID.toString() + ",ou=authorities,ou="
+ + getId() + "," + getDBSubsystem().getBaseDN();
+
+ // connect to database
+ ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory("updateAuthority");
+ dbFactory.init(CMS.getConfigStore().getSubStore("internaldb"));
+ LDAPConnection conn = dbFactory.getConn();
+ try {
+ conn.modify(dn, mods);
+ } catch (LDAPException e) {
+ throw new EBaseException("Error adding sub-CA entry to database: " + e);
+ } finally {
+ dbFactory.returnConn(conn);
+ dbFactory.reset();
+ }
+
+ // update was successful; update CA's state
+ authorityEnabled = nextEnabled;
+ authorityDescription = nextDesc;
+ }
+
+ }
}
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 5ca21892d441e4b0e611d117e4dd490df05cf6e6..9a542c8b8eec3a5122d1a977a6d721d540595dad 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
@@ -141,6 +141,42 @@ public class AuthorityService extends PKIService implements AuthorityResource {
}
}
+ @Override
+ public Response modifyCA(String aidString, AuthorityData data) {
+ // Ensure all (mutable) attributes are present
+ // because PUT expects complete representation
+ if (
+ data.getEnabled() == null
+ || data.getDescription() == null
+ ) {
+ throw new BadRequestException("Incomplete data");
+ }
+
+ return patchCA(aidString, data);
+ }
+
+ @Override
+ public Response patchCA(String aidString, AuthorityData data) {
+ AuthorityID aid = null;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad CA ID: " + aidString);
+ }
+
+ ICertificateAuthority ca = topCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+
+ try {
+ ca.modifyAuthority(data.getEnabled(), data.getDescription());
+ return createOKResponse(readAuthorityData(ca));
+ } catch (EBaseException e) {
+ CMS.debug(e);
+ throw new PKIException("Error modifying authority: " + e.toString());
+ }
+ }
+
private static AuthorityData readAuthorityData(ICertificateAuthority ca)
throws PKIException {
String dn;
@@ -155,6 +191,7 @@ public class AuthorityService extends PKIService implements AuthorityResource {
dn,
ca.getAuthorityID().toString(),
parentAID != null ? parentAID.toString() : null,
+ ca.getAuthorityEnabled(),
ca.getAuthorityDescription()
);
}
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityData.java b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java
index 64f2af83720311e3eac0ee7a9197a05ff0e7198a..13c0cecab03816eee8f77409902a0ee17ec50f80 100644
--- a/base/common/src/com/netscape/certsrv/authority/AuthorityData.java
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java
@@ -77,6 +77,14 @@ public class AuthorityData {
@XmlAttribute
+ protected Boolean enabled;
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+
+ @XmlAttribute
protected String description;
public String getDescription() {
@@ -99,10 +107,11 @@ public class AuthorityData {
public AuthorityData(
String dn, String aid, String parentAID,
- String description) {
+ Boolean enabled, String description) {
this.dn = dn;
this.aid = aid;
this.parentAID = parentAID;
+ this.enabled = enabled;
this.description = description;
}
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
index 817885687b04d1b10ea35dca9a1942fe5ce201f4..24134d01178b0bee3f5c3aa951f20fbbc36e8c99 100644
--- a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
@@ -2,6 +2,7 @@ package com.netscape.certsrv.authority;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
@@ -11,6 +12,7 @@ import org.jboss.resteasy.annotations.ClientResponseType;
import com.netscape.certsrv.acls.ACLMapping;
import com.netscape.certsrv.authentication.AuthMethodMapping;
+import com.netscape.certsrv.base.PATCH;
@Path("authorities")
public interface AuthorityResource {
@@ -33,4 +35,33 @@ public interface AuthorityResource {
//@AuthMethodMapping("certs")
public Response createCA(AuthorityData data);
+ /**
+ * Modify a CA.
+ *
+ * AuthorityID, AuthorityParentID and DN are immutable;
+ * differences in these values are ignored.
+ */
+ @PUT
+ @Path("{id}")
+ @ClientResponseType(entityType=AuthorityData.class)
+ public Response modifyCA(
+ @PathParam("id") String caIDString,
+ AuthorityData data);
+
+ /**
+ * Modify a CA, supporting partial modifications.
+ *
+ * AuthorityID, AuthorityParentID and DN are immutable;
+ * these values are ignored.
+ *
+ * Other values, if null, are ignored, otherwise they are
+ * set to the new value. To remove the description, use an
+ * empty string.
+ */
+ @PATCH
+ @Path("{id}")
+ @ClientResponseType(entityType=AuthorityData.class)
+ public Response patchCA(
+ @PathParam("id") String caIDString,
+ AuthorityData data);
}
diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
index d707bf848ba0973c2a817c45abeba8219bff574f..c31d08ac75553c2a03d0cee3cf2d55a893d50761 100644
--- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
+++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
@@ -536,6 +536,11 @@ public interface ICertificateAuthority extends ISubsystem {
/**
* Return CA description. May be null.
*/
+ public boolean getAuthorityEnabled();
+
+ /**
+ * Return CA description. May be null.
+ */
public String getAuthorityDescription();
/**
@@ -559,4 +564,13 @@ public interface ICertificateAuthority extends ISubsystem {
public ICertificateAuthority createSubCA(
String dn, String desc)
throws EBaseException;
+
+ /**
+ * Update authority configurables.
+ *
+ * @param enabled Whether CA is enabled or disabled
+ * @param desc Description; null or empty removes it
+ */
+ public void modifyAuthority(Boolean enabled, String desc)
+ throws EBaseException;
}
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java
index 19329cbba0fac3f8c9722cc6854cbeaf6a31c75c..244dd51aabe5ec876849b2fb32c00f74cc140aff 100644
--- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java
@@ -78,7 +78,7 @@ public class AuthorityCreateCLI extends CLI {
String dn = cmdArgs[0];
AuthorityData data = new AuthorityData(
- dn, null, parentAIDString, desc);
+ dn, null, parentAIDString, true /* enabled */, desc);
AuthorityData newData = authorityCLI.authorityClient.createCA(data);
AuthorityCLI.printAuthorityData(newData);
}
--
2.4.3
-------------- next part --------------
From 0d79007e35cfd858938e40085ef290a48325a28e Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Thu, 17 Sep 2015 02:26:35 -0400
Subject: [PATCH 50/50] Lightweight CAs: add enable/disable CLI
Add the ca-authority-{disable,enable} commands and Java client
support for the modify and patch API.
Part of: https://fedorahosted.org/pki/ticket/1604
---
.../certsrv/authority/AuthorityClient.java | 10 ++++
.../netscape/cmstools/authority/AuthorityCLI.java | 7 ++-
.../cmstools/authority/AuthorityDisableCLI.java | 59 ++++++++++++++++++++++
.../cmstools/authority/AuthorityEnableCLI.java | 59 ++++++++++++++++++++++
4 files changed, 134 insertions(+), 1 deletion(-)
create mode 100644 base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java
create mode 100644 base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java b/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
index 7ad549c7d4bf85513aa64911fdf1c71c5ac4fc21..73dce1914fa926881012d7dd2481bbe823c13e3f 100644
--- a/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
@@ -54,4 +54,14 @@ public class AuthorityClient extends Client {
return client.getEntity(response, AuthorityData.class);
}
+ public AuthorityData modifyCA(AuthorityData data) {
+ Response response = proxy.modifyCA(data.getID(), data);
+ return client.getEntity(response, AuthorityData.class);
+ }
+
+ public AuthorityData patchCA(AuthorityData data) {
+ Response response = proxy.patchCA(data.getID(), data);
+ return client.getEntity(response, AuthorityData.class);
+ }
+
}
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
index f2b630d32f9b8fc12792d14c84e90e12f7c23f4d..f07e9916e9cd9b0f1ee1f6c6d5eb4906f9a94c85 100644
--- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
@@ -27,6 +27,8 @@ public class AuthorityCLI extends CLI {
addModule(new AuthorityFindCLI(this));
addModule(new AuthorityShowCLI(this));
addModule(new AuthorityCreateCLI(this));
+ addModule(new AuthorityDisableCLI(this));
+ addModule(new AuthorityEnableCLI(this));
}
public String getFullName() {
@@ -47,7 +49,10 @@ public class AuthorityCLI extends CLI {
protected static void printAuthorityData(AuthorityData data) {
System.out.println(" Issuer DN: " + data.getDN());
System.out.println(" ID: " + data.getID());
- System.out.println(" Parent ID: " + data.getParentID());
+ String parentAID = data.getParentID();
+ if (parentAID != null)
+ System.out.println(" Parent ID: " + data.getParentID());
+ System.out.println(" Enabled: " + data.getEnabled());
String desc = data.getDescription();
if (desc != null)
System.out.println(" Description: " + desc);
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d008676ebb41722865205a4b5078ba3393a28ce
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java
@@ -0,0 +1,59 @@
+package com.netscape.cmstools.authority;
+
+import java.util.Arrays;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.ParseException;
+
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class AuthorityDisableCLI extends CLI {
+
+ public AuthorityCLI authorityCLI;
+
+ public AuthorityDisableCLI(AuthorityCLI authorityCLI) {
+ super("disable", "Disable CAs", authorityCLI);
+ this.authorityCLI = authorityCLI;
+ }
+
+ public void printHelp() {
+ formatter.printHelp(getFullName() + " <ID>", options);
+ }
+
+ public void execute(String[] args) throws Exception {
+ // Always check for "--help" prior to parsing
+ if (Arrays.asList(args).contains("--help")) {
+ // Display usage
+ printHelp();
+ System.exit(0);
+ }
+
+ CommandLine cmd = null;
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.err.println("Error: " + e.getMessage());
+ printHelp();
+ System.exit(-1);
+ }
+
+ String[] cmdArgs = cmd.getArgs();
+
+ if (cmdArgs.length < 1) {
+ System.err.println("Error: No ID specified.");
+ printHelp();
+ System.exit(-1);
+ }
+
+ AuthorityData data = new AuthorityData(
+ null, cmdArgs[0], null, false, null);
+ data = authorityCLI.authorityClient.patchCA(data);
+ AuthorityCLI.printAuthorityData(data);
+ }
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff1f35ac4cd9e8507d9550bc22b29188ca115d9c
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java
@@ -0,0 +1,59 @@
+package com.netscape.cmstools.authority;
+
+import java.util.Arrays;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.ParseException;
+
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class AuthorityEnableCLI extends CLI {
+
+ public AuthorityCLI authorityCLI;
+
+ public AuthorityEnableCLI(AuthorityCLI authorityCLI) {
+ super("enable", "Enable CAs", authorityCLI);
+ this.authorityCLI = authorityCLI;
+ }
+
+ public void printHelp() {
+ formatter.printHelp(getFullName() + " <ID>", options);
+ }
+
+ public void execute(String[] args) throws Exception {
+ // Always check for "--help" prior to parsing
+ if (Arrays.asList(args).contains("--help")) {
+ // Display usage
+ printHelp();
+ System.exit(0);
+ }
+
+ CommandLine cmd = null;
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.err.println("Error: " + e.getMessage());
+ printHelp();
+ System.exit(-1);
+ }
+
+ String[] cmdArgs = cmd.getArgs();
+
+ if (cmdArgs.length < 1) {
+ System.err.println("Error: No ID specified.");
+ printHelp();
+ System.exit(-1);
+ }
+
+ AuthorityData data = new AuthorityData(
+ null, cmdArgs[0], null, true, null);
+ data = authorityCLI.authorityClient.patchCA(data);
+ AuthorityCLI.printAuthorityData(data);
+ }
+
+}
--
2.4.3
More information about the Pki-devel
mailing list