/* * * Copyright (c) 2013 - 2017 Lijun Liao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the XiPKI software without * disclosing the source code of your own applications. * * For more information, please contact Lijun Liao at this * address: lijun.liao@gmail.com */ package org.xipki.pki.ca.client.impl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigInteger; import java.security.cert.CRLException; import java.security.cert.CertificateException; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1GeneralizedTime; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.cmp.CMPCertificate; import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers; import org.bouncycastle.asn1.cmp.CertRepMessage; import org.bouncycastle.asn1.cmp.CertResponse; import org.bouncycastle.asn1.cmp.CertifiedKeyPair; import org.bouncycastle.asn1.cmp.ErrorMsgContent; import org.bouncycastle.asn1.cmp.GenRepContent; import org.bouncycastle.asn1.cmp.InfoTypeAndValue; import org.bouncycastle.asn1.cmp.PKIBody; import org.bouncycastle.asn1.cmp.PKIFailureInfo; import org.bouncycastle.asn1.cmp.PKIFreeText; import org.bouncycastle.asn1.cmp.PKIHeader; import org.bouncycastle.asn1.cmp.PKIMessage; import org.bouncycastle.asn1.cmp.PKIStatus; import org.bouncycastle.asn1.cmp.PKIStatusInfo; import org.bouncycastle.asn1.cmp.RevDetails; import org.bouncycastle.asn1.cmp.RevRepContent; import org.bouncycastle.asn1.cmp.RevReqContent; import org.bouncycastle.asn1.crmf.AttributeTypeAndValue; import org.bouncycastle.asn1.crmf.CertId; import org.bouncycastle.asn1.crmf.CertReqMessages; import org.bouncycastle.asn1.crmf.CertReqMsg; import org.bouncycastle.asn1.crmf.CertRequest; import org.bouncycastle.asn1.crmf.CertTemplateBuilder; import org.bouncycastle.asn1.crmf.ProofOfPossession; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.CertificateList; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.cmp.CMPException; import org.bouncycastle.cert.cmp.CertificateConfirmationContent; import org.bouncycastle.cert.cmp.CertificateConfirmationContentBuilder; import org.bouncycastle.operator.DigestCalculatorProvider; import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xipki.commons.common.RequestResponseDebug; import org.xipki.commons.common.util.CollectionUtil; import org.xipki.commons.common.util.DateUtil; import org.xipki.commons.common.util.LogUtil; import org.xipki.commons.common.util.ParamUtil; import org.xipki.commons.common.util.StringUtil; import org.xipki.commons.common.util.XmlUtil; import org.xipki.commons.security.ConcurrentContentSigner; import org.xipki.commons.security.CrlReason; import org.xipki.commons.security.ObjectIdentifiers; import org.xipki.commons.security.SecurityFactory; import org.xipki.commons.security.XiSecurityConstants; import org.xipki.commons.security.util.X509Util; import org.xipki.pki.ca.client.api.CertprofileInfo; import org.xipki.pki.ca.client.api.PkiErrorException; import org.xipki.pki.ca.client.api.dto.CsrEnrollCertRequest; import org.xipki.pki.ca.client.api.dto.EnrollCertRequest; import org.xipki.pki.ca.client.api.dto.EnrollCertRequestEntry; import org.xipki.pki.ca.client.api.dto.EnrollCertResultEntry; import org.xipki.pki.ca.client.api.dto.EnrollCertResultResp; import org.xipki.pki.ca.client.api.dto.ErrorResultEntry; import org.xipki.pki.ca.client.api.dto.IssuerSerialEntry; import org.xipki.pki.ca.client.api.dto.ResultEntry; import org.xipki.pki.ca.client.api.dto.RevokeCertRequest; import org.xipki.pki.ca.client.api.dto.RevokeCertRequestEntry; import org.xipki.pki.ca.client.api.dto.RevokeCertResultEntry; import org.xipki.pki.ca.client.api.dto.RevokeCertResultType; import org.xipki.pki.ca.client.api.dto.UnrevokeOrRemoveCertEntry; import org.xipki.pki.ca.client.api.dto.UnrevokeOrRemoveCertRequest; import org.xipki.pki.ca.common.cmp.CmpUtf8Pairs; import org.xipki.pki.ca.common.cmp.CmpUtil; import org.xipki.pki.ca.common.cmp.PkiResponse; import org.xml.sax.SAXException; /** * @author Lijun Liao * @since 2.0.0 */ abstract class X509CmpRequestor extends CmpRequestor { private static final DigestCalculatorProvider DIGEST_CALCULATOR_PROVIDER = new BcDigestCalculatorProvider(); private static final BigInteger MINUS_ONE = BigInteger.valueOf(-1); private static final Logger LOG = LoggerFactory.getLogger(X509CmpRequestor.class); private final DocumentBuilder xmlDocBuilder; private boolean implicitConfirm = true; X509CmpRequestor(final X509Certificate requestorCert, final CmpResponder responder, final SecurityFactory securityFactory) { super(requestorCert, responder, securityFactory); xmlDocBuilder = newDocumentBuilder(); } X509CmpRequestor(final ConcurrentContentSigner requestor, final CmpResponder responder, final SecurityFactory securityFactory, final boolean signRequest) { super(requestor, responder, securityFactory, signRequest); xmlDocBuilder = newDocumentBuilder(); } public X509CRL generateCrl(final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { int action = XiSecurityConstants.CMP_ACTION_GEN_CRL; PKIMessage request = buildMessageWithXipkAction(action, null); PkiResponse response = signAndSend(request, debug); return evaluateCrlResponse(response, action); } public X509CRL downloadCurrentCrl(final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { return downloadCrl((BigInteger) null, debug); } public X509CRL downloadCrl(final BigInteger crlNumber, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { Integer action = null; PKIMessage request; if (crlNumber == null) { ASN1ObjectIdentifier type = CMPObjectIdentifiers.it_currentCRL; request = buildMessageWithGeneralMsgContent(type, null); } else { action = XiSecurityConstants.CMP_ACTION_GET_CRL_WITH_SN; request = buildMessageWithXipkAction(action, new ASN1Integer(crlNumber)); } PkiResponse response = signAndSend(request, debug); return evaluateCrlResponse(response, action); } private X509CRL evaluateCrlResponse(final PkiResponse response, final Integer xipkiAction) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonNull("response", response); checkProtection(response); PKIBody respBody = response.getPkiMessage().getBody(); int bodyType = respBody.getType(); if (PKIBody.TYPE_ERROR == bodyType) { ErrorMsgContent content = ErrorMsgContent.getInstance(respBody.getContent()); throw new PkiErrorException(content.getPKIStatusInfo()); } else if (PKIBody.TYPE_GEN_REP != bodyType) { throw new CmpRequestorException(String.format( "unknown PKI body type %s instead the expected [%s, %s]", bodyType, PKIBody.TYPE_GEN_REP, PKIBody.TYPE_ERROR)); } ASN1ObjectIdentifier expectedType = (xipkiAction == null) ? CMPObjectIdentifiers.it_currentCRL : ObjectIdentifiers.id_xipki_cmp_cmpGenmsg; GenRepContent genRep = GenRepContent.getInstance(respBody.getContent()); InfoTypeAndValue[] itvs = genRep.toInfoTypeAndValueArray(); InfoTypeAndValue itv = null; if (itvs != null && itvs.length > 0) { for (InfoTypeAndValue m : itvs) { if (expectedType.equals(m.getInfoType())) { itv = m; break; } } } if (itv == null) { throw new CmpRequestorException("the response does not contain InfoTypeAndValue " + expectedType); } ASN1Encodable certListAsn1Object = (xipkiAction == null) ? itv.getInfoValue() : extractXipkiActionContent(itv.getInfoValue(), xipkiAction); CertificateList certList = CertificateList.getInstance(certListAsn1Object); X509CRL crl; try { crl = X509Util.toX509Crl(certList); } catch (CRLException | CertificateException ex) { throw new CmpRequestorException("returned CRL is invalid: " + ex.getMessage()); } return crl; } // method evaluateCrlResponse public RevokeCertResultType revokeCertificate(final RevokeCertRequest request, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonNull("request", request); PKIMessage reqMessage = buildRevokeCertRequest(request); PkiResponse response = signAndSend(reqMessage, debug); return parse(response, request.getRequestEntries()); } public RevokeCertResultType unrevokeCertificate(final UnrevokeOrRemoveCertRequest request, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonNull("request", request); PKIMessage reqMessage = buildUnrevokeOrRemoveCertRequest(request, CrlReason.REMOVE_FROM_CRL.getCode()); PkiResponse response = signAndSend(reqMessage, debug); return parse(response, request.getRequestEntries()); } public RevokeCertResultType removeCertificate(final UnrevokeOrRemoveCertRequest request, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonNull("request", request); PKIMessage reqMessage = buildUnrevokeOrRemoveCertRequest(request, XiSecurityConstants.CMP_CRL_REASON_REMOVE); PkiResponse response = signAndSend(reqMessage, debug); return parse(response, request.getRequestEntries()); } private RevokeCertResultType parse(final PkiResponse response, final List<? extends IssuerSerialEntry> reqEntries) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonNull("response", response); checkProtection(response); PKIBody respBody = response.getPkiMessage().getBody(); int bodyType = respBody.getType(); if (PKIBody.TYPE_ERROR == bodyType) { ErrorMsgContent content = ErrorMsgContent.getInstance(respBody.getContent()); throw new PkiErrorException(content.getPKIStatusInfo()); } else if (PKIBody.TYPE_REVOCATION_REP != bodyType) { throw new CmpRequestorException(String.format( "unknown PKI body type %s instead the expected [%s, %s]", bodyType, PKIBody.TYPE_REVOCATION_REP, PKIBody.TYPE_ERROR)); } RevRepContent content = RevRepContent.getInstance(respBody.getContent()); PKIStatusInfo[] statuses = content.getStatus(); if (statuses == null || statuses.length != reqEntries.size()) { int statusesLen = 0; if (statuses != null) { statusesLen = statuses.length; } throw new CmpRequestorException(String.format( "incorrect number of status entries in response '%s' instead the expected '%s'", statusesLen, reqEntries.size())); } CertId[] revCerts = content.getRevCerts(); RevokeCertResultType result = new RevokeCertResultType(); for (int i = 0; i < statuses.length; i++) { PKIStatusInfo statusInfo = statuses[i]; int status = statusInfo.getStatus().intValue(); IssuerSerialEntry re = reqEntries.get(i); if (status != PKIStatus.GRANTED && status != PKIStatus.GRANTED_WITH_MODS) { PKIFreeText text = statusInfo.getStatusString(); String statusString = (text == null) ? null : text.getStringAt(0).getString(); ResultEntry resultEntry = new ErrorResultEntry(re.getId(), status, statusInfo.getFailInfo().intValue(), statusString); result.addResultEntry(resultEntry); continue; } CertId certId = null; if (revCerts != null) { for (CertId entry : revCerts) { if (re.getIssuer().equals(entry.getIssuer().getName()) && re.getSerialNumber().equals(entry.getSerialNumber().getValue())) { certId = entry; break; } } } if (certId == null) { LOG.warn("certId is not present in response for (issuer='{}', serialNumber={})", X509Util.getRfc4519Name(re.getIssuer()), LogUtil.formatCsn(re.getSerialNumber())); certId = new CertId(new GeneralName(re.getIssuer()), re.getSerialNumber()); continue; } ResultEntry resultEntry = new RevokeCertResultEntry(re.getId(), certId); result.addResultEntry(resultEntry); } return result; } // method parse public EnrollCertResultResp requestCertificate(final CsrEnrollCertRequest csr, final Date notBefore, final Date notAfter, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonNull("csr", csr); PKIMessage request = buildPkiMessage(csr, notBefore, notAfter); Map<BigInteger, String> reqIdIdMap = new HashMap<>(); reqIdIdMap.put(MINUS_ONE, csr.getId()); return internRequestCertificate(request, reqIdIdMap, PKIBody.TYPE_CERT_REP, debug); } public EnrollCertResultResp requestCertificate(final EnrollCertRequest req, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonNull("req", req); PKIMessage request = buildPkiMessage(req); Map<BigInteger, String> reqIdIdMap = new HashMap<>(); List<EnrollCertRequestEntry> reqEntries = req.getRequestEntries(); for (EnrollCertRequestEntry reqEntry : reqEntries) { reqIdIdMap.put(reqEntry.getCertReq().getCertReqId().getValue(), reqEntry.getId()); } int exptectedBodyType; switch (req.getType()) { case CERT_REQ: exptectedBodyType = PKIBody.TYPE_CERT_REP; break; case KEY_UPDATE: exptectedBodyType = PKIBody.TYPE_KEY_UPDATE_REP; break; default: exptectedBodyType = PKIBody.TYPE_CROSS_CERT_REP; } return internRequestCertificate(request, reqIdIdMap, exptectedBodyType, debug); } private EnrollCertResultResp internRequestCertificate(final PKIMessage reqMessage, final Map<BigInteger, String> reqIdIdMap, final int expectedBodyType, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { PkiResponse response = signAndSend(reqMessage, debug); checkProtection(response); PKIBody respBody = response.getPkiMessage().getBody(); final int bodyType = respBody.getType(); if (PKIBody.TYPE_ERROR == bodyType) { ErrorMsgContent content = ErrorMsgContent.getInstance(respBody.getContent()); throw new PkiErrorException(content.getPKIStatusInfo()); } else if (expectedBodyType != bodyType) { throw new CmpRequestorException(String.format( "unknown PKI body type %s instead the expected [%s, %s]", bodyType, expectedBodyType, PKIBody.TYPE_ERROR)); } CertRepMessage certRep = CertRepMessage.getInstance(respBody.getContent()); CertResponse[] certResponses = certRep.getResponse(); EnrollCertResultResp result = new EnrollCertResultResp(); // CA certificates CMPCertificate[] caPubs = certRep.getCaPubs(); if (caPubs != null && caPubs.length > 0) { for (int i = 0; i < caPubs.length; i++) { if (caPubs[i] != null) { result.addCaCertificate(caPubs[i]); } } } CertificateConfirmationContentBuilder certConfirmBuilder = null; if (!CmpUtil.isImplictConfirm(response.getPkiMessage().getHeader())) { certConfirmBuilder = new CertificateConfirmationContentBuilder(); } boolean requireConfirm = false; // We only accept the certificates which are requested. for (CertResponse certResp : certResponses) { PKIStatusInfo statusInfo = certResp.getStatus(); int status = statusInfo.getStatus().intValue(); BigInteger certReqId = certResp.getCertReqId().getValue(); String thisId = reqIdIdMap.get(certReqId); if (thisId != null) { reqIdIdMap.remove(certReqId); } else if (reqIdIdMap.size() == 1) { thisId = reqIdIdMap.values().iterator().next(); reqIdIdMap.clear(); } if (thisId == null) { continue; // ignore it. this cert is not requested by me } ResultEntry resultEntry; if (status == PKIStatus.GRANTED || status == PKIStatus.GRANTED_WITH_MODS) { CertifiedKeyPair cvk = certResp.getCertifiedKeyPair(); if (cvk == null) { return null; } CMPCertificate cmpCert = cvk.getCertOrEncCert().getCertificate(); if (cmpCert == null) { return null; } resultEntry = new EnrollCertResultEntry(thisId, cmpCert, status); if (certConfirmBuilder != null) { requireConfirm = true; X509CertificateHolder certHolder = null; try { certHolder = new X509CertificateHolder(cmpCert.getEncoded()); } catch (IOException ex) { resultEntry = new ErrorResultEntry(thisId, ClientErrorCode.PKISTATUS_RESPONSE_ERROR, PKIFailureInfo.systemFailure, "could not decode the certificate"); } if (certHolder != null) { certConfirmBuilder.addAcceptedCertificate(certHolder, certReqId); } } } else { PKIFreeText statusString = statusInfo.getStatusString(); String errorMessage = (statusString == null) ? null : statusString.getStringAt(0).getString(); int failureInfo = statusInfo.getFailInfo().intValue(); resultEntry = new ErrorResultEntry(thisId, status, failureInfo, errorMessage); } result.addResultEntry(resultEntry); } if (CollectionUtil.isNonEmpty(reqIdIdMap)) { for (BigInteger reqId : reqIdIdMap.keySet()) { ErrorResultEntry ere = new ErrorResultEntry(reqIdIdMap.get(reqId), ClientErrorCode.PKISTATUS_NO_ANSWER); result.addResultEntry(ere); } } if (!requireConfirm) { return result; } PKIMessage confirmRequest = buildCertConfirmRequest( response.getPkiMessage().getHeader().getTransactionID(), certConfirmBuilder); response = signAndSend(confirmRequest, debug); checkProtection(response); return result; } // method internRequestCertificate private PKIMessage buildCertConfirmRequest(final ASN1OctetString tid, final CertificateConfirmationContentBuilder certConfirmBuilder) throws CmpRequestorException { PKIHeader header = buildPkiHeader(implicitConfirm, tid, null, (InfoTypeAndValue[]) null); CertificateConfirmationContent certConfirm; try { certConfirm = certConfirmBuilder.build(DIGEST_CALCULATOR_PROVIDER); } catch (CMPException ex) { throw new CmpRequestorException(ex.getMessage(), ex); } PKIBody body = new PKIBody(PKIBody.TYPE_CERT_CONFIRM, certConfirm.toASN1Structure()); return new PKIMessage(header, body); } private PKIMessage buildRevokeCertRequest(final RevokeCertRequest request) throws CmpRequestorException { PKIHeader header = buildPkiHeader(null); List<RevokeCertRequestEntry> requestEntries = request.getRequestEntries(); List<RevDetails> revDetailsArray = new ArrayList<>(requestEntries.size()); for (RevokeCertRequestEntry requestEntry : requestEntries) { CertTemplateBuilder certTempBuilder = new CertTemplateBuilder(); certTempBuilder.setIssuer(requestEntry.getIssuer()); certTempBuilder.setSerialNumber(new ASN1Integer(requestEntry.getSerialNumber())); byte[] aki = requestEntry.getAuthorityKeyIdentifier(); if (aki != null) { Extensions certTempExts = getCertTempExtensions(aki); certTempBuilder.setExtensions(certTempExts); } Date invalidityDate = requestEntry.getInvalidityDate(); int idx = (invalidityDate == null) ? 1 : 2; Extension[] extensions = new Extension[idx]; try { ASN1Enumerated reason = new ASN1Enumerated(requestEntry.getReason()); extensions[0] = new Extension(Extension.reasonCode, true, new DEROctetString(reason.getEncoded())); if (invalidityDate != null) { ASN1GeneralizedTime time = new ASN1GeneralizedTime(invalidityDate); extensions[1] = new Extension(Extension.invalidityDate, true, new DEROctetString(time.getEncoded())); } } catch (IOException ex) { throw new CmpRequestorException(ex.getMessage(), ex); } Extensions exts = new Extensions(extensions); RevDetails revDetails = new RevDetails(certTempBuilder.build(), exts); revDetailsArray.add(revDetails); } RevReqContent content = new RevReqContent(revDetailsArray.toArray(new RevDetails[0])); PKIBody body = new PKIBody(PKIBody.TYPE_REVOCATION_REQ, content); return new PKIMessage(header, body); } // method buildRevokeCertRequest private PKIMessage buildUnrevokeOrRemoveCertRequest(final UnrevokeOrRemoveCertRequest request, final int reasonCode) throws CmpRequestorException { PKIHeader header = buildPkiHeader(null); List<UnrevokeOrRemoveCertEntry> requestEntries = request.getRequestEntries(); List<RevDetails> revDetailsArray = new ArrayList<>(requestEntries.size()); for (UnrevokeOrRemoveCertEntry requestEntry : requestEntries) { CertTemplateBuilder certTempBuilder = new CertTemplateBuilder(); certTempBuilder.setIssuer(requestEntry.getIssuer()); certTempBuilder.setSerialNumber(new ASN1Integer(requestEntry.getSerialNumber())); byte[] aki = requestEntry.getAuthorityKeyIdentifier(); if (aki != null) { Extensions certTempExts = getCertTempExtensions(aki); certTempBuilder.setExtensions(certTempExts); } Extension[] extensions = new Extension[1]; try { ASN1Enumerated reason = new ASN1Enumerated(reasonCode); extensions[0] = new Extension(Extension.reasonCode, true, new DEROctetString(reason.getEncoded())); } catch (IOException ex) { throw new CmpRequestorException(ex.getMessage(), ex); } Extensions exts = new Extensions(extensions); RevDetails revDetails = new RevDetails(certTempBuilder.build(), exts); revDetailsArray.add(revDetails); } RevReqContent content = new RevReqContent(revDetailsArray.toArray(new RevDetails[0])); PKIBody body = new PKIBody(PKIBody.TYPE_REVOCATION_REQ, content); return new PKIMessage(header, body); } // method buildUnrevokeOrRemoveCertRequest private PKIMessage buildPkiMessage(final CsrEnrollCertRequest csr, final Date notBefore, final Date notAfter) { CmpUtf8Pairs utf8Pairs = new CmpUtf8Pairs(CmpUtf8Pairs.KEY_CERT_PROFILE, csr.getCertprofile()); if (notBefore != null) { utf8Pairs.putUtf8Pair(CmpUtf8Pairs.KEY_NOT_BEFORE, DateUtil.toUtcTimeyyyyMMddhhmmss(notBefore)); } if (notAfter != null) { utf8Pairs.putUtf8Pair(CmpUtf8Pairs.KEY_NOT_AFTER, DateUtil.toUtcTimeyyyyMMddhhmmss(notAfter)); } PKIHeader header = buildPkiHeader(implicitConfirm, null, utf8Pairs); PKIBody body = new PKIBody(PKIBody.TYPE_P10_CERT_REQ, csr.getCsr()); return new PKIMessage(header, body); } private PKIMessage buildPkiMessage(final EnrollCertRequest req) { PKIHeader header = buildPkiHeader(implicitConfirm, null); List<EnrollCertRequestEntry> reqEntries = req.getRequestEntries(); CertReqMsg[] certReqMsgs = new CertReqMsg[reqEntries.size()]; for (int i = 0; i < reqEntries.size(); i++) { EnrollCertRequestEntry reqEntry = reqEntries.get(i); CmpUtf8Pairs utf8Pairs = new CmpUtf8Pairs(CmpUtf8Pairs.KEY_CERT_PROFILE, reqEntry.getCertprofile()); AttributeTypeAndValue certprofileInfo = CmpUtil.buildAttributeTypeAndValue(utf8Pairs); AttributeTypeAndValue[] atvs = (certprofileInfo == null) ? null : new AttributeTypeAndValue[]{certprofileInfo}; certReqMsgs[i] = new CertReqMsg(reqEntry.getCertReq(), reqEntry.getPopo(), atvs); } int bodyType; switch (req.getType()) { case CERT_REQ: bodyType = PKIBody.TYPE_CERT_REQ; break; case KEY_UPDATE: bodyType = PKIBody.TYPE_KEY_UPDATE_REQ; break; default: bodyType = PKIBody.TYPE_CROSS_CERT_REQ; } PKIBody body = new PKIBody(bodyType, new CertReqMessages(certReqMsgs)); return new PKIMessage(header, body); } // method buildPkiMessage private PKIMessage buildPkiMessage(final CertRequest req, final ProofOfPossession pop, final String profileName) { PKIHeader header = buildPkiHeader(implicitConfirm, null); CmpUtf8Pairs utf8Pairs = new CmpUtf8Pairs(CmpUtf8Pairs.KEY_CERT_PROFILE, profileName); AttributeTypeAndValue certprofileInfo = CmpUtil.buildAttributeTypeAndValue(utf8Pairs); CertReqMsg[] certReqMsgs = new CertReqMsg[1]; certReqMsgs[0] = new CertReqMsg(req, pop, new AttributeTypeAndValue[]{certprofileInfo}); PKIBody body = new PKIBody(PKIBody.TYPE_CERT_REQ, new CertReqMessages(certReqMsgs)); return new PKIMessage(header, body); } public PKIMessage envelope(final CertRequest req, final ProofOfPossession pop, final String profileName) throws CmpRequestorException { ParamUtil.requireNonNull("req", req); ParamUtil.requireNonNull("pop", pop); ParamUtil.requireNonNull("profileName", profileName); PKIMessage request = buildPkiMessage(req, pop, profileName); return sign(request); } public PKIMessage envelopeRevocation(final RevokeCertRequest request) throws CmpRequestorException { ParamUtil.requireNonNull("request", request); PKIMessage reqMessage = buildRevokeCertRequest(request); reqMessage = sign(reqMessage); return reqMessage; } public CaInfo retrieveCaInfo(final String caName, final RequestResponseDebug debug) throws CmpRequestorException, PkiErrorException { ParamUtil.requireNonBlank("caName", caName); ASN1EncodableVector vec = new ASN1EncodableVector(); vec.add(new ASN1Integer(2)); ASN1Sequence acceptVersions = new DERSequence(vec); int action = XiSecurityConstants.CMP_ACTION_GET_CAINFO; PKIMessage request = buildMessageWithXipkAction(action, acceptVersions); PkiResponse response = signAndSend(request, debug); ASN1Encodable itvValue = extractXipkiActionRepContent(response, action); DERUTF8String utf8Str = DERUTF8String.getInstance(itvValue); String systemInfoStr = utf8Str.getString(); LOG.debug("CAInfo for CA {}: {}", caName, systemInfoStr); Document doc; try { doc = xmlDocBuilder.parse(new ByteArrayInputStream(systemInfoStr.getBytes("UTF-8"))); } catch (SAXException | IOException ex) { throw new CmpRequestorException("could not parse the returned systemInfo for CA " + caName + ": " + ex.getMessage(), ex); } final String namespace = null; Element root = doc.getDocumentElement(); String str = root.getAttribute("version"); if (StringUtil.isBlank(str)) { str = root.getAttributeNS(namespace, "version"); } int version = StringUtil.isBlank(str) ? 1 : Integer.parseInt(str); if (version == 2) { // CACert X509Certificate caCert; String b64CaCert = XmlUtil.getValueOfFirstElementChild(root, namespace, "CACert"); try { caCert = X509Util.parseBase64EncodedCert(b64CaCert); } catch (CertificateException ex) { throw new CmpRequestorException("could no parse the CA certificate", ex); } // CmpControl ClientCmpControl cmpControl = null; Element cmpCtrlElement = XmlUtil.getFirstElementChild(root, namespace, "cmpControl"); if (cmpCtrlElement != null) { String tmpStr = XmlUtil.getValueOfFirstElementChild(cmpCtrlElement, namespace, "rrAkiRequired"); boolean required = (tmpStr == null) ? false : Boolean.parseBoolean(tmpStr); cmpControl = new ClientCmpControl(required); } // certprofiles Set<String> profileNames = new HashSet<>(); Element profilesElement = XmlUtil.getFirstElementChild(root, namespace, "certprofiles"); Set<CertprofileInfo> profiles = new HashSet<>(); if (profilesElement != null) { List<Element> profileElements = XmlUtil.getElementChilden(profilesElement, namespace, "certprofile"); for (Element element : profileElements) { String name = XmlUtil.getValueOfFirstElementChild(element, namespace, "name"); String type = XmlUtil.getValueOfFirstElementChild(element, namespace, "type"); String conf = XmlUtil.getValueOfFirstElementChild(element, namespace, "conf"); CertprofileInfo profile = new CertprofileInfo(name, type, conf); profiles.add(profile); profileNames.add(name); if (LOG.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("configured for CA ").append(caName).append(" certprofile ("); sb.append("name=").append(name).append(", "); sb.append("type=").append(type).append(", "); sb.append("conf=").append(conf).append(")"); LOG.debug(sb.toString()); } } } LOG.info("CA {} supports profiles {}", caName, profileNames); return new CaInfo(caCert, cmpControl, profiles); } else { throw new CmpRequestorException("unknown CAInfo version " + version); } } // method retrieveCaInfo private static DocumentBuilder newDocumentBuilder() { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); try { return dbf.newDocumentBuilder(); } catch (ParserConfigurationException ex) { throw new RuntimeException("could not create XML document builder", ex); } } private static Extensions getCertTempExtensions(byte[] authorityKeyIdentifier) throws CmpRequestorException { AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(authorityKeyIdentifier); byte[] encodedAki; try { encodedAki = aki.getEncoded(); } catch (IOException ex) { throw new CmpRequestorException("could not encoded AuthorityKeyIdentifier", ex); } Extension extAki = new Extension(Extension.authorityKeyIdentifier, false, encodedAki); Extensions certTempExts = new Extensions(extAki); return certTempExts; } }