summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFraser Tweedale <ftweedal@redhat.com>2016-05-13 09:00:44 +1000
committerEndi S. Dewata <edewata@redhat.com>2016-06-05 18:59:30 +0200
commitcb9eb967b5e24f5fde8bbf8ae87aa615b7033db7 (patch)
tree1d51d87f1e09df6592a928e5bf66a8d0b1f4f25a
parent45c26ba97095a82bb91a12e0427fdb14cbe77699 (diff)
downloadpki-cb9eb967b5e24f5fde8bbf8ae87aa615b7033db7.tar.gz
pki-cb9eb967b5e24f5fde8bbf8ae87aa615b7033db7.tar.xz
pki-cb9eb967b5e24f5fde8bbf8ae87aa615b7033db7.zip
Lightweight CAs: add method to renew certificate
Add the CertificateAuthority.renewAuthority() method that creates and processes a renewal request for the lightweight CA's signing cert. The new certificate replaces the old certificate in the NSSDB and the serial number is stored in the 'authoritySerial' attribute. Clones observe when the 'authoritySerial' attribute has changed and update the certificate in their NSSDB, too. The renewal behaviour is available in the REST API as a POST to /ca/rest/authorities/<id>/renew. Fixes: https://fedorahosted.org/pki/ticket/2327
-rw-r--r--base/ca/src/com/netscape/ca/CertificateAuthority.java120
-rw-r--r--base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java31
-rw-r--r--base/common/src/com/netscape/certsrv/authority/AuthorityResource.java7
-rw-r--r--base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java6
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java9
5 files changed, 168 insertions, 5 deletions
diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java
index 93c5a9fb4..c9de9f9a5 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
@@ -94,6 +94,7 @@ import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.ca.IssuerUnavailableException;
import com.netscape.certsrv.cert.CertEnrollmentRequest;
import com.netscape.certsrv.dbs.IDBSubsystem;
+import com.netscape.certsrv.dbs.certdb.CertId;
import com.netscape.certsrv.dbs.certdb.ICertRecord;
import com.netscape.certsrv.dbs.certdb.ICertificateRepository;
import com.netscape.certsrv.dbs.crldb.ICRLRepository;
@@ -121,6 +122,7 @@ import com.netscape.certsrv.security.ISigningUnit;
import com.netscape.certsrv.util.IStatsSubsystem;
import com.netscape.cms.servlet.cert.CertEnrollmentRequestFactory;
import com.netscape.cms.servlet.cert.EnrollmentProcessor;
+import com.netscape.cms.servlet.cert.RenewalProcessor;
import com.netscape.cms.servlet.processors.CAProcessor;
import com.netscape.cmscore.base.ArgBlock;
import com.netscape.cmscore.dbs.CRLRepository;
@@ -210,6 +212,7 @@ public class CertificateAuthority
protected CertificateAuthority hostCA = null;
protected AuthorityID authorityID = null;
protected AuthorityID authorityParentID = null;
+ protected BigInteger authoritySerial = null;
protected String authorityDescription = null;
protected Collection<String> authorityKeyHosts = null;
protected boolean authorityEnabled = true;
@@ -346,6 +349,7 @@ public class CertificateAuthority
X500Name dn,
AuthorityID aid,
AuthorityID parentAID,
+ BigInteger serial,
String signingKeyNickname,
Collection<String> authorityKeyHosts,
String authorityDescription,
@@ -360,6 +364,7 @@ public class CertificateAuthority
this.authorityID = aid;
this.authorityParentID = parentAID;
+ this.authoritySerial = serial;
this.authorityDescription = authorityDescription;
this.authorityEnabled = authorityEnabled;
mNickname = signingKeyNickname;
@@ -526,6 +531,8 @@ public class CertificateAuthority
}
}
+ checkForNewerCert();
+
mUseNonces = mConfig.getBoolean("enableNonces", true);
mMaxNonces = mConfig.getInteger("maxNumberOfNonces", 100);
@@ -604,6 +611,49 @@ public class CertificateAuthority
}
}
+ private void checkForNewerCert() throws EBaseException {
+ if (authoritySerial == null)
+ return;
+ if (authoritySerial.equals(mCaCert.getSerialNumber()))
+ return;
+
+ // The authoritySerial recorded in LDAP differs from the
+ // certificate in NSSDB. Import the newer cert.
+ //
+ // Note that the new serial number need not be greater,
+ // e.g. if random serial numbers are enabled.
+ //
+ CMS.debug(
+ "Updating certificate in NSSDB; new serial number: "
+ + authoritySerial);
+ try {
+ X509Certificate oldCert = mCaX509Cert;
+ CryptoManager manager = CryptoManager.getInstance();
+
+ // add new cert
+ X509CertImpl newCert = mCertRepot.getX509Certificate(authoritySerial);
+ manager.importUserCACertPackage(newCert.getEncoded(), mNickname);
+
+ // delete old cert
+ manager.getInternalKeyStorageToken().getCryptoStore()
+ .deleteCert(oldCert);
+
+ // reinit signing unit
+ initSigUnit(false);
+ } catch (CertificateException e) {
+ throw new ECAException("Failed to update certificate", e);
+ } catch (CryptoManager.NotInitializedException e) {
+ throw new ECAException("CryptoManager not initialized", e);
+ } catch (CryptoManager.NicknameConflictException e) {
+ throw new ECAException("Failed to update certificate; nickname conflict", e);
+ } catch (CryptoManager.UserCertConflictException e) {
+ throw new ECAException("Failed to update certificate; user cert conflict", e);
+ } catch (TokenException | NoSuchItemOnTokenException e) {
+ // really shouldn't happen
+ throw new ECAException("Failed to update certificate", e);
+ }
+ }
+
private String authorityBaseDN() {
return "ou=authorities,ou=" + getId()
+ "," + getDBSubsystem().getBaseDN();
@@ -2580,6 +2630,8 @@ public class CertificateAuthority
addAuthorityEntry(aid, ldapEntry);
+ X509CertImpl cert = null;
+
try {
// Generate signing key
CryptoManager cryptoManager = CryptoManager.getInstance();
@@ -2628,7 +2680,7 @@ public class CertificateAuthority
throw new EBaseException("createSubCA: certificate request did not complete; status: " + requestStatus);
// Add certificate to nssdb
- X509CertImpl cert = request.getExtDataInCert(IEnrollProfile.REQUEST_ISSUED_CERT);
+ cert = request.getExtDataInCert(IEnrollProfile.REQUEST_ISSUED_CERT);
cryptoManager.importCertPackage(cert.getEncoded(), nickname);
} catch (Exception e) {
// something went wrong; delete just-added entry
@@ -2644,11 +2696,65 @@ public class CertificateAuthority
throw new ECAException("Error creating lightweight CA certificate: " + e);
}
- return new CertificateAuthority(
+ CertificateAuthority ca = new CertificateAuthority(
hostCA, subjectX500Name,
- aid, this.authorityID,
+ aid, this.authorityID, cert.getSerialNumber(),
nickname, Collections.singleton(thisClone),
description, true);
+
+ // Update authority record with serial of issued cert
+ LDAPModificationSet mods = new LDAPModificationSet();
+ mods.add(
+ LDAPModification.REPLACE,
+ new LDAPAttribute("authoritySerial", cert.getSerialNumber().toString()));
+ ca.modifyAuthorityEntry(mods);
+
+ return ca;
+ }
+
+ /**
+ * Renew certificate of this CA.
+ */
+ public void renewAuthority(HttpServletRequest httpReq)
+ throws EBaseException {
+ if (
+ authorityParentID != null
+ && !authorityParentID.equals(authorityID)
+ ) {
+ ICertificateAuthority issuer = getCA(authorityParentID);
+ issuer.ensureReady();
+ }
+
+ IProfileSubsystem ps = (IProfileSubsystem)
+ CMS.getSubsystem(IProfileSubsystem.ID);
+ IProfile profile = ps.getProfile("caManualRenewal");
+ CertEnrollmentRequest req = CertEnrollmentRequestFactory.create(
+ new ArgBlock(), profile, httpReq.getLocale());
+ req.setSerialNum(new CertId(mCaCert.getSerialNumber()));
+ RenewalProcessor processor =
+ new RenewalProcessor("renewAuthority", httpReq.getLocale());
+ Map<String, Object> resultMap =
+ processor.processRenewal(req, httpReq, null);
+ IRequest requests[] = (IRequest[]) resultMap.get(CAProcessor.ARG_REQUESTS);
+ IRequest request = requests[0];
+ Integer result = request.getExtDataInInteger(IRequest.RESULT);
+ if (result != null && !result.equals(IRequest.RES_SUCCESS))
+ throw new EBaseException("renewAuthority: certificate renewal submission resulted in error: " + result);
+ RequestStatus requestStatus = request.getRequestStatus();
+ if (requestStatus != RequestStatus.COMPLETE)
+ throw new EBaseException("renewAuthority: certificate renewal did not complete; status: " + requestStatus);
+ X509CertImpl cert = request.getExtDataInCert(IEnrollProfile.REQUEST_ISSUED_CERT);
+ authoritySerial = cert.getSerialNumber();
+
+ // Update authority record with serial of issued cert
+ LDAPModificationSet mods = new LDAPModificationSet();
+ mods.add(
+ LDAPModification.REPLACE,
+ new LDAPAttribute("authoritySerial", authoritySerial.toString()));
+ modifyAuthorityEntry(mods);
+
+ // update cert in NSSDB
+ checkForNewerCert();
}
/**
@@ -3041,6 +3147,7 @@ public class CertificateAuthority
LDAPAttribute dnAttr = entry.getAttribute("authorityDN");
LDAPAttribute parentAIDAttr = entry.getAttribute("authorityParentID");
LDAPAttribute parentDNAttr = entry.getAttribute("authorityParentDN");
+ LDAPAttribute serialAttr = entry.getAttribute("authoritySerial");
if (aidAttr == null || nickAttr == null || dnAttr == null) {
CMS.debug("Malformed authority object; required attribute(s) missing: " + entry.getDN());
@@ -3115,6 +3222,10 @@ public class CertificateAuthority
parentAID = new AuthorityID((String)
parentAIDAttr.getStringValues().nextElement());
+ BigInteger serial = null;
+ if (serialAttr != null)
+ serial = new BigInteger(serialAttr.getStringValueArray()[0]);
+
boolean enabled = true;
LDAPAttribute enabledAttr = entry.getAttribute("authorityEnabled");
if (enabledAttr != null) {
@@ -3125,7 +3236,8 @@ public class CertificateAuthority
try {
CertificateAuthority ca = new CertificateAuthority(
- hostCA, dn, aid, parentAID, keyNick, keyHosts, desc, enabled);
+ hostCA, dn, aid, parentAID, serial,
+ keyNick, keyHosts, desc, enabled);
caMap.put(aid, ca);
entryUSNs.put(aid, newEntryUSN);
nsUniqueIds.put(aid, nsUniqueId);
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 199ebef1a..0993b5c0d 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
@@ -282,6 +282,37 @@ public class AuthorityService extends PKIService implements AuthorityResource {
}
@Override
+ public Response renewCA(String aidString) {
+ AuthorityID aid = null;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad AuthorityID: " + aidString);
+ }
+
+ ICertificateAuthority ca = hostCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+
+ Map<String, String> auditParams = new LinkedHashMap<>();
+
+ try {
+ ca.renewAuthority(servletRequest);
+ audit(ILogger.SUCCESS, OpDef.OP_MODIFY, aidString, null);
+ return createNoContentResponse();
+ } catch (CADisabledException e) {
+ auditParams.put("exception", e.toString());
+ audit(ILogger.FAILURE, OpDef.OP_MODIFY, aidString, auditParams);
+ throw new ConflictingOperationException(e.toString());
+ } catch (EBaseException e) {
+ CMS.debug(e);
+ auditParams.put("exception", e.toString());
+ audit(ILogger.FAILURE, OpDef.OP_MODIFY, aidString, auditParams);
+ throw new PKIException("Error renewing authority: " + e.toString());
+ }
+ }
+
+ @Override
public Response deleteCA(String aidString) {
AuthorityID aid = null;
try {
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
index c6dc69624..0f8b70ade 100644
--- a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
@@ -94,6 +94,13 @@ public interface AuthorityResource {
@ACLMapping("authorities.modify")
public Response disableCA(@PathParam("id") String caIDString);
+ @POST
+ @Path("{id}/renew")
+ @ClientResponseType(entityType=AuthorityData.class)
+ @AuthMethodMapping("authorities")
+ @ACLMapping("authorities.modify")
+ public Response renewCA(@PathParam("id") String caIDString);
+
@DELETE
@Path("{id}")
@ClientResponseType(entityType=Void.class)
diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
index dd0d1b085..308bfba12 100644
--- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
+++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
@@ -598,6 +598,12 @@ public interface ICertificateAuthority extends ISubsystem {
throws EBaseException;
/**
+ * Renew certificate of CA.
+ */
+ public void renewAuthority(HttpServletRequest httpReq)
+ throws EBaseException;
+
+ /**
* Delete this lightweight CA.
*/
public void deleteAuthority()
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
index 8efa9162a..206d23a5d 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
@@ -19,6 +19,7 @@ package com.netscape.cms.servlet.cert;
import java.math.BigInteger;
import java.security.cert.X509Certificate;
+import java.security.Principal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
@@ -51,6 +52,7 @@ import com.netscape.certsrv.registry.IPluginInfo;
import com.netscape.certsrv.registry.IPluginRegistry;
import com.netscape.certsrv.request.IRequest;
import com.netscape.cms.profile.input.SerialNumRenewInput;
+import com.netscape.cms.realm.PKIPrincipal;
import com.netscape.cms.servlet.common.AuthCredentials;
import com.netscape.cms.servlet.common.CMSTemplate;
import com.netscape.cms.servlet.profile.SSLClientCertProvider;
@@ -265,7 +267,12 @@ public class RenewalProcessor extends CertProcessor {
context.put("origSubjectDN", origSubjectDN);
// before creating the request, authenticate the request
- IAuthToken authToken = authenticate(request, origReq, authenticator, context, true, credentials);
+ IAuthToken authToken = null;
+ Principal principal = request.getUserPrincipal();
+ if (principal instanceof PKIPrincipal)
+ authToken = ((PKIPrincipal) principal).getAuthToken();
+ if (authToken == null)
+ authToken = authenticate(request, origReq, authenticator, context, true, credentials);
// authentication success, now authorize
authorize(profileId, renewProfile, authToken);