-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ca certs api v2 #4635
Ca certs api v2 #4635
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,11 +13,19 @@ | |
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
/** | ||
* @author Marco Fargetta <[email protected]> | ||
*/ | ||
public class CAServlet extends HttpServlet { | ||
public static final long serialVersionUID = 1L; | ||
|
||
|
||
public void get(HttpServletRequest request, HttpServletResponse response) throws Exception { | ||
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); | ||
} | ||
|
||
public void post(HttpServletRequest request, HttpServletResponse response) throws Exception { | ||
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); | ||
} | ||
|
||
@Override | ||
|
@@ -33,6 +41,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"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <[email protected]> | ||
*/ | ||
@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()); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,263 @@ | ||
// | ||
// Copyright Red Hat, Inc. | ||
// | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// | ||
package org.dogtagpki.server.ca.v2; | ||
|
||
import java.io.BufferedReader; | ||
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.text.SimpleDateFormat; | ||
import java.util.ArrayList; | ||
import java.util.Date; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.stream.Collectors; | ||
|
||
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.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.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.cert.CertSearchRequest; | ||
import com.netscape.certsrv.dbs.certdb.CertId; | ||
import com.netscape.certsrv.util.JSONSerializer; | ||
import com.netscape.cms.servlet.cert.FilterBuilder; | ||
import com.netscape.cmscore.dbs.CertRecord; | ||
import com.netscape.cmscore.dbs.CertificateRepository; | ||
import com.netscape.cmscore.dbs.RevocationInfo; | ||
|
||
/** | ||
* @author Marco Fargetta <[email protected]> | ||
*/ | ||
@WebServlet("/v2/certs/*") | ||
public class CertServlet extends CAServlet { | ||
public static final int DEFAULT_MAXTIME = 0; | ||
public static final 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 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")); | ||
|
||
CertSearchRequest searchElems = CertSearchRequest.fromMap(request.getParameterMap()); | ||
CertDataInfos infos = listCerts(searchElems, 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()); | ||
|
||
if(request.getPathInfo() == null || !request.getPathInfo().equals("/search")) { | ||
response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI()); | ||
return; | ||
} | ||
|
||
BufferedReader reader = request.getReader(); | ||
String postMessage = reader.lines().collect(Collectors.joining()); | ||
|
||
CertSearchRequest requestFilter = JSONSerializer.fromJSON(postMessage, CertSearchRequest.class); | ||
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(requestFilter, start, size); | ||
|
||
response.setContentType("application/json"); | ||
PrintWriter out = response.getWriter(); | ||
out.println(infos.toJSON()); | ||
} | ||
|
||
private CertData getCertData(CertId id, Locale loc) throws Exception { | ||
CAEngine engine = getCAEngine(); | ||
CertificateRepository repo = engine.getCertificateRepository(); | ||
|
||
//find the cert in question | ||
CertRecord certRecord = repo.readCertificateRecord(id.toBigInteger()); | ||
X509CertImpl cert = certRecord.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 = Utils.base64encode(cert.getEncoded(), true); | ||
|
||
certData.setEncoded(base64); | ||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering if we should skip this field since not all clients needs to get the PKCS #7 cert chain, so maybe it can be provided by a different API. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can remove this field when the new API is created in a separate PR. |
||
|
||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); | ||
Date notBefore = cert.getNotBefore(); | ||
if (notBefore != null) certData.setNotBefore(sdf.format(notBefore)); | ||
|
||
Date notAfter = cert.getNotAfter(); | ||
if (notAfter != null) certData.setNotAfter(sdf.format(notAfter)); | ||
|
||
certData.setRevokedOn(certRecord.getRevokedOn()); | ||
certData.setRevokedBy(certRecord.getRevokedBy()); | ||
|
||
RevocationInfo revInfo = certRecord.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) { | ||
logger.debug("CRL extension error for certificate {}", id.toHexString()); | ||
} | ||
} | ||
} | ||
|
||
certData.setStatus(certRecord.getStatus()); | ||
|
||
return certData; | ||
} | ||
|
||
private CertDataInfos listCerts(CertSearchRequest searchReq, int start, int size) { | ||
return listCerts(searchReq, -1, start, size); | ||
} | ||
|
||
private CertDataInfos listCerts(CertSearchRequest searchReq, int maxTime, int start, int size) { | ||
CAEngine engine = getCAEngine(); | ||
CertificateRepository repo = engine.getCertificateRepository(); | ||
|
||
logger.info("Listing certificates"); | ||
FilterBuilder builder = new FilterBuilder(searchReq); | ||
String filter = builder.buildFilter(); | ||
|
||
logger.info("Search filter: " + filter); | ||
|
||
CertDataInfos infos = new CertDataInfos(); | ||
try { | ||
Iterator<CertRecord> e = repo.searchCertificates(filter, maxTime, start, size); | ||
if (e == null) { | ||
throw new EBaseException("search results are null"); | ||
} | ||
|
||
// store non-null results in a list | ||
List<CertDataInfo> results = new ArrayList<>(); | ||
while (e.hasNext()) { | ||
CertRecord rec = e.next(); | ||
if (rec == null) continue; | ||
results.add(createCertDataInfo(rec)); | ||
} | ||
|
||
infos.setTotal(results.size()); | ||
logger.info("Search results: " + results.size()); | ||
infos.setEntries(results); | ||
} 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 certRecord) throws EBaseException, InvalidKeyException { | ||
CertDataInfo info = new CertDataInfo(); | ||
|
||
CertId id = new CertId(certRecord.getSerialNumber()); | ||
info.setID(id); | ||
|
||
X509Certificate cert = certRecord.getCertificate(); | ||
info.setIssuerDN(cert.getIssuerDN().toString()); | ||
info.setSubjectDN(cert.getSubjectDN().toString()); | ||
info.setStatus(certRecord.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(certRecord.getCreateTime()); | ||
info.setIssuedBy(certRecord.getIssuedBy()); | ||
|
||
info.setRevokedOn(certRecord.getRevokedOn()); | ||
info.setRevokedBy(certRecord.getRevokedBy()); | ||
|
||
return info; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
timeLimit
doesn't seem to be used. Should we keep it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is implemented in the pagedSeearch. I will add to the call.