From d9b0fd7fa809cc8a57e2fe022fde22e9011bec5e Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Wed, 15 Nov 2023 20:07:35 +0100 Subject: [PATCH] Implement GET verb for /ca/v2/certs/ REST end-point This replicate the current behaviour imeplemented in /ca/rest/certs/. --- .../org/dogtagpki/server/ca/CAServlet.java | 19 ++ .../dogtagpki/server/ca/v2/CAInfoServlet.java | 9 + .../dogtagpki/server/ca/v2/CertServlet.java | 261 ++++++++++++++++++ .../netscape/certsrv/cert/CertDataInfos.java | 3 +- 4 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 base/ca/src/main/java/org/dogtagpki/server/ca/v2/CertServlet.java diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/CAServlet.java b/base/ca/src/main/java/org/dogtagpki/server/ca/CAServlet.java index 6596b49c57e..f125dd67003 100644 --- a/base/ca/src/main/java/org/dogtagpki/server/ca/CAServlet.java +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/CAServlet.java @@ -13,6 +13,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +/** + * @author Marco Fargetta + */ public class CAServlet extends HttpServlet { public static final long serialVersionUID = 1L; @@ -20,6 +23,9 @@ public class CAServlet extends HttpServlet { public void get(HttpServletRequest request, HttpServletResponse response) throws Exception { } + public void post(HttpServletRequest request, HttpServletResponse response) throws Exception { + } + @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { @@ -33,6 +39,19 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro } } + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + try { + post(request, response); + + } catch (ServletException | IOException e) { + throw e; + + } catch (Exception e) { + throw new ServletException(e); + } + } + public CAEngine getCAEngine() { ServletContext servletContext = getServletContext(); return (CAEngine) servletContext.getAttribute("engine"); diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/v2/CAInfoServlet.java b/base/ca/src/main/java/org/dogtagpki/server/ca/v2/CAInfoServlet.java index b4205c15737..548fbd78761 100644 --- a/base/ca/src/main/java/org/dogtagpki/server/ca/v2/CAInfoServlet.java +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/v2/CAInfoServlet.java @@ -1,3 +1,8 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// package org.dogtagpki.server.ca.v2; import java.io.PrintWriter; @@ -13,11 +18,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * @author Marco Fargetta + */ @WebServlet("/v2/info") public class CAInfoServlet extends CAServlet { private static final long serialVersionUID = 1L; private static Logger logger = LoggerFactory.getLogger(CAInfoServlet.class); + @Override public void get(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpSession session = request.getSession(); logger.debug("CAInfoServlet.get(): session: " + session.getId()); diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/v2/CertServlet.java b/base/ca/src/main/java/org/dogtagpki/server/ca/v2/CertServlet.java new file mode 100644 index 00000000000..e7db60c0597 --- /dev/null +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/v2/CertServlet.java @@ -0,0 +1,261 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// +package org.dogtagpki.server.ca.v2; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.security.InvalidKeyException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.dogtagpki.server.ca.CAEngine; +import org.dogtagpki.server.ca.CAServlet; +import org.dogtagpki.util.cert.CertUtil; +import org.mozilla.jss.netscape.security.pkcs.ContentInfo; +import org.mozilla.jss.netscape.security.pkcs.PKCS7; +import org.mozilla.jss.netscape.security.pkcs.SignerInfo; +import org.mozilla.jss.netscape.security.provider.RSAPublicKey; +import org.mozilla.jss.netscape.security.util.CertPrettyPrint; +import org.mozilla.jss.netscape.security.util.Utils; +import org.mozilla.jss.netscape.security.x509.AlgorithmId; +import org.mozilla.jss.netscape.security.x509.CRLExtensions; +import org.mozilla.jss.netscape.security.x509.CRLReasonExtension; +import org.mozilla.jss.netscape.security.x509.X509CertImpl; +import org.mozilla.jss.netscape.security.x509.X509ExtensionException; +import org.mozilla.jss.netscape.security.x509.X509Key; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.cert.CertData; +import com.netscape.certsrv.cert.CertDataInfo; +import com.netscape.certsrv.cert.CertDataInfos; +import com.netscape.certsrv.dbs.certdb.CertId; +import com.netscape.cmscore.dbs.CertRecord; +import com.netscape.cmscore.dbs.CertificateRepository; +import com.netscape.cmscore.dbs.RevocationInfo; +import com.netscape.cmsutil.ldap.LDAPUtil; + +/** + * @author Marco Fargetta + */ +@WebServlet("/v2/certs/*") +public class CertServlet extends CAServlet { + public static final int DEFAULT_MAXTIME = 0; + public static final int DEFAULT_MAXRESULTS = 20; + public final static int DEFAULT_SIZE = 20; + + private static final long serialVersionUID = 1L; + private static Logger logger = LoggerFactory.getLogger(CertServlet.class); + + @Override + public void get(HttpServletRequest request, HttpServletResponse response) throws Exception { + HttpSession session = request.getSession(); + logger.debug("CertServlet.get(): session: {}", session.getId()); + + response.setContentType("application/json"); + PrintWriter out = response.getWriter(); + if(request.getPathInfo() != null) { + CertId id = new CertId(request.getPathInfo().substring(1)); + CertData cert; + try { + cert = getCertData(id, request.getLocale()); + out.println(cert.toJSON()); + } catch (Exception e) { + response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI()); + } + return; + } + + int maxResults = request.getParameter("maxResults") == null ? + DEFAULT_MAXRESULTS : Integer.parseInt(request.getParameter("maxResults")); + int maxTime = request.getParameter("maxTime") == null ? + DEFAULT_MAXTIME : Integer.parseInt(request.getParameter("maxTime")); + int size = request.getParameter("size") == null ? + DEFAULT_SIZE : Integer.parseInt(request.getParameter("size")); + int start = request.getParameter("start") == null ? 0 : Integer.parseInt(request.getParameter("start")); + + CertDataInfos infos = listCerts(request.getParameter("status"), maxResults, maxTime, start, size); + out.println(infos.toJSON()); + } + + @Override + public void post(HttpServletRequest request, HttpServletResponse response) throws Exception { + HttpSession session = request.getSession(); + logger.debug("CertServlet.post(): session: {}", session.getId()); + + response.setContentType("application/json"); + PrintWriter out = response.getWriter(); + + if(request.getPathInfo() == null || !request.getPathInfo().equals("/search")) { + response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI()); + return; + } + + CertDataInfos infos = new CertDataInfos(); + out.println(infos.toString()); + } + + private CertData getCertData(CertId id, Locale loc) throws Exception { + CAEngine engine = getCAEngine(); + CertificateRepository repo = engine.getCertificateRepository(); + + //find the cert in question + CertRecord record = repo.readCertificateRecord(id.toBigInteger()); + X509CertImpl cert = record.getCertificate(); + + CertData certData = new CertData(); + certData.setSerialNumber(id); + + Principal issuerDN = cert.getIssuerName(); + if (issuerDN != null) certData.setIssuerDN(issuerDN.toString()); + + Principal subjectDN = cert.getSubjectName(); + if (subjectDN != null) certData.setSubjectDN(subjectDN.toString()); + + String base64 = CertUtil.toPEM(cert); + certData.setEncoded(base64); + + CertPrettyPrint print = new CertPrettyPrint(cert); + certData.setPrettyPrint(print.toString(loc)); + + X509Certificate[] certChain = engine.getCertChain(cert); + + PKCS7 pkcs7 = new PKCS7( + new AlgorithmId[0], + new ContentInfo(new byte[0]), + certChain, + new SignerInfo[0]); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + pkcs7.encodeSignedData(bos, false); + byte[] p7Bytes = bos.toByteArray(); + + String p7Str = Utils.base64encode(p7Bytes, true); + certData.setPkcs7CertChain(p7Str); + + Date notBefore = cert.getNotBefore(); + if (notBefore != null) certData.setNotBefore(notBefore.toString()); + + Date notAfter = cert.getNotAfter(); + if (notAfter != null) certData.setNotAfter(notAfter.toString()); + + certData.setRevokedOn(record.getRevokedOn()); + certData.setRevokedBy(record.getRevokedBy()); + + RevocationInfo revInfo = record.getRevocationInfo(); + if (revInfo != null) { + CRLExtensions revExts = revInfo.getCRLEntryExtensions(); + if (revExts != null) { + try { + CRLReasonExtension ext = (CRLReasonExtension) + revExts.get(CRLReasonExtension.NAME); + certData.setRevocationReason(ext.getReason().getCode()); + } catch (X509ExtensionException e) { + // nothing to do + } + } + } + + certData.setStatus(record.getStatus()); + + return certData; + } + + private CertDataInfos listCerts(String status, int maxResults, int maxTime, int start, int size) { + CAEngine engine = getCAEngine(); + CertificateRepository repo = engine.getCertificateRepository(); + + logger.info("Listing certificates"); + + String filter; + if (status == null) { + filter = "(certstatus=*)"; + + } else { + filter = "(certStatus=" + LDAPUtil.escapeFilter(status) + ")"; + } + logger.info("Search filter: " + filter); + + CertDataInfos infos = new CertDataInfos(); + try { + Enumeration e = repo.searchCertificates(filter, maxResults, maxTime); + if (e == null) { + throw new EBaseException("search results are null"); + } + + // store non-null results in a list + List results = new ArrayList<>(); + while (e.hasMoreElements()) { + CertRecord rec = e.nextElement(); + if (rec == null) continue; + results.add(createCertDataInfo(rec)); + } + + int total = results.size(); + logger.info("Search results: " + total); + infos.setTotal(total); + + // return entries in the requested page + for (int i = start; i < start + size && i < total ; i++) { + infos.addEntry(results.get(i)); + } + } catch (Exception e) { + logger.error("Unable to list certificates: " + e.getMessage(), e); + throw new PKIException("Unable to list certificates: " + e.getMessage(), e); + } + + return infos; + } + + private CertDataInfo createCertDataInfo(CertRecord record) throws EBaseException, InvalidKeyException { + CertDataInfo info = new CertDataInfo(); + + CertId id = new CertId(record.getSerialNumber()); + info.setID(id); + + X509Certificate cert = record.getCertificate(); + info.setIssuerDN(cert.getIssuerDN().toString()); + info.setSubjectDN(cert.getSubjectDN().toString()); + info.setStatus(record.getStatus()); + info.setVersion(cert.getVersion()); + info.setType(cert.getType()); + + PublicKey key = cert.getPublicKey(); + if (key instanceof X509Key) { + X509Key x509Key = (X509Key)key; + info.setKeyAlgorithmOID(x509Key.getAlgorithmId().getOID().toString()); + + if (x509Key.getAlgorithmId().toString().equalsIgnoreCase("RSA")) { + RSAPublicKey rsaKey = new RSAPublicKey(x509Key.getEncoded()); + info.setKeyLength(rsaKey.getKeySize()); + } + } + + info.setNotValidBefore(cert.getNotBefore()); + info.setNotValidAfter(cert.getNotAfter()); + + info.setIssuedOn(record.getCreateTime()); + info.setIssuedBy(record.getIssuedBy()); + + info.setRevokedOn(record.getRevokedOn()); + info.setRevokedBy(record.getRevokedBy()); + + return info; + } +} diff --git a/base/common/src/main/java/com/netscape/certsrv/cert/CertDataInfos.java b/base/common/src/main/java/com/netscape/certsrv/cert/CertDataInfos.java index dc0ca8b8668..e373d563c3d 100644 --- a/base/common/src/main/java/com/netscape/certsrv/cert/CertDataInfos.java +++ b/base/common/src/main/java/com/netscape/certsrv/cert/CertDataInfos.java @@ -38,10 +38,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.netscape.certsrv.base.DataCollection; +import com.netscape.certsrv.util.JSONSerializer; @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown=true) -public class CertDataInfos extends DataCollection { +public class CertDataInfos extends DataCollection implements JSONSerializer { public Element toDOM(Document document) {