summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEndi Sukma Dewata <edewata@redhat.com>2013-01-08 20:05:53 +0700
committerEndi Sukma Dewata <edewata@redhat.com>2013-01-09 09:05:43 -0500
commit2ff261b40c35fa25cb91f079eb8f9ac97f266db7 (patch)
tree7071497c655406a735d2be23974545387c154710
parent6efdcc4f2e6d8980437cb083d9dad09706b64e29 (diff)
downloadpki-ticket-213.zip
pki-ticket-213.tar.gz
pki-ticket-213.tar.xz
Added nonce validation for certificate revocation.ticket-213
The certificate REST service has been modified to validate nonce when revoking a certificate. Ticket #213
-rw-r--r--base/common/src/com/netscape/certsrv/cert/CertData.java17
-rw-r--r--base/common/src/com/netscape/certsrv/cert/CertRevokeRequest.java18
-rw-r--r--base/common/src/com/netscape/cms/servlet/cert/CertService.java40
-rw-r--r--base/common/src/com/netscape/cms/servlet/cert/DoRevoke.java72
-rw-r--r--base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java61
-rw-r--r--base/common/src/com/netscape/cms/servlet/processors/Processor.java2
-rw-r--r--base/java-tools/src/com/netscape/cmstools/cert/CertHoldCLI.java7
-rw-r--r--base/java-tools/src/com/netscape/cmstools/cert/CertRevokeCLI.java8
8 files changed, 148 insertions, 77 deletions
diff --git a/base/common/src/com/netscape/certsrv/cert/CertData.java b/base/common/src/com/netscape/certsrv/cert/CertData.java
index 58f7fcd..1c75d8f 100644
--- a/base/common/src/com/netscape/certsrv/cert/CertData.java
+++ b/base/common/src/com/netscape/certsrv/cert/CertData.java
@@ -63,6 +63,7 @@ public class CertData {
String notBefore;
String notAfter;
String status;
+ Long nonce;
Link link;
@@ -148,6 +149,15 @@ public class CertData {
this.status = status;
}
+ @XmlElement(name="Nonce")
+ public Long getNonce() {
+ return nonce;
+ }
+
+ public void setNonce(Long nonce) {
+ this.nonce = nonce;
+ }
+
@XmlElement(name="Link")
public Link getLink() {
return link;
@@ -163,6 +173,7 @@ public class CertData {
int result = 1;
result = prime * result + ((encoded == null) ? 0 : encoded.hashCode());
result = prime * result + ((issuerDN == null) ? 0 : issuerDN.hashCode());
+ result = prime * result + ((nonce == null) ? 0 : nonce.hashCode());
result = prime * result + ((notAfter == null) ? 0 : notAfter.hashCode());
result = prime * result + ((notBefore == null) ? 0 : notBefore.hashCode());
result = prime * result + ((pkcs7CertChain == null) ? 0 : pkcs7CertChain.hashCode());
@@ -192,6 +203,11 @@ public class CertData {
return false;
} else if (!issuerDN.equals(other.issuerDN))
return false;
+ if (nonce == null) {
+ if (other.nonce != null)
+ return false;
+ } else if (!nonce.equals(other.nonce))
+ return false;
if (notAfter == null) {
if (other.notAfter != null)
return false;
@@ -273,6 +289,7 @@ public class CertData {
before.setIssuerDN("CN=Test User,UID=testuser,O=EXAMPLE-COM");
before.setSubjectDN("CN=Test User,UID=testuser,O=EXAMPLE-COM");
before.setEncoded(sw.toString());
+ before.setNonce(12345l);
String string = before.toString();
System.out.println(string);
diff --git a/base/common/src/com/netscape/certsrv/cert/CertRevokeRequest.java b/base/common/src/com/netscape/certsrv/cert/CertRevokeRequest.java
index 7b86286..0fe0c46 100644
--- a/base/common/src/com/netscape/certsrv/cert/CertRevokeRequest.java
+++ b/base/common/src/com/netscape/certsrv/cert/CertRevokeRequest.java
@@ -63,6 +63,7 @@ public class CertRevokeRequest {
Date invalidityDate;
String comments;
String encoded;
+ Long nonce;
@XmlElement(name="RequestID")
@@ -118,6 +119,16 @@ public class CertRevokeRequest {
this.encoded = encoded;
}
+ @XmlElement(name="Nonce")
+ @FormParam("nonce")
+ public Long getNonce() {
+ return nonce;
+ }
+
+ public void setNonce(Long nonce) {
+ this.nonce = nonce;
+ }
+
@Override
public int hashCode() {
final int prime = 31;
@@ -125,6 +136,7 @@ public class CertRevokeRequest {
result = prime * result + ((comments == null) ? 0 : comments.hashCode());
result = prime * result + ((encoded == null) ? 0 : encoded.hashCode());
result = prime * result + ((invalidityDate == null) ? 0 : invalidityDate.hashCode());
+ result = prime * result + ((nonce == null) ? 0 : nonce.hashCode());
result = prime * result + ((reason == null) ? 0 : reason.hashCode());
result = prime * result + ((requestID == null) ? 0 : requestID.hashCode());
return result;
@@ -154,6 +166,11 @@ public class CertRevokeRequest {
return false;
} else if (!invalidityDate.equals(other.invalidityDate))
return false;
+ if (nonce == null) {
+ if (other.nonce != null)
+ return false;
+ } else if (!nonce.equals(other.nonce))
+ return false;
if (reason == null) {
if (other.reason != null)
return false;
@@ -194,6 +211,7 @@ public class CertRevokeRequest {
before.setInvalidityDate(new Date());
before.setComments("test");
before.setEncoded("test");
+ before.setNonce(12345l);
String string = before.toString();
System.out.println(string);
diff --git a/base/common/src/com/netscape/cms/servlet/cert/CertService.java b/base/common/src/com/netscape/cms/servlet/cert/CertService.java
index 9b7b9d4..6abe81f 100644
--- a/base/common/src/com/netscape/cms/servlet/cert/CertService.java
+++ b/base/common/src/com/netscape/cms/servlet/cert/CertService.java
@@ -25,11 +25,10 @@ import java.net.URI;
import java.security.Principal;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.List;
+import java.util.*;
+import com.netscape.certsrv.base.*;
+import com.netscape.cmscore.realm.PKIPrincipal;
import netscape.security.pkcs.ContentInfo;
import netscape.security.pkcs.PKCS7;
import netscape.security.pkcs.SignerInfo;
@@ -40,11 +39,6 @@ import netscape.security.x509.X509CertImpl;
import org.jboss.resteasy.plugins.providers.atom.Link;
import com.netscape.certsrv.apps.CMS;
-import com.netscape.certsrv.base.BadRequestException;
-import com.netscape.certsrv.base.EBaseException;
-import com.netscape.certsrv.base.ICertPrettyPrint;
-import com.netscape.certsrv.base.PKIException;
-import com.netscape.certsrv.base.UnauthorizedException;
import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.cert.CertData;
import com.netscape.certsrv.cert.CertDataInfo;
@@ -76,11 +70,17 @@ public class CertService extends PKIService implements CertResource {
ICertificateAuthority authority;
ICertificateRepository repo;
+ Random random;
+ Nonces nonces;
public final static int DEFAULT_SIZE = 20;
public CertService() {
authority = (ICertificateAuthority) CMS.getSubsystem("ca");
+ if (authority.noncesEnabled()) {
+ random = new Random();
+ nonces = authority.getNonces();
+ }
repo = authority.getCertificateRepository();
}
@@ -104,9 +104,9 @@ public class CertService extends PKIService implements CertResource {
} catch (EDBRecordNotFoundException e) {
throw new CertNotFoundException(id);
} catch (EBaseException e) {
- throw new PKIException("Problem returning certificate: " + id);
+ throw new PKIException(e.getMessage(), e);
} catch (CertificateEncodingException e) {
- throw new PKIException("Problem encoding certificate searched for: " + id);
+ throw new PKIException(e.getMessage(), e);
}
return certData;
@@ -178,11 +178,19 @@ public class CertService extends PKIService implements CertResource {
}
// Find target cert record if different from client cert.
+ processor.validateNonce(clientCert, request.getNonce());
+
ICertRecord targetRecord = id.equals(clientSerialNumber) ? clientRecord : processor.getCertificateRecord(id);
X509CertImpl targetCert = targetRecord.getCertificate();
processor.createCRLExtension();
- processor.validateCertificateToRevoke(clientSubjectDN, targetRecord, caCert);
+
+ PKIPrincipal principal = (PKIPrincipal)servletRequest.getUserPrincipal();
+ // TODO: do not hard-code role name
+ String subjectDN = principal.hasRole("Certificate Manager Agents") ?
+ null : clientSubjectDN;
+
+ processor.validateCertificateToRevoke(subjectDN, targetRecord, caCert);
processor.addCertificateToRevoke(targetCert);
processor.createRevocationRequest();
@@ -444,6 +452,14 @@ public class CertService extends PKIService implements CertResource {
certData.setStatus(record.getStatus());
+ if (nonces != null) {
+ long n = random.nextLong();
+ long m = nonces.addNonce(n, Processor.getSSLClientCertificate(servletRequest));
+ if (n + m != 0) {
+ certData.setNonce(m);
+ }
+ }
+
URI uri = uriInfo.getBaseUriBuilder().path(CertResource.class).path("{id}").build(certId.toHexString());
certData.setLink(new Link("self", uri));
diff --git a/base/common/src/com/netscape/cms/servlet/cert/DoRevoke.java b/base/common/src/com/netscape/cms/servlet/cert/DoRevoke.java
index f45947e..5d33bf2 100644
--- a/base/common/src/com/netscape/cms/servlet/cert/DoRevoke.java
+++ b/base/common/src/com/netscape/cms/servlet/cert/DoRevoke.java
@@ -47,6 +47,7 @@ import com.netscape.certsrv.authority.ICertAuthority;
import com.netscape.certsrv.authorization.AuthzToken;
import com.netscape.certsrv.authorization.EAuthzAccessDenied;
import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.ForbiddenException;
import com.netscape.certsrv.base.IArgBlock;
import com.netscape.certsrv.base.Nonces;
import com.netscape.certsrv.base.PKIException;
@@ -62,10 +63,8 @@ import com.netscape.certsrv.ra.IRegistrationAuthority;
import com.netscape.certsrv.request.IRequest;
import com.netscape.certsrv.request.RequestId;
import com.netscape.certsrv.request.RequestStatus;
-import com.netscape.certsrv.usrgrp.Certificates;
import com.netscape.certsrv.usrgrp.ICertUserLocator;
import com.netscape.certsrv.usrgrp.IUGSubsystem;
-import com.netscape.certsrv.usrgrp.IUser;
import com.netscape.cms.servlet.base.CMSServlet;
import com.netscape.cms.servlet.common.CMSRequest;
import com.netscape.cms.servlet.common.CMSTemplate;
@@ -207,47 +206,6 @@ public class DoRevoke extends CMSServlet {
}
revokeAll = req.getParameter("revokeAll");
- if (mNonces != null) {
- boolean nonceVerified = false;
- boolean skipNonceVerification = false;
-
- X509Certificate cert2 = getSSLClientCertificate(req);
- if (cert2 != null) {
- X509Certificate certChain[] = new X509Certificate[1];
- certChain[0] = cert2;
- IUser user = null;
- try {
- user = mUL.locateUser(new Certificates(certChain));
- } catch (Exception e) {
- CMS.debug("DoRevoke: Failed to map certificate '" +
- cert2.getSubjectDN().getName() + "' to user.");
- }
- if (mUG.isMemberOf(user, "Subsystem Group")) {
- skipNonceVerification = true;
- }
- }
-
- String nonceStr = req.getParameter("nonce");
- if (nonceStr != null) {
- long nonce = Long.parseLong(nonceStr.trim());
- X509Certificate cert1 = mNonces.getCertificate(nonce);
- if (cert1 == null) {
- CMS.debug("DoRevoke: Unknown nonce");
- } else if (cert1 != null && cert2 != null && cert1.equals(cert2)) {
- nonceVerified = true;
- mNonces.removeNonce(nonce);
- }
- } else {
- CMS.debug("DoRevoke: Missing nonce");
- }
- CMS.debug("DoRevoke: nonceVerified=" + nonceVerified);
- CMS.debug("DoRevoke: skipNonceVerification=" + skipNonceVerification);
- if ((!nonceVerified) && (!skipNonceVerification)) {
- cmsReq.setStatus(CMSRequest.UNAUTHORIZED);
- return;
- }
- }
-
String comments = req.getParameter(IRequest.REQUESTOR_COMMENTS);
String eeSubjectDN = null;
String eeSerialNumber = null;
@@ -279,11 +237,13 @@ public class DoRevoke extends CMSServlet {
if (mAuthMgr != null && mAuthMgr.equals(IAuthSubsystem.CERTUSERDB_AUTHMGR_ID)) {
if (authToken != null) {
+ // Request is from agent.
String serialNumber = req.getParameter("serialNumber");
getSSLClientCertificate(req); // throw exception on error
if (serialNumber != null) {
+ // Agent has null subject DN.
eeSerialNumber = serialNumber;
}
@@ -294,7 +254,7 @@ public class DoRevoke extends CMSServlet {
" authenticated by " + authMgr;
}
} else {
- // request is fromUser.
+ // Request is from user.
initiative = AuditFormat.FROMUSER;
String serialNumber = req.getParameter("serialNumber");
@@ -302,7 +262,8 @@ public class DoRevoke extends CMSServlet {
if (serialNumber == null || sslCert == null ||
!(serialNumber.equals(sslCert.getSerialNumber().toString(16)))) {
- authorized = false;
+ throw new ForbiddenException("Invalid serial number.");
+
} else {
eeSubjectDN = sslCert.getSubjectDN().toString();
eeSerialNumber = sslCert.getSerialNumber().toString();
@@ -310,19 +271,21 @@ public class DoRevoke extends CMSServlet {
}
- if (authorized) {
- BigInteger serialNumber = parseSerialNumber(eeSerialNumber);
+ BigInteger serialNumber = parseSerialNumber(eeSerialNumber);
- process(argSet, header, reason, invalidityDate, initiative,
- req, resp, verifiedRecordCount, revokeAll,
- totalRecordCount, serialNumber, eeSubjectDN,
- comments, locale[0]);
- }
+ process(argSet, header, reason, invalidityDate, initiative,
+ req, resp, verifiedRecordCount, revokeAll,
+ totalRecordCount, serialNumber, eeSubjectDN,
+ comments, locale[0]);
} catch (NumberFormatException e) {
log(ILogger.LL_FAILURE,
CMS.getLogMessage("BASE_INVALID_NUMBER_FORMAT"));
error = new EBaseException(CMS.getLogMessage("BASE_INVALID_NUMBER_FORMAT"));
+
+ } catch (ForbiddenException e) {
+ authorized = false;
+
} catch (EBaseException e) {
error = e;
}
@@ -442,6 +405,11 @@ public class DoRevoke extends CMSServlet {
processor.setAuthority((ICertificateAuthority)mAuthority);
}
+ X509Certificate clientCert = getSSLClientCertificate(req);
+ String requestedNonce = req.getParameter("nonce");
+ Long nonce = requestedNonce == null ? null : new Long(requestedNonce.trim());
+ processor.validateNonce(clientCert, nonce);
+
try {
processor.createCRLExtension();
diff --git a/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java b/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java
index 36d0045..b559015 100644
--- a/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java
+++ b/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java
@@ -27,6 +27,10 @@ import java.util.Collection;
import java.util.Date;
import java.util.Locale;
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.usrgrp.Certificates;
+import com.netscape.certsrv.usrgrp.IUser;
+import com.netscape.cms.servlet.processors.Processor;
import netscape.security.x509.CRLExtensions;
import netscape.security.x509.CRLReasonExtension;
import netscape.security.x509.InvalidityDateExtension;
@@ -35,10 +39,6 @@ import netscape.security.x509.RevokedCertImpl;
import netscape.security.x509.X509CertImpl;
import com.netscape.certsrv.apps.CMS;
-import com.netscape.certsrv.base.BadRequestException;
-import com.netscape.certsrv.base.EBaseException;
-import com.netscape.certsrv.base.EPropertyNotFound;
-import com.netscape.certsrv.base.UnauthorizedException;
import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.dbs.certdb.CertId;
import com.netscape.certsrv.dbs.certdb.ICertRecord;
@@ -51,6 +51,8 @@ import com.netscape.certsrv.request.IRequestQueue;
import com.netscape.certsrv.request.RequestId;
import com.netscape.certsrv.request.RequestStatus;
+import javax.servlet.http.HttpServletRequest;
+
/**
* @author Endi S. Dewata
*/
@@ -185,14 +187,59 @@ public class RevocationProcessor extends CertProcessor {
return request;
}
- public void validateCertificateToRevoke(String clientSubjectDN, ICertRecord targetRecord, boolean revokingCACert) {
+ public void validateNonce(X509Certificate clientCert, Long nonce) {
+
+ if (nonces != null) {
+ boolean nonceVerified = false;
+ boolean skipNonceVerification = false;
+
+ if (clientCert != null) {
+ X509Certificate certChain[] = new X509Certificate[1];
+ certChain[0] = clientCert;
+ IUser user = null;
+ try {
+ user = ul.locateUser(new Certificates(certChain));
+ } catch (Exception e) {
+ CMS.debug("RevocationProcessor: Failed to map certificate '" +
+ clientCert.getSubjectDN().getName() + "' to user.");
+ }
+ if (ug.isMemberOf(user, "Subsystem Group")) {
+ skipNonceVerification = true;
+ }
+ }
+
+ if (nonce != null) {
+ X509Certificate storedCert = nonces.getCertificate(nonce);
+ if (storedCert == null) {
+ CMS.debug("RevocationProcessor: Unknown nonce");
+
+ } else if (clientCert != null && storedCert.equals(clientCert)) {
+ nonceVerified = true;
+ nonces.removeNonce(nonce);
+ }
+ } else {
+ CMS.debug("RevocationProcessor: Missing nonce");
+ }
+
+ CMS.debug("RevocationProcessor: nonceVerified=" + nonceVerified);
+ CMS.debug("RevocationProcessor: skipNonceVerification=" + skipNonceVerification);
+ if ((!nonceVerified) && (!skipNonceVerification)) {
+ throw new ForbiddenException("Invalid nonce.");
+ }
+ }
+
+ }
+
+ public void validateCertificateToRevoke(String subjectDN, ICertRecord targetRecord, boolean revokingCACert) {
X509CertImpl targetCert = targetRecord.getCertificate();
BigInteger targetSerialNumber = targetCert.getSerialNumber();
Principal targetSubjectDN = targetCert.getSubjectDN();
- // Verify client cert's subject DN matches the target cert's subject DN.
- if (clientSubjectDN != null && !clientSubjectDN.equals(targetSubjectDN.toString())) {
+ // Verify the subject DN matches the target cert's subject DN.
+ // Agent has null subject DN so he can revoke any certificate.
+ // Other users can only revoke their own certificate.
+ if (subjectDN != null && !subjectDN.equals(targetSubjectDN.toString())) {
throw new UnauthorizedException(
"Certificate 0x" + targetSerialNumber.toString(16) + " belongs to different subject.");
}
diff --git a/base/common/src/com/netscape/cms/servlet/processors/Processor.java b/base/common/src/com/netscape/cms/servlet/processors/Processor.java
index fdbc85d..f5c57f0 100644
--- a/base/common/src/com/netscape/cms/servlet/processors/Processor.java
+++ b/base/common/src/com/netscape/cms/servlet/processors/Processor.java
@@ -33,6 +33,7 @@ import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
+import com.netscape.certsrv.usrgrp.ICertUserLocator;
import netscape.security.x509.X509CertImpl;
import com.netscape.certsrv.apps.CMS;
@@ -144,6 +145,7 @@ public class Processor {
protected ICertificateAuthority authority = (ICertificateAuthority) CMS.getSubsystem("ca");
protected IAuthzSubsystem authz = (IAuthzSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTHZ);
protected IUGSubsystem ug = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG);
+ protected ICertUserLocator ul = ug.getCertUserLocator();
protected IRequestQueue queue;
protected IProfileSubsystem ps;
protected ICertificateRepository certdb;
diff --git a/base/java-tools/src/com/netscape/cmstools/cert/CertHoldCLI.java b/base/java-tools/src/com/netscape/cmstools/cert/CertHoldCLI.java
index b5c604a..808ef60 100644
--- a/base/java-tools/src/com/netscape/cmstools/cert/CertHoldCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cert/CertHoldCLI.java
@@ -77,14 +77,14 @@ public class CertHoldCLI extends CLI {
}
CertId certID = new CertId(cmdArgs[0]);
+ CertData certData = parent.client.getCert(certID);
if (!cmd.hasOption("force")) {
- CertData certData = parent.client.getCert(certID);
-
System.out.println("Placing certificate on-hold:");
CertCLI.printCertData(certData, false, false);
+ if (verbose) System.out.println(" Nonce: " + certData.getNonce());
System.out.print("Are you sure (Y/N)? ");
System.out.flush();
@@ -99,6 +99,7 @@ public class CertHoldCLI extends CLI {
CertRevokeRequest request = new CertRevokeRequest();
request.setReason(RevocationReason.CERTIFICATE_HOLD);
request.setComments(cmd.getOptionValue("comments"));
+ request.setNonce(certData.getNonce());
CertRequestInfo certRequestInfo = parent.client.revokeCert(certID, request);
@@ -108,7 +109,7 @@ public class CertHoldCLI extends CLI {
if (certRequestInfo.getRequestStatus() == RequestStatus.COMPLETE) {
MainCLI.printMessage("Placed certificate \"" + certID.toHexString() + "\" on-hold");
- CertData certData = parent.client.getCert(certID);
+ certData = parent.client.getCert(certID);
CertCLI.printCertData(certData, false, false);
} else {
diff --git a/base/java-tools/src/com/netscape/cmstools/cert/CertRevokeCLI.java b/base/java-tools/src/com/netscape/cmstools/cert/CertRevokeCLI.java
index d8a4d52..af0c7da 100644
--- a/base/java-tools/src/com/netscape/cmstools/cert/CertRevokeCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cert/CertRevokeCLI.java
@@ -105,9 +105,9 @@ public class CertRevokeCLI extends CLI {
return;
}
- if (!cmd.hasOption("force")) {
+ CertData certData = parent.client.getCert(certID);
- CertData certData = parent.client.getCert(certID);
+ if (!cmd.hasOption("force")) {
if (reason == RevocationReason.CERTIFICATE_HOLD) {
System.out.println("Placing certificate on-hold:");
@@ -118,6 +118,7 @@ public class CertRevokeCLI extends CLI {
}
CertCLI.printCertData(certData, false, false);
+ if (verbose) System.out.println(" Nonce: " + certData.getNonce());
System.out.print("Are you sure (Y/N)? ");
System.out.flush();
@@ -132,6 +133,7 @@ public class CertRevokeCLI extends CLI {
CertRevokeRequest request = new CertRevokeRequest();
request.setReason(reason);
request.setComments(cmd.getOptionValue("comments"));
+ request.setNonce(certData.getNonce());
CertRequestInfo certRequestInfo;
@@ -154,7 +156,7 @@ public class CertRevokeCLI extends CLI {
MainCLI.printMessage("Revoked certificate \"" + certID.toHexString() + "\"");
}
- CertData certData = parent.client.getCert(certID);
+ certData = parent.client.getCert(certID);
CertCLI.printCertData(certData, false, false);
} else {