/*
*
* 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.server.impl.scep;
import java.io.IOException;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.pkcs.CertificationRequest;
import org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.CertificateList;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSAbsentContent;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.commons.audit.AuditEvent;
import org.xipki.commons.audit.AuditStatus;
import org.xipki.commons.common.InvalidConfException;
import org.xipki.commons.common.ObjectCreationException;
import org.xipki.commons.common.util.CollectionUtil;
import org.xipki.commons.common.util.LogUtil;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.commons.security.HashAlgoType;
import org.xipki.commons.security.KeyCertPair;
import org.xipki.commons.security.SignatureAlgoControl;
import org.xipki.commons.security.SignerConf;
import org.xipki.commons.security.X509Cert;
import org.xipki.commons.security.util.X509Util;
import org.xipki.pki.ca.api.NameId;
import org.xipki.pki.ca.api.OperationException;
import org.xipki.pki.ca.api.OperationException.ErrorCode;
import org.xipki.pki.ca.api.RequestType;
import org.xipki.pki.ca.api.publisher.x509.X509CertificateInfo;
import org.xipki.pki.ca.server.impl.ByUserRequestorInfo;
import org.xipki.pki.ca.server.impl.CaAuditConstants;
import org.xipki.pki.ca.server.impl.CaManagerImpl;
import org.xipki.pki.ca.server.impl.CertTemplateData;
import org.xipki.pki.ca.server.impl.KnowCertResult;
import org.xipki.pki.ca.server.impl.X509Ca;
import org.xipki.pki.ca.server.impl.util.CaUtil;
import org.xipki.pki.ca.server.mgmt.api.CaMgmtException;
import org.xipki.pki.ca.server.mgmt.api.CaStatus;
import org.xipki.pki.ca.server.mgmt.api.PermissionConstants;
import org.xipki.pki.ca.server.mgmt.api.x509.ScepControl;
import org.xipki.pki.ca.server.mgmt.api.x509.ScepEntry;
import org.xipki.pki.scep.crypto.ScepHashAlgoType;
import org.xipki.pki.scep.exception.MessageDecodingException;
import org.xipki.pki.scep.exception.MessageEncodingException;
import org.xipki.pki.scep.message.CaCaps;
import org.xipki.pki.scep.message.DecodedPkiMessage;
import org.xipki.pki.scep.message.EnvelopedDataDecryptor;
import org.xipki.pki.scep.message.EnvelopedDataDecryptorInstance;
import org.xipki.pki.scep.message.IssuerAndSubject;
import org.xipki.pki.scep.message.PkiMessage;
import org.xipki.pki.scep.transaction.CaCapability;
import org.xipki.pki.scep.transaction.FailInfo;
import org.xipki.pki.scep.transaction.MessageType;
import org.xipki.pki.scep.transaction.Nonce;
import org.xipki.pki.scep.transaction.PkiStatus;
import org.xipki.pki.scep.transaction.TransactionId;
/**
*
* @author Lijun Liao
* @since 2.0.0
*
*/
public class Scep {
private static final Logger LOG = LoggerFactory.getLogger(Scep.class);
private static final long DFLT_MAX_SIGNINGTIME_BIAS = 5L * 60 * 1000; // 5 minutes
private static final Set<ASN1ObjectIdentifier> AES_ENC_ALGOS = new HashSet<>();
private final String name;
private final NameId caIdent;
private final ScepEntry dbEntry;
private final Set<String> certProfiles;
private final ScepControl control;
private final CaManagerImpl caManager;
private final PrivateKey responderKey;
private final X509Certificate responderCert;
private final CaCaps caCaps;
private final EnvelopedDataDecryptor envelopedDataDecryptor;
private X509Cert caCert;
private CaCertRespBytes caCertRespBytes;
private long maxSigningTimeBiasInMs = DFLT_MAX_SIGNINGTIME_BIAS;
static {
AES_ENC_ALGOS.add(CMSAlgorithm.AES128_CBC);
AES_ENC_ALGOS.add(CMSAlgorithm.AES128_CCM);
AES_ENC_ALGOS.add(CMSAlgorithm.AES128_GCM);
AES_ENC_ALGOS.add(CMSAlgorithm.AES192_CBC);
AES_ENC_ALGOS.add(CMSAlgorithm.AES192_CCM);
AES_ENC_ALGOS.add(CMSAlgorithm.AES192_GCM);
AES_ENC_ALGOS.add(CMSAlgorithm.AES256_CBC);
AES_ENC_ALGOS.add(CMSAlgorithm.AES256_CCM);
AES_ENC_ALGOS.add(CMSAlgorithm.AES256_GCM);
}
public Scep(final ScepEntry dbEntry, final CaManagerImpl caManager) throws CaMgmtException {
this.caManager = ParamUtil.requireNonNull("caManager", caManager);
this.dbEntry = ParamUtil.requireNonNull("dbEntry", dbEntry);
this.name = dbEntry.getName();
this.caIdent = dbEntry.getCaIdent();
this.certProfiles = dbEntry.getCertProfiles();
try {
this.control = new ScepControl(dbEntry.getControl());
} catch (InvalidConfException ex) {
throw new CaMgmtException(ex);
}
LOG.info("SCEP {}: caCert.included={}, signerCert.included={}", this.caIdent,
this.control.isIncludeCaCert(), this.control.isIncludeSignerCert());
String type = dbEntry.getResponderType();
if (!"PKCS12".equalsIgnoreCase(type) && !"JKS".equalsIgnoreCase(type)) {
throw new CaMgmtException("unsupported SCEP responder type '" + type + "'");
}
KeyCertPair privKeyAndCert;
try {
// ResponderConf does not contain algo.
SignerConf signerConf = new SignerConf(dbEntry.getResponderConf(), HashAlgoType.SHA256,
new SignatureAlgoControl());
privKeyAndCert = caManager.getSecurityFactory().createPrivateKeyAndCert(
dbEntry.getResponderType(), signerConf, dbEntry.getCertificate());
} catch (ObjectCreationException ex) {
throw new CaMgmtException(ex);
}
this.responderKey = privKeyAndCert.getPrivateKey();
this.responderCert = privKeyAndCert.getCertificate();
if (!(responderCert.getPublicKey() instanceof RSAPublicKey)) {
throw new IllegalArgumentException(
"The responder key is not RSA key for CA " + caIdent);
}
// CACaps
CaCaps caps = new CaCaps();
caps.addCapability(CaCapability.AES);
caps.addCapability(CaCapability.DES3);
caps.addCapability(CaCapability.POSTPKIOperation);
caps.addCapability(CaCapability.Renewal);
caps.addCapability(CaCapability.SHA1);
caps.addCapability(CaCapability.SHA256);
caps.addCapability(CaCapability.SHA512);
this.caCaps = caps;
EnvelopedDataDecryptorInstance di = new EnvelopedDataDecryptorInstance(responderCert,
responderKey);
this.envelopedDataDecryptor = new EnvelopedDataDecryptor(di);
}
/**
*
* @param ms signing time bias in milliseconds. non-positive value deactivate the check of
* signing time.
*/
public void setMaxSigningTimeBias(final long ms) {
this.maxSigningTimeBiasInMs = ms;
}
public String getName() {
return name;
}
public NameId getCaIdent() {
return caIdent;
}
public ScepEntry getDbEntry() {
return dbEntry;
}
public CaCaps getCaCaps() {
return caCaps;
}
public CaCertRespBytes getCaCertResp() throws OperationException {
refreshCa();
return caCertRespBytes;
}
public boolean supportsCertProfile(final String profileName) throws CaMgmtException {
if (certProfiles.contains("ALL") || certProfiles.contains(profileName.toUpperCase())) {
return caManager.getX509Ca(caIdent).supportsCertProfile(profileName);
} else {
return false;
}
}
public CaStatus getStatus() {
if (!dbEntry.isActive() || dbEntry.isFaulty()) {
return CaStatus.INACTIVE;
}
try {
return caManager.getX509Ca(caIdent).getCaInfo().getStatus();
} catch (CaMgmtException ex) {
LogUtil.error(LOG, ex);
return CaStatus.INACTIVE;
}
}
public ContentInfo servicePkiOperation(final CMSSignedData requestContent,
final String certProfileName, final String msgId, final AuditEvent event)
throws MessageDecodingException, OperationException {
CaStatus status = getStatus();
if (CaStatus.ACTIVE != status) {
LOG.warn("SCEP {} is not active", caIdent);
throw new OperationException(ErrorCode.SYSTEM_UNAVAILABLE);
}
DecodedPkiMessage req = DecodedPkiMessage.decode(requestContent,
envelopedDataDecryptor, null);
PkiMessage rep = doServicePkiOperation(requestContent, req, certProfileName, msgId, event);
audit(event, CaAuditConstants.NAME_SCEP_pkiStatus, rep.getPkiStatus().toString());
if (rep.getPkiStatus() == PkiStatus.FAILURE) {
event.setStatus(AuditStatus.FAILED);
}
if (rep.getFailInfo() != null) {
audit(event, CaAuditConstants.NAME_SCEP_failInfo, rep.getFailInfo().toString());
}
return encodeResponse(rep, req);
} // method servicePkiOperation
private PkiMessage doServicePkiOperation(final CMSSignedData requestContent,
final DecodedPkiMessage req, final String certProfileName, final String msgId,
final AuditEvent event)
throws MessageDecodingException, OperationException {
ParamUtil.requireNonNull("requestContent", requestContent);
ParamUtil.requireNonNull("req", req);
String tid = req.getTransactionId().getId();
// verify and decrypt the request
audit(event, CaAuditConstants.NAME_tid, tid);
if (req.getFailureMessage() != null) {
audit(event, CaAuditConstants.NAME_SCEP_failureMessage, req.getFailureMessage());
}
Boolean bo = req.isSignatureValid();
if (bo != null && !bo.booleanValue()) {
audit(event, CaAuditConstants.NAME_SCEP_signature, "invalid");
}
bo = req.isDecryptionSuccessful();
if (bo != null && !bo.booleanValue()) {
audit(event, CaAuditConstants.NAME_SCEP_decryption, "failed");
}
PkiMessage rep = new PkiMessage(req.getTransactionId(), MessageType.CertRep,
Nonce.randomNonce());
rep.setRecipientNonce(req.getSenderNonce());
if (req.getFailureMessage() != null) {
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badRequest);
}
bo = req.isSignatureValid();
if (bo != null && !bo.booleanValue()) {
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badMessageCheck);
}
bo = req.isDecryptionSuccessful();
if (bo != null && !bo.booleanValue()) {
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badRequest);
}
Date signingTime = req.getSigningTime();
if (maxSigningTimeBiasInMs > 0) {
boolean isTimeBad = false;
if (signingTime == null) {
isTimeBad = true;
} else {
long now = System.currentTimeMillis();
long diff = now - signingTime.getTime();
if (diff < 0) {
diff = -1 * diff;
}
isTimeBad = diff > maxSigningTimeBiasInMs;
}
if (isTimeBad) {
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badTime);
}
} // end if
// check the digest algorithm
String oid = req.getDigestAlgorithm().getId();
ScepHashAlgoType hashAlgoType = ScepHashAlgoType.forNameOrOid(oid);
if (hashAlgoType == null) {
LOG.warn("tid={}: unknown digest algorithm {}", tid, oid);
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badAlg);
} else {
boolean supported = false;
if (hashAlgoType == ScepHashAlgoType.SHA1) {
if (caCaps.containsCapability(CaCapability.SHA1)) {
supported = true;
}
} else if (hashAlgoType == ScepHashAlgoType.SHA256) {
if (caCaps.containsCapability(CaCapability.SHA256)) {
supported = true;
}
} else if (hashAlgoType == ScepHashAlgoType.SHA512) {
if (caCaps.containsCapability(CaCapability.SHA512)) {
supported = true;
}
}
if (!supported) {
LOG.warn("tid={}: unsupported digest algorithm {}", tid, oid);
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badAlg);
}
}
// check the content encryption algorithm
ASN1ObjectIdentifier encOid = req.getContentEncryptionAlgorithm();
if (CMSAlgorithm.DES_EDE3_CBC.equals(encOid)) {
if (!caCaps.containsCapability(CaCapability.DES3)) {
LOG.warn("tid={}: encryption with DES3 algorithm is not permitted", tid, encOid);
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badAlg);
}
} else if (AES_ENC_ALGOS.contains(encOid)) {
if (!caCaps.containsCapability(CaCapability.AES)) {
LOG.warn("tid={}: encryption with AES algorithm {} is not permitted", tid, encOid);
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badAlg);
}
} else {
LOG.warn("tid={}: encryption with algorithm {} is not permitted", tid, encOid);
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(FailInfo.badAlg);
}
if (rep.getPkiStatus() == PkiStatus.FAILURE) {
return rep;
}
X509Ca ca;
try {
ca = caManager.getX509Ca(caIdent);
} catch (CaMgmtException ex) {
LogUtil.error(LOG, ex, tid + "=" + tid + ",could not get X509CA");
throw new OperationException(ErrorCode.SYSTEM_FAILURE, ex);
}
X500Name caX500Name = ca.getCaInfo().getCertificate().getSubjectAsX500Name();
try {
SignedData signedData;
MessageType mt = req.getMessageType();
audit(event, CaAuditConstants.NAME_SCEP_messageType, mt.toString());
switch (mt) {
case PKCSReq:
case RenewalReq:
case UpdateReq:
CertificationRequest csr = CertificationRequest.getInstance(req.getMessageData());
X500Name reqSubject = csr.getCertificationRequestInfo().getSubject();
String reqSubjectText = X509Util.getRfc4519Name(reqSubject);
LOG.info("tid={}, subject={}", tid, reqSubjectText);
try {
ca.checkCsr(csr);
} catch (OperationException ex) {
LogUtil.warn(LOG, ex, "tid=" + tid + " POPO verification failed");
throw FailInfoException.BAD_MESSAGE_CHECK;
}
CertificationRequestInfo csrReqInfo = csr.getCertificationRequestInfo();
X509Certificate reqSignatureCert = req.getSignatureCert();
boolean selfSigned = reqSignatureCert.getSubjectX500Principal().equals(
reqSignatureCert.getIssuerX500Principal());
String cn = X509Util.getCommonName(csrReqInfo.getSubject());
if (cn == null) {
throw new OperationException(ErrorCode.BAD_CERT_TEMPLATE,
"tid=" + tid + ": no CommonName in requested subject");
}
NameId userIdent = null;
String challengePwd = CaUtil.getChallengePassword(csrReqInfo);
if (challengePwd != null) {
String[] strs = challengePwd.split(":");
if (strs == null || strs.length != 2) {
LOG.warn("tid={}: challengePassword does not have the"
+ " format <user>:<password>", tid);
throw FailInfoException.BAD_REQUEST;
}
String user = strs[0];
String password = strs[1];
userIdent = ca.authenticateUser(user, password.getBytes());
if (userIdent == null) {
LOG.warn("tid={}: could not authenticate user {}", tid, user);
throw FailInfoException.BAD_REQUEST;
}
} // end if
if (selfSigned) {
if (MessageType.PKCSReq != mt) {
LOG.warn("tid={}: self-signed certificate is not permitted for"
+ " messageType {}", tid, mt);
throw FailInfoException.BAD_REQUEST;
}
if (userIdent == null) {
LOG.warn("tid={}: could not extract user & password from challengePassword"
+ ", which are required for self-signed signature certificate",
tid);
throw FailInfoException.BAD_REQUEST;
}
} else {
// No challengePassword is sent, try to find out whether the signature
// certificate is known by the CA
if (userIdent == null) {
// up to draft-nourse-scep-23 the client sends all messages to enroll
// certificate via MessageType PKCSReq
KnowCertResult knowCertRes = ca.knowsCertificate(reqSignatureCert);
if (!knowCertRes.isKnown()) {
LOG.warn("tid={}: signature certificate is not trusted by the CA", tid);
throw FailInfoException.BAD_REQUEST;
}
Integer userId = knowCertRes.getUserId();
if (userId == null) {
LOG.warn("tid={}: could not extract user from the signature cert", tid);
throw FailInfoException.BAD_REQUEST;
}
userIdent = ca.getUserIdent(userId);
} // end if
} // end if
ByUserRequestorInfo requestor = ca.getByUserRequestor(userIdent);
checkUserPermission(requestor, certProfileName);
byte[] tidBytes = getTransactionIdBytes(tid);
Extensions extensions = CaUtil.getExtensions(csrReqInfo);
CertTemplateData certTemplateData = new CertTemplateData(csrReqInfo.getSubject(),
csrReqInfo.getSubjectPublicKeyInfo(), (Date) null, (Date) null, extensions,
certProfileName);
X509CertificateInfo cert = ca.generateCertificate(certTemplateData, requestor,
RequestType.SCEP, tidBytes, msgId);
/* Don't save SCEP message, since it contains password in plaintext
if (ca.getCaInfo().isSaveRequest() && cert.getCert().getCertId() != null) {
byte[] encodedRequest;
try {
encodedRequest = requestContent.getEncoded();
} catch (IOException ex) {
LOG.warn("could not encode request");
encodedRequest = null;
}
if (encodedRequest != null) {
long reqId = ca.addRequest(encodedRequest);
ca.addRequestCert(reqId, cert.getCert().getCertId());
}
}*/
signedData = buildSignedData(cert.getCert().getCert());
break;
case CertPoll:
IssuerAndSubject is = IssuerAndSubject.getInstance(req.getMessageData());
audit(event, CaAuditConstants.NAME_issuer, X509Util.getRfc4519Name(is.getIssuer()));
audit(event, CaAuditConstants.NAME_subject,
X509Util.getRfc4519Name(is.getSubject()));
ensureIssuedByThisCa(caX500Name, is.getIssuer());
signedData = pollCert(ca, is.getSubject(), req.getTransactionId());
break;
case GetCert:
IssuerAndSerialNumber isn = IssuerAndSerialNumber.getInstance(req.getMessageData());
BigInteger serial = isn.getSerialNumber().getPositiveValue();
audit(event, CaAuditConstants.NAME_issuer, X509Util.getRfc4519Name(isn.getName()));
audit(event, CaAuditConstants.NAME_serial, LogUtil.formatCsn(serial));
ensureIssuedByThisCa(caX500Name, isn.getName());
signedData = getCert(ca, isn.getSerialNumber().getPositiveValue());
break;
case GetCRL:
isn = IssuerAndSerialNumber.getInstance(req.getMessageData());
serial = isn.getSerialNumber().getPositiveValue();
audit(event, CaAuditConstants.NAME_issuer, X509Util.getRfc4519Name(isn.getName()));
audit(event, CaAuditConstants.NAME_serial, LogUtil.formatCsn(serial));
ensureIssuedByThisCa(caX500Name, isn.getName());
signedData = getCrl(ca, serial);
break;
default:
LOG.error("unknown SCEP messageType '{}'", req.getMessageType());
throw FailInfoException.BAD_REQUEST;
} // end switch<
ContentInfo ci = new ContentInfo(CMSObjectIdentifiers.signedData, signedData);
rep.setMessageData(ci);
rep.setPkiStatus(PkiStatus.SUCCESS);
} catch (FailInfoException ex) {
LogUtil.error(LOG, ex);
rep.setPkiStatus(PkiStatus.FAILURE);
rep.setFailInfo(ex.getFailInfo());
}
return rep;
} // method doServicePkiOperation
private SignedData getCert(final X509Ca ca, final BigInteger serialNumber)
throws FailInfoException, OperationException {
X509Certificate cert;
try {
cert = ca.getCertificate(serialNumber);
} catch (CertificateException ex) {
final String message = "could not get certificate for CA '" + caIdent
+ "' and serialNumber=" + LogUtil.formatCsn(serialNumber) + ")";
LogUtil.error(LOG, ex, message);
throw new OperationException(ErrorCode.SYSTEM_FAILURE, ex);
}
if (cert == null) {
throw FailInfoException.BAD_CERTID;
}
return buildSignedData(cert);
} // method getCert
private SignedData pollCert(final X509Ca ca, final X500Name subject, final TransactionId tid)
throws FailInfoException, OperationException {
byte[] tidBytes = getTransactionIdBytes(tid.getId());
List<X509Certificate> certs = ca.getCertificate(subject, tidBytes);
if (CollectionUtil.isEmpty(certs)) {
certs = ca.getCertificate(subject, null);
}
if (CollectionUtil.isEmpty(certs)) {
throw FailInfoException.BAD_CERTID;
}
if (certs.size() > 1) {
LOG.warn("given certId (subject: {}) and transactionId {} match multiple certificates",
X509Util.getRfc4519Name(subject), tid.getId());
throw FailInfoException.BAD_CERTID;
}
return buildSignedData(certs.get(0));
} // method pollCert
private SignedData buildSignedData(final X509Certificate cert) throws OperationException {
CMSSignedDataGenerator cmsSignedDataGen = new CMSSignedDataGenerator();
try {
X509CertificateHolder certHolder = new X509CertificateHolder(cert.getEncoded());
cmsSignedDataGen.addCertificate(certHolder);
if (control.isIncludeCaCert()) {
refreshCa();
cmsSignedDataGen.addCertificate(caCert.getCertHolder());
}
CMSSignedData signedData = cmsSignedDataGen.generate(new CMSAbsentContent());
return SignedData.getInstance(signedData.toASN1Structure().getContent());
} catch (CMSException | IOException | CertificateEncodingException ex) {
LogUtil.error(LOG, ex);
throw new OperationException(ErrorCode.SYSTEM_FAILURE, ex);
}
} // method buildSignedData
private SignedData getCrl(final X509Ca ca, final BigInteger serialNumber)
throws FailInfoException, OperationException {
CertificateList crl = ca.getBcCurrentCrl();
if (crl == null) {
throw FailInfoException.BAD_REQUEST;
}
CMSSignedDataGenerator cmsSignedDataGen = new CMSSignedDataGenerator();
cmsSignedDataGen.addCRL(new X509CRLHolder(crl));
CMSSignedData signedData;
try {
signedData = cmsSignedDataGen.generate(new CMSAbsentContent());
} catch (CMSException ex) {
LogUtil.error(LOG, ex, "could not generate CMSSignedData");
throw new OperationException(ErrorCode.SYSTEM_FAILURE, ex);
}
return SignedData.getInstance(signedData.toASN1Structure().getContent());
} // method getCrl
private ContentInfo encodeResponse(final PkiMessage response, final DecodedPkiMessage request)
throws OperationException {
ParamUtil.requireNonNull("response", response);
ParamUtil.requireNonNull("request", request);
String signatureAlgorithm = getSignatureAlgorithm(responderKey,
request.getDigestAlgorithm());
ContentInfo ci;
try {
X509Certificate[] cmsCertSet = control.isIncludeSignerCert()
? new X509Certificate[]{responderCert} : null;
ci = response.encode(responderKey, signatureAlgorithm, responderCert, cmsCertSet,
request.getSignatureCert(), request.getContentEncryptionAlgorithm());
} catch (MessageEncodingException ex) {
LogUtil.error(LOG, ex, "could not encode response");
throw new OperationException(ErrorCode.SYSTEM_FAILURE, ex);
}
return ci;
} // method encodeResponse
private static void checkUserPermission(ByUserRequestorInfo requestor, String certProfile)
throws OperationException {
int permission = PermissionConstants.ENROLL_CERT;
if (!requestor.isPermitted(permission)) {
throw new OperationException(ErrorCode.NOT_PERMITTED,
PermissionConstants.getTextForCode(permission) + " is not permitted for user "
+ requestor.getCaHasUser().getUserIdent().getName());
}
if (!requestor.isCertProfilePermitted(certProfile)) {
throw new OperationException(ErrorCode.NOT_PERMITTED,
"Certificate profile " + certProfile + " is not permitted for user "
+ requestor.getCaHasUser().getUserIdent().getName());
}
}
private static String getSignatureAlgorithm(final PrivateKey key,
final ASN1ObjectIdentifier digestOid) {
ScepHashAlgoType hashAlgo = ScepHashAlgoType.forNameOrOid(digestOid.getId());
if (hashAlgo == null) {
hashAlgo = ScepHashAlgoType.SHA256;
}
String algorithm = key.getAlgorithm();
if ("RSA".equalsIgnoreCase(algorithm)) {
return hashAlgo.getName() + "withRSA";
} else {
throw new UnsupportedOperationException(
"getSignatureAlgorithm() for non-RSA is not supported yet.");
}
} // method getSignatureAlgorithm
private static void ensureIssuedByThisCa(final X500Name thisCaX500Name,
final X500Name caX500Name) throws FailInfoException {
if (!thisCaX500Name.equals(caX500Name)) {
throw FailInfoException.BAD_CERTID;
}
}
static CMSSignedData createDegeneratedSigendData(final X509Certificate... certs)
throws CMSException, CertificateException {
CMSSignedDataGenerator cmsSignedDataGen = new CMSSignedDataGenerator();
try {
for (X509Certificate cert : certs) {
cmsSignedDataGen.addCertificate(new X509CertificateHolder(cert.getEncoded()));
}
return cmsSignedDataGen.generate(new CMSAbsentContent());
} catch (IOException ex) {
throw new CMSException("could not build CMS SignedDta");
}
}
private static byte[] getTransactionIdBytes(final String tid) {
final int n = tid.length();
if (n % 2 != 0) { // neither hex nor base64 encoded
return tid.getBytes();
}
try {
return Hex.decode(tid);
} catch (Exception ex) {
if (n % 4 == 0) {
try {
return Base64.decode(tid);
} catch (Exception ex2) {
LOG.error("could not decode (hex or base64) '{}': {}", tid, ex2.getMessage());
}
}
}
return tid.getBytes();
} // method getTransactionIdBytes
private static void audit(final AuditEvent audit, final String name, final String value) {
audit.addEventData(name, (value == null) ? "null" : value);
} // method audit
private void refreshCa() throws OperationException {
try {
X509Ca ca = caManager.getX509Ca(caIdent);
X509Cert currentCaCert = ca.getCaInfo().getCertificate();
if (currentCaCert.equals(caCert)) {
return;
}
caCert = currentCaCert;
caCertRespBytes = new CaCertRespBytes(currentCaCert.getCert(), responderCert);
} catch (CaMgmtException | CertificateException | CMSException ex) {
throw new OperationException(ErrorCode.SYSTEM_FAILURE, ex.getMessage());
}
}
}