summaryrefslogtreecommitdiffstats
path: root/base/ca
diff options
context:
space:
mode:
authorFraser Tweedale <ftweedal@redhat.com>2015-01-28 02:41:10 -0500
committerFraser Tweedale <ftweedal@redhat.com>2015-09-26 14:11:51 +1000
commit2a9f56d02b4a284cda6f8b61b250e1494f19a83e (patch)
tree9b12125932ed41a5dbe06f8dafb66656e78c7ad8 /base/ca
parenta5a50e95a691587e22335018538b4f578dfee6d1 (diff)
downloadpki-2a9f56d02b4a284cda6f8b61b250e1494f19a83e.tar.gz
pki-2a9f56d02b4a284cda6f8b61b250e1494f19a83e.tar.xz
pki-2a9f56d02b4a284cda6f8b61b250e1494f19a83e.zip
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
Diffstat (limited to 'base/ca')
-rw-r--r--base/ca/shared/conf/acl.ldif2
-rw-r--r--base/ca/shared/conf/acl.properties4
-rw-r--r--base/ca/shared/conf/auth-method.properties1
-rw-r--r--base/ca/shared/conf/db.ldif5
-rw-r--r--base/ca/shared/webapps/ca/WEB-INF/web.xml10
-rw-r--r--base/ca/src/com/netscape/ca/CAService.java53
-rw-r--r--base/ca/src/com/netscape/ca/CertificateAuthority.java680
-rw-r--r--base/ca/src/com/netscape/ca/SigningUnit.java22
-rw-r--r--base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java282
-rw-r--r--base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java3
-rw-r--r--base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java5
11 files changed, 968 insertions, 99 deletions
diff --git a/base/ca/shared/conf/acl.ldif b/base/ca/shared/conf/acl.ldif
index 0da10939f..54c9f1d5c 100644
--- a/base/ca/shared/conf/acl.ldif
+++ b/base/ca/shared/conf/acl.ldif
@@ -57,3 +57,5 @@ resourceACLS: certServer.ca.certs:execute:allow (execute) group="Certificate Man
resourceACLS: certServer.ca.groups:execute:allow (execute) group="Administrators":Admins may execute group operations
resourceACLS: certServer.ca.selftests:read,execute:allow (read,execute) group="Administrators":Only admins can access selftests.
resourceACLS: certServer.ca.users:execute:allow (execute) group="Administrators":Admins may execute user operations
+resourceACLS: certServer.ca.authorities:list,read:allow (list,read) user="anybody":Anybody may list and read lightweight authorities
+resourceACLS: certServer.ca.authorities:create,modify:allow (create,modify) group="Administrators":Administrators may create and modify lightweight authorities
diff --git a/base/ca/shared/conf/acl.properties b/base/ca/shared/conf/acl.properties
index d14d1832c..f0b5b9f65 100644
--- a/base/ca/shared/conf/acl.properties
+++ b/base/ca/shared/conf/acl.properties
@@ -21,3 +21,7 @@ securityDomain.installToken = certServer.securitydomain.domainxml,read
selftests.read = certServer.ca.selftests,read
selftests.execute = certServer.ca.selftests,execute
users = certServer.ca.users,execute
+authorities.create = certServer.ca.authorities,create
+authorities.list = certServer.ca.authorities,list
+authorities.modify = certServer.ca.authorities,modify
+authorities.read = certServer.ca.authorities,read
diff --git a/base/ca/shared/conf/auth-method.properties b/base/ca/shared/conf/auth-method.properties
index a213534ad..8d67690af 100644
--- a/base/ca/shared/conf/auth-method.properties
+++ b/base/ca/shared/conf/auth-method.properties
@@ -8,6 +8,7 @@
default = *
account = certUserDBAuthMgr,passwdUserDBAuthMgr
+authorities = certUserDBAuthMgr
certs = certUserDBAuthMgr
certrequests = certUserDBAuthMgr
groups = certUserDBAuthMgr
diff --git a/base/ca/shared/conf/db.ldif b/base/ca/shared/conf/db.ldif
index 8a2e0b072..704b8d11b 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/shared/webapps/ca/WEB-INF/web.xml b/base/ca/shared/webapps/ca/WEB-INF/web.xml
index bba40e203..628eea2a2 100644
--- a/base/ca/shared/webapps/ca/WEB-INF/web.xml
+++ b/base/ca/shared/webapps/ca/WEB-INF/web.xml
@@ -2417,6 +2417,16 @@
<security-constraint>
<web-resource-collection>
+ <web-resource-name>Authority Services</web-resource-name>
+ <url-pattern>/rest/authorities/*</url-pattern>
+ </web-resource-collection>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+
+ <security-constraint>
+ <web-resource-collection>
<web-resource-name>Security Domain Services</web-resource-name>
<url-pattern>/rest/securityDomain/installToken</url-pattern>
</web-resource-collection>
diff --git a/base/ca/src/com/netscape/ca/CAService.java b/base/ca/src/com/netscape/ca/CAService.java
index 36f0bd592..a49d641ce 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 acf07b9bd..42a0ec4d1 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
@@ -23,36 +23,26 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
+import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
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.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;
import javax.servlet.http.HttpSession;
-import netscape.security.util.DerOutputStream;
-import netscape.security.util.DerValue;
-import netscape.security.x509.AlgorithmId;
-import netscape.security.x509.CertificateChain;
-import netscape.security.x509.CertificateIssuerName;
-import netscape.security.x509.CertificateSubjectName;
-import netscape.security.x509.CertificateVersion;
-import netscape.security.x509.X500Name;
-import netscape.security.x509.X509CRLImpl;
-import netscape.security.x509.X509CertImpl;
-import netscape.security.x509.X509CertInfo;
-import netscape.security.x509.X509ExtensionException;
-import netscape.security.x509.X509Key;
-
import org.mozilla.jss.CryptoManager;
import org.mozilla.jss.asn1.ASN1Util;
import org.mozilla.jss.asn1.GeneralizedTime;
@@ -60,6 +50,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 +66,21 @@ 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.CADisabledException;
+import com.netscape.certsrv.ca.CANotFoundException;
+import com.netscape.certsrv.ca.CATypeException;
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.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 +95,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 +107,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 +125,29 @@ import com.netscape.cmsutil.ocsp.SingleResponse;
import com.netscape.cmsutil.ocsp.TBSRequest;
import com.netscape.cmsutil.ocsp.UnknownInfo;
+import netscape.ldap.LDAPAttribute;
+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 netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.x509.AlgorithmId;
+import netscape.security.x509.CertificateChain;
+import netscape.security.x509.CertificateIssuerName;
+import netscape.security.x509.CertificateSubjectName;
+import netscape.security.x509.CertificateVersion;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CRLImpl;
+import netscape.security.x509.X509CertImpl;
+import netscape.security.x509.X509CertInfo;
+import netscape.security.x509.X509ExtensionException;
+import netscape.security.x509.X509Key;
+
+
/**
* A class represents a Certificate Authority that is
* responsible for certificate specific operations.
@@ -136,6 +161,13 @@ 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 hostCA = null;
+ protected AuthorityID authorityID = null;
+ protected AuthorityID authorityParentID = null;
+ protected String authorityDescription = null;
+ protected boolean authorityEnabled = true;
+
protected ISubsystem mOwner = null;
protected IConfigStore mConfig = null;
protected ILogger mLogger = CMS.getLogger();
@@ -234,6 +266,41 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
* Constructs a CA subsystem.
*/
public CertificateAuthority() {
+ hostCA = this;
+ }
+
+ /**
+ * Construct and initialise a lightweight authority
+ */
+ private CertificateAuthority(
+ CertificateAuthority hostCA,
+ AuthorityID aid,
+ AuthorityID parentAID,
+ String signingKeyNickname,
+ String authorityDescription,
+ boolean authorityEnabled
+ ) throws EBaseException {
+ setId(hostCA.getId());
+ this.hostCA = hostCA;
+ this.authorityID = aid;
+ this.authorityParentID = parentAID;
+ this.authorityDescription = authorityDescription;
+ this.authorityEnabled = authorityEnabled;
+ mNickname = signingKeyNickname;
+ init(hostCA.mOwner, hostCA.mConfig);
+ }
+
+ public boolean isHostAuthority() {
+ return hostCA == this;
+ }
+
+ private void ensureEnabled() throws CADisabledException {
+ if (!authorityEnabled)
+ throw new CADisabledException("Authority is disabled");
+ }
+
+ public boolean getAuthorityEnabled() {
+ return authorityEnabled;
}
/**
@@ -334,8 +401,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 (isHostAuthority()) {
+ 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 = hostCA.mReplicaRepot;
+ }
// init signing unit & CA cert.
try {
@@ -358,51 +439,37 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
if (CMS.isPreOpMode())
return;
- // set certificate status to 10 minutes
- mCertRepot.setCertStatusUpdateInterval(
+ /* The host CA owns these resources so skip these
+ * steps for lightweight CAs.
+ */
+ if (isHostAuthority()) {
+ /* 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"));
-
- initMiscellaneousListeners();
+ mService.init(config.getSubStore("connector"));
- // 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 +479,9 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
// being functional.
initCRL();
+ if (isHostAuthority())
+ loadLightweightCAs();
+
} catch (EBaseException e) {
if (CMS.isPreOpMode())
return;
@@ -420,6 +490,37 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
}
}
+ private void initCRLPublisher() throws EBaseException {
+ // instantiate CRL publisher
+ if (!isHostAuthority()) {
+ mByName = hostCA.mByName;
+ mCRLPublisher = hostCA.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 +641,11 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
mService.startup();
mRequestQueue.recover();
- // Note that this could be null.
-
- // setup Admin operations
-
- initNotificationListeners();
-
- startPublish();
- // startCRL();
+ if (isHostAuthority()) {
+ // setup Admin operations
+ initNotificationListeners();
+ startPublish();
+ }
}
/**
@@ -555,6 +653,10 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
* <P>
*/
public void shutdown() {
+ // lightweight authorities don't own these resources
+ if (!isHostAuthority())
+ return;
+
Enumeration<ICRLIssuingPoint> enums = mCRLIssuePoints.elements();
while (enums.hasMoreElements()) {
CRLIssuingPoint point = (CRLIssuingPoint) enums.nextElement();
@@ -967,6 +1069,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");
@@ -1039,6 +1142,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
public X509CertImpl sign(X509CertInfo certInfo, String algname)
throws EBaseException {
+ ensureEnabled();
X509CertImpl signedcert = null;
@@ -1123,6 +1227,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
public byte[] sign(byte[] data, String algname)
throws EBaseException {
+ ensureEnabled();
return mSigningUnit.sign(data, algname);
}
@@ -1228,13 +1333,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 (isHostAuthority() && CrlStore != null && CrlStore.size() > 0) {
mCRLSigningUnit = new SigningUnit();
mCRLSigningUnit.init(this, mConfig.getSubStore(PROP_CRL_SIGNING_SUBSTORE));
} else {
@@ -1304,7 +1409,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
IConfigStore OCSPStore = mConfig.getSubStore(PROP_OCSP_SIGNING_SUBSTORE);
- if (OCSPStore != null && OCSPStore.size() > 0) {
+ if (isHostAuthority() && 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 +1548,13 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
/**
* init cert & crl database
*/
- private void initCaDatabases()
+ private void initCertDatabase()
throws EBaseException {
+ if (!isHostAuthority()) {
+ mCertRepot = hostCA.mCertRepot;
+ return;
+ }
+
int certdb_inc = mConfig.getInteger(PROP_CERTDB_INC, 5);
String certReposDN = mConfig.getString(PROP_CERT_REPOS_DN, null);
@@ -1471,8 +1581,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 (!isHostAuthority()) {
+ mCRLRepot = hostCA.mCRLRepot;
+ return;
+ }
int crldb_inc = mConfig.getInteger(PROP_CRLDB_INC, 5);
@@ -1482,14 +1601,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 +1624,11 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
private void initPublish()
throws EBaseException {
+ if (!isHostAuthority()) {
+ mPublisherProcessor = hostCA.mPublisherProcessor;
+ return;
+ }
+
IConfigStore c = null;
try {
@@ -1676,6 +1792,15 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
*/
private void initRequestQueue()
throws EBaseException {
+ if (!isHostAuthority()) {
+ mPolicy = hostCA.mPolicy;
+ mService = hostCA.mService;
+ mNotify = hostCA.mNotify;
+ mPNotify = hostCA.mPNotify;
+ mRequestQueue = hostCA.mRequestQueue;
+ return;
+ }
+
mPolicy = new CAPolicy();
mPolicy.init(this, mConfig.getSubStore(PROP_POLICY));
CMS.debug("CA policy inited");
@@ -1734,6 +1859,11 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
@SuppressWarnings("unchecked")
private void initCRL()
throws EBaseException {
+ if (!isHostAuthority()) {
+ mCRLIssuePoints = hostCA.mCRLIssuePoints;
+ mMasterCRLIssuePoint = hostCA.mMasterCRLIssuePoint;
+ return;
+ }
IConfigStore crlConfig = mConfig.getSubStore(PROP_CRL_SUBSTORE);
if ((crlConfig == null) || (crlConfig.size() <= 0)) {
@@ -1799,6 +1929,109 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
log(ILogger.LL_INFO, "CRL Issuing Points inited");
}
+ /**
+ * Find, instantiate and register lightweight CAs.
+ *
+ * This method must only be called by the host CA.
+ */
+ private synchronized void loadLightweightCAs() throws EBaseException {
+ ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory("loadLightweightCAs");
+ dbFactory.init(CMS.getConfigStore().getSubStore("internaldb"));
+ LDAPConnection conn = dbFactory.getConn();
+
+ String searchDN = "ou=authorities,ou=" + getId()
+ + "," + getDBSubsystem().getBaseDN();
+ LDAPSearchResults results = null;
+ boolean foundHostAuthority = false;
+ boolean haveLightweightCAsContainer = true;
+ 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 nickAttr = entry.getAttribute("authorityKeyNickname");
+ LDAPAttribute dnAttr = entry.getAttribute("authorityDN");
+ LDAPAttribute parentAIDAttr = entry.getAttribute("authorityParentID");
+ LDAPAttribute parentDNAttr = entry.getAttribute("authorityParentDN");
+
+ if (aidAttr == null || nickAttr == null || dnAttr == null)
+ throw new ECAException("Malformed authority object; required attribute(s) missing: " + entry.getDN());
+
+ AuthorityID aid = new AuthorityID((String)
+ aidAttr.getStringValues().nextElement());
+
+ X500Name dn = null;
+ try {
+ dn = new X500Name((String) dnAttr.getStringValues().nextElement());
+ } catch (IOException e) {
+ throw new ECAException("Malformed authority object; invalid authorityDN: " + entry.getDN());
+ }
+
+ String desc = null;
+ LDAPAttribute descAttr = entry.getAttribute("description");
+ if (descAttr != null)
+ desc = (String) descAttr.getStringValues().nextElement();
+
+ if (dn.equals(mName)) {
+ foundHostAuthority = true;
+ this.authorityID = aid;
+ this.authorityDescription = desc;
+ caMap.put(aid, this);
+ continue;
+ }
+
+ @SuppressWarnings("unused")
+ X500Name parentDN = null;
+ if (parentDNAttr != null) {
+ try {
+ parentDN = new X500Name((String) parentDNAttr.getStringValues().nextElement());
+ } catch (IOException e) {
+ throw new ECAException("Malformed authority object; invalid authorityParentDN: " + entry.getDN());
+ }
+ }
+
+ String keyNick = (String) nickAttr.getStringValues().nextElement();
+ AuthorityID parentAID = null;
+ if (parentAIDAttr != null)
+ parentAID = new AuthorityID((String)
+ parentAIDAttr.getStringValues().nextElement());
+
+ boolean enabled = true;
+ LDAPAttribute enabledAttr = entry.getAttribute("authorityEnabled");
+ if (enabledAttr != null) {
+ String enabledString = (String)
+ enabledAttr.getStringValues().nextElement();
+ enabled = enabledString.equalsIgnoreCase("TRUE");
+ }
+
+ CertificateAuthority ca = new CertificateAuthority(
+ this, aid, parentAID, keyNick, desc, enabled);
+ caMap.put(aid, ca);
+ }
+ } catch (LDAPException e) {
+ if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
+ CMS.debug(
+ "Missing lightweight CAs container '" + searchDN
+ + "'. Disabling lightweight CAs.");
+ haveLightweightCAsContainer = false;
+ } else {
+ throw new ECAException("Failed to execute LDAP search for lightweight CAs: " + e);
+ }
+ } finally {
+ dbFactory.returnConn(conn);
+ dbFactory.reset();
+ }
+
+ if (haveLightweightCAsContainer && !foundHostAuthority) {
+ CMS.debug("loadLightweightCAs: no entry for host authority");
+ CMS.debug("loadLightweightCAs: adding entry for host authority");
+ caMap.put(addHostAuthorityEntry(), this);
+ }
+ }
+
public String getOfficialName() {
return OFFICIAL_NAME;
}
@@ -1960,6 +2193,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
}
private BasicOCSPResponse sign(ResponseData rd) throws EBaseException {
+ ensureEnabled();
try (DerOutputStream out = new DerOutputStream()) {
DerOutputStream tmp = new DerOutputStream();
@@ -2083,4 +2317,310 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
return new SingleResponse(cid, certStatus, thisUpdate, nextUpdate);
}
+
+ /**
+ * Enumerate all authorities (including host authority)
+ */
+ public synchronized List<ICertificateAuthority> getCAs() {
+ List<ICertificateAuthority> cas = new ArrayList<>();
+ for (ICertificateAuthority ca : caMap.values()) {
+ cas.add(ca);
+ }
+ return cas;
+ }
+
+ /**
+ * Get authority by ID.
+ *
+ * @param aid The ID of the CA to retrieve, or null
+ * to retreive the host authority.
+ *
+ * @return the authority, or null if not found
+ */
+ public ICertificateAuthority getCA(AuthorityID aid) {
+ return aid == null ? hostCA : caMap.get(aid);
+ }
+
+ public ICertificateAuthority getCA(X500Name dn) {
+ for (ICertificateAuthority ca : getCAs()) {
+ if (ca.getX500Name().equals(dn))
+ return ca;
+ }
+ return null;
+ }
+
+ public AuthorityID getAuthorityID() {
+ return authorityID;
+ }
+
+ public AuthorityID getAuthorityParentID() {
+ return authorityParentID;
+ }
+
+ public String getAuthorityDescription() {
+ return authorityDescription;
+ }
+
+ /**
+ * Create a new lightweight authority.
+ *
+ * @param subjectDN Subject DN for new CA
+ * @param parentAID ID of parent 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 ca = parentCA.createSubCA(
+ subjectDN, description);
+ synchronized (this) {
+ caMap.put(ca.getAuthorityID(), ca);
+ }
+ return ca;
+ }
+
+ private void ensureAuthorityDNAvailable(X500Name dn)
+ throws IssuerUnavailableException {
+ for (ICertificateAuthority ca : getCAs()) {
+ if (ca.getX500Name().equals(dn))
+ throw new IssuerUnavailableException(
+ "DN '" + dn + "' is used by an existing authority");
+ }
+ }
+
+ /**
+ * Create a new lightweight authority signed by this authority.
+ *
+ * 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 requested DN
+ X500Name subjectX500Name = null;
+ try {
+ subjectX500Name = new X500Name(subjectDN);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ "Invalid Subject DN: " + subjectDN);
+ }
+ ensureAuthorityDNAvailable(subjectX500Name);
+
+ // generate authority ID and nickname
+ AuthorityID aid = new AuthorityID();
+ String aidString = aid.toString();
+ String nickname = hostCA.getNickname() + " " + aidString;
+
+ // build database entry
+ String dn = "cn=" + aidString + ",ou=authorities,ou="
+ + getId() + "," + getDBSubsystem().getBaseDN();
+ CMS.debug("createSubCA: DN = " + dn);
+ String parentDNString = null;
+ try {
+ parentDNString = mName.toLdapDNString();
+ } catch (IOException e) {
+ throw new EBaseException("Failed to convert issuer DN to string: " + e);
+ }
+
+ LDAPAttribute[] attrs = {
+ new LDAPAttribute("objectclass", "authority"),
+ new LDAPAttribute("cn", aidString),
+ new LDAPAttribute("authorityID", aidString),
+ new LDAPAttribute("authorityKeyNickname", nickname),
+ new LDAPAttribute("authorityEnabled", "TRUE"),
+ new LDAPAttribute("authorityDN", subjectDN),
+ new LDAPAttribute("authorityParentDN", parentDNString)
+ };
+ 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 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 lightweight CA certificate: " + e);
+ }
+ } catch (LDAPException e) {
+ throw new EBaseException("Error adding authority entry to database: " + e);
+ } finally {
+ dbFactory.returnConn(conn);
+ dbFactory.reset();
+ }
+
+ return new CertificateAuthority(
+ hostCA, aid, this.authorityID, nickname, description, true);
+ }
+
+ /**
+ * Add an LDAP entry for the host authority.
+ *
+ * This method also sets the authorityID and authorityDescription
+ * fields.
+ *
+ * It is the caller's responsibility to add the returned
+ * AuthorityID to the caMap.
+ */
+ private AuthorityID addHostAuthorityEntry() throws EBaseException {
+ if (!isHostAuthority())
+ throw new EBaseException("Can only invoke from host CA");
+
+ // generate authority ID
+ AuthorityID aid = new AuthorityID();
+ String aidString = aid.toString();
+
+ // build database entry
+ String dn = "cn=" + aidString + ",ou=authorities,ou="
+ + getId() + "," + getDBSubsystem().getBaseDN();
+ String dnString = null;
+ try {
+ dnString = mName.toLdapDNString();
+ } catch (IOException e) {
+ throw new EBaseException("Failed to convert issuer DN to string: " + e);
+ }
+
+ String desc = "Host authority";
+ LDAPAttribute[] attrs = {
+ new LDAPAttribute("objectclass", "authority"),
+ new LDAPAttribute("cn", aidString),
+ new LDAPAttribute("authorityID", aidString),
+ new LDAPAttribute("authorityKeyNickname", getNickname()),
+ new LDAPAttribute("authorityEnabled", "TRUE"),
+ new LDAPAttribute("authorityDN", dnString),
+ new LDAPAttribute("description", desc)
+ };
+ LDAPAttributeSet attrSet = new LDAPAttributeSet(attrs);
+ LDAPEntry ldapEntry = new LDAPEntry(dn, attrSet);
+
+ // connect to database
+ ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory("addHostAuthorityEntry");
+ dbFactory.init(CMS.getConfigStore().getSubStore("internaldb"));
+ LDAPConnection conn = dbFactory.getConn();
+
+ try {
+ conn.add(ldapEntry);
+ } catch (LDAPException e) {
+ throw new ELdapException("Error adding host authority entry to database: " + e);
+ } finally {
+ dbFactory.returnConn(conn);
+ dbFactory.reset();
+ }
+
+ this.authorityID = aid;
+ this.authorityDescription = desc;
+ return aid;
+ }
+
+ /**
+ * 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 (isHostAuthority() && enabled != null && !enabled)
+ throw new CATypeException("Cannot disable the host CA");
+
+ 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 authority 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/com/netscape/ca/SigningUnit.java b/base/ca/src/com/netscape/ca/SigningUnit.java
index 2466fb652..0410bd290 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 000000000..820f8ab64
--- /dev/null
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
@@ -0,0 +1,282 @@
+//--- 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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+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.UriInfo;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authority.AuthorityData;
+import com.netscape.certsrv.authority.AuthorityResource;
+import com.netscape.certsrv.base.BadRequestException;
+import com.netscape.certsrv.base.ConflictingOperationException;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.ForbiddenException;
+import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.base.ResourceNotFoundException;
+import com.netscape.certsrv.ca.AuthorityID;
+import com.netscape.certsrv.ca.CANotFoundException;
+import com.netscape.certsrv.ca.CATypeException;
+import com.netscape.certsrv.ca.ICertificateAuthority;
+import com.netscape.certsrv.ca.IssuerUnavailableException;
+import com.netscape.cms.servlet.base.PKIService;
+import com.netscape.cmsutil.util.Utils;
+
+/**
+ * @author ftweedal
+ */
+public class AuthorityService extends PKIService implements AuthorityResource {
+
+ ICertificateAuthority hostCA;
+
+ public AuthorityService() {
+ hostCA = (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 : hostCA.getCAs())
+ results.add(readAuthorityData(ca));
+
+ GenericEntity<List<AuthorityData>> entity =
+ new GenericEntity<List<AuthorityData>>(results) {};
+ return Response.ok(entity).build();
+ }
+
+ @Override
+ public Response getCA(String aidString) {
+ ICertificateAuthority ca = hostCA;
+
+ if (!AuthorityResource.HOST_AUTHORITY.equals(aidString)) {
+ AuthorityID aid;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad CA ID: " + aidString);
+ }
+
+ ca = hostCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+ }
+
+ return createOKResponse(readAuthorityData(ca));
+ }
+
+ @Override
+ public Response getCert(String aidString) {
+ AuthorityID aid;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad CA ID: " + aidString);
+ }
+
+ ICertificateAuthority ca = hostCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+
+ try {
+ return Response.ok(ca.getCaX509Cert().getEncoded()).build();
+ } catch (CertificateEncodingException e) {
+ // this really is a 500 Internal Server Error
+ throw new PKIException("Error encoding certificate: " + e);
+ }
+ }
+
+ @Override
+ public Response getCertPEM(String aidString) {
+ byte[] der = (byte[]) getCert(aidString).getEntity();
+ return Response.ok(toPem("CERTIFICATE", der)).build();
+ }
+
+ @Override
+ public Response getChain(String aidString) {
+ AuthorityID aid;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad CA ID: " + aidString);
+ }
+
+ ICertificateAuthority ca = hostCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ ca.getCACertChain().encode(out);
+ } catch (IOException e) {
+ throw new PKIException("Error encoding certificate chain: " + e);
+ }
+
+ return Response.ok(out.toByteArray()).build();
+ }
+
+ @Override
+ public Response getChainPEM(String aidString) {
+ byte[] der = (byte[]) getCert(aidString).getEntity();
+ return Response.ok(toPem("PKCS7", der)).build();
+ }
+
+ @Override
+ public Response createCA(AuthorityData data) {
+ String parentAIDString = data.getParentID();
+ AuthorityID parentAID = null;
+ try {
+ parentAID = new AuthorityID(parentAIDString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad Authority ID: " + parentAIDString);
+ }
+
+ try {
+ ICertificateAuthority subCA = hostCA.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 CA: " + e.toString());
+ }
+ }
+
+ @Override
+ public Response modifyCA(String aidString, AuthorityData data) {
+ AuthorityID aid = null;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad CA ID: " + aidString);
+ }
+
+ ICertificateAuthority ca = hostCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+
+ try {
+ ca.modifyAuthority(data.getEnabled(), data.getDescription());
+ return createOKResponse(readAuthorityData(ca));
+ } catch (CATypeException e) {
+ throw new ForbiddenException(e.toString());
+ } catch (IssuerUnavailableException e) {
+ throw new ConflictingOperationException(e.toString());
+ } catch (EBaseException e) {
+ CMS.debug(e);
+ throw new PKIException("Error modifying authority: " + e.toString());
+ }
+ }
+
+ @Override
+ public Response enableCA(String aidString) {
+ return modifyCA(
+ aidString,
+ new AuthorityData(null, null, null, null, true, null));
+ }
+
+ @Override
+ public Response disableCA(String aidString) {
+ return modifyCA(
+ aidString,
+ new AuthorityData(null, null, null, null, false, null));
+ }
+
+ 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(
+ ca.isHostAuthority(),
+ dn,
+ ca.getAuthorityID().toString(),
+ parentAID != null ? parentAID.toString() : null,
+ ca.getAuthorityEnabled(),
+ ca.getAuthorityDescription()
+ );
+ }
+
+ private String toPem(String name, byte[] data) {
+ return "-----BEGIN " + name + "-----\n" +
+ Utils.base64encode(data) +
+ "-----END " + name + "-----\n";
+ }
+
+ /* 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 16eae7877..235ea105b 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/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
index 95f1f4c20..1da1ce171 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java
@@ -38,9 +38,11 @@ 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.UnauthorizedException;
+import com.netscape.certsrv.ca.CADisabledException;
import com.netscape.certsrv.cert.CertEnrollmentRequest;
import com.netscape.certsrv.cert.CertRequestInfo;
import com.netscape.certsrv.cert.CertRequestInfos;
@@ -210,6 +212,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),