use of org.xipki.ocsp.api.OcspStore in project xipki by xipki.
the class OcspServerImpl method init0.
private void init0() throws InvalidConfException, DataAccessException, PasswordResolverException {
if (confFile == null) {
throw new IllegalStateException("confFile is not set");
}
if (datasourceFactory == null) {
throw new IllegalStateException("datasourceFactory is not set");
}
if (securityFactory == null) {
throw new IllegalStateException("securityFactory is not set");
}
OCSPServer conf = parseConf(confFile);
// -- check the duplication names
Set<String> set = new HashSet<>();
// Duplication name check: responder
for (ResponderType m : conf.getResponders().getResponder()) {
String name = m.getName();
if (set.contains(name)) {
throw new InvalidConfException("duplicated definition of responder named '" + name + "'");
}
if (StringUtil.isBlank(name)) {
throw new InvalidConfException("responder name must not be empty");
}
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
if (!((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '-') || ch == '_' || ch == '.') {
throw new InvalidConfException("invalid OCSP responder name '" + name + "'");
}
}
// end for
set.add(name);
}
// end for
// Duplication name check: signer
set.clear();
for (SignerType m : conf.getSigners().getSigner()) {
String name = m.getName();
if (set.contains(name)) {
throw new InvalidConfException("duplicated definition of signer option named '" + name + "'");
}
set.add(name);
}
// Duplication name check: requests
set.clear();
for (RequestOptionType m : conf.getRequestOptions().getRequestOption()) {
String name = m.getName();
if (set.contains(name)) {
throw new InvalidConfException("duplicated definition of request option named '" + name + "'");
}
set.add(name);
}
// Duplication name check: response
set.clear();
for (ResponseOptionType m : conf.getResponseOptions().getResponseOption()) {
String name = m.getName();
if (set.contains(name)) {
throw new InvalidConfException("duplicated definition of response option named '" + name + "'");
}
set.add(name);
}
// Duplication name check: store
set.clear();
for (StoreType m : conf.getStores().getStore()) {
String name = m.getName();
if (set.contains(name)) {
throw new InvalidConfException("duplicated definition of store named '" + name + "'");
}
}
// Duplication name check: datasource
set.clear();
if (conf.getDatasources() != null) {
for (DatasourceType m : conf.getDatasources().getDatasource()) {
String name = m.getName();
if (set.contains(name)) {
throw new InvalidConfException("duplicated definition of datasource named '" + name + "'");
}
set.add(name);
}
}
this.master = conf.isMaster();
// Response Cache
ResponseCacheType cacheType = conf.getResponseCache();
if (cacheType != null) {
DatasourceType cacheSourceConf = cacheType.getDatasource();
DataSourceWrapper datasource;
InputStream dsStream = null;
try {
dsStream = getInputStream(cacheSourceConf.getConf());
datasource = datasourceFactory.createDataSource(cacheSourceConf.getName(), dsStream, securityFactory.getPasswordResolver());
} catch (IOException ex) {
throw new InvalidConfException(ex.getMessage(), ex);
} finally {
close(dsStream);
}
responseCacher = new ResponseCacher(datasource, master, cacheType.getValidity());
responseCacher.init();
}
// signers
for (SignerType m : conf.getSigners().getSigner()) {
ResponderSigner signer = initSigner(m);
signers.put(m.getName(), signer);
}
// requests
for (RequestOptionType m : conf.getRequestOptions().getRequestOption()) {
RequestOption option = new RequestOption(m);
requestOptions.put(m.getName(), option);
}
// responses
for (ResponseOptionType m : conf.getResponseOptions().getResponseOption()) {
ResponseOption option = new ResponseOption(m);
responseOptions.put(m.getName(), option);
}
// datasources
Map<String, DataSourceWrapper> datasources = new HashMap<>();
if (conf.getDatasources() != null) {
for (DatasourceType m : conf.getDatasources().getDatasource()) {
String name = m.getName();
DataSourceWrapper datasource;
InputStream dsStream = null;
try {
dsStream = getInputStream(m.getConf());
datasource = datasourceFactory.createDataSource(name, dsStream, securityFactory.getPasswordResolver());
} catch (IOException ex) {
throw new InvalidConfException(ex.getMessage(), ex);
} finally {
close(dsStream);
}
datasources.put(name, datasource);
}
// end for
}
// end if
// responders
Map<String, ResponderOption> responderOptions = new HashMap<>();
for (ResponderType m : conf.getResponders().getResponder()) {
ResponderOption option = new ResponderOption(m);
String optName = option.getSignerName();
if (!signers.containsKey(optName)) {
throw new InvalidConfException("no signer named '" + optName + "' is defined");
}
String reqOptName = option.getRequestOptionName();
if (!requestOptions.containsKey(reqOptName)) {
throw new InvalidConfException("no requestOption named '" + reqOptName + "' is defined");
}
String respOptName = option.getResponseOptionName();
if (!responseOptions.containsKey(respOptName)) {
throw new InvalidConfException("no responseOption named '" + respOptName + "' is defined");
}
// required HashAlgorithms for certificate
List<StoreType> storeDefs = conf.getStores().getStore();
Set<String> storeNames = new HashSet<>(storeDefs.size());
for (StoreType storeDef : storeDefs) {
storeNames.add(storeDef.getName());
}
responderOptions.put(m.getName(), option);
}
// stores
for (StoreType m : conf.getStores().getStore()) {
OcspStore store = newStore(m, datasources);
stores.put(m.getName(), store);
}
// responders
for (String name : responderOptions.keySet()) {
ResponderOption option = responderOptions.get(name);
List<OcspStore> statusStores = new ArrayList<>(option.getStoreNames().size());
for (String storeName : option.getStoreNames()) {
statusStores.add(stores.get(storeName));
}
ResponseOption responseOption = responseOptions.get(option.getResponseOptionName());
ResponderSigner signer = signers.get(option.getSignerName());
if (signer.isMacSigner()) {
if (responseOption.isResponderIdByName()) {
throw new InvalidConfException("could not use ResponderIdByName for signer " + option.getSignerName());
}
if (EmbedCertsMode.NONE != responseOption.getEmbedCertsMode()) {
throw new InvalidConfException("could not embed certifcate in response for signer " + option.getSignerName());
}
}
ResponderImpl responder = new ResponderImpl(option, requestOptions.get(option.getRequestOptionName()), responseOption, signer, statusStores);
responders.put(name, responder);
}
// end for
// servlet paths
List<SizeComparableString> tmpList = new LinkedList<>();
for (String name : responderOptions.keySet()) {
ResponderImpl responder = responders.get(name);
ResponderOption option = responderOptions.get(name);
List<String> strs = option.getServletPaths();
for (String path : strs) {
tmpList.add(new SizeComparableString(path));
path2responderMap.put(path, responder);
}
}
// Sort the servlet paths according to the length of path. The first one is the
// longest, and the last one is the shortest.
Collections.sort(tmpList);
List<String> list2 = new ArrayList<>(tmpList.size());
for (SizeComparableString m : tmpList) {
list2.add(m.str);
}
this.servletPaths = list2;
}
use of org.xipki.ocsp.api.OcspStore in project xipki by xipki.
the class OcspServerImpl method healthCheck.
@Override
public HealthCheckResult healthCheck(Responder responder2) {
ResponderImpl responder = (ResponderImpl) responder2;
HealthCheckResult result = new HealthCheckResult("OCSPResponder");
boolean healthy = true;
for (OcspStore store : responder.getStores()) {
boolean storeHealthy = store.isHealthy();
healthy &= storeHealthy;
HealthCheckResult storeHealth = new HealthCheckResult("CertStatusStore." + store.getName());
storeHealth.setHealthy(storeHealthy);
result.addChildCheck(storeHealth);
}
boolean signerHealthy = responder.getSigner().isHealthy();
healthy &= signerHealthy;
HealthCheckResult signerHealth = new HealthCheckResult("Signer");
signerHealth.setHealthy(signerHealthy);
result.addChildCheck(signerHealth);
result.setHealthy(healthy);
return result;
}
use of org.xipki.ocsp.api.OcspStore in project xipki by xipki.
the class OcspServerImpl method answer.
@Override
public OcspRespWithCacheInfo answer(Responder responder2, byte[] request, boolean viaGet) {
ResponderImpl responder = (ResponderImpl) responder2;
RequestOption reqOpt = responder.getRequestOption();
int version;
try {
version = OcspRequest.readRequestVersion(request);
} catch (EncodingException ex) {
String message = "could not extract version from request";
LOG.warn(message);
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
if (!reqOpt.isVersionAllowed(version)) {
String message = "invalid request version " + version;
LOG.warn(message);
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
ResponderSigner signer = responder.getSigner();
ResponseOption repOpt = responder.getResponseOption();
try {
Object reqOrRrrorResp = checkSignature(request, reqOpt);
if (reqOrRrrorResp instanceof OcspRespWithCacheInfo) {
return (OcspRespWithCacheInfo) reqOrRrrorResp;
}
OcspRequest req = (OcspRequest) reqOrRrrorResp;
List<CertID> requestList = req.getRequestList();
int requestsSize = requestList.size();
if (requestsSize > reqOpt.getMaxRequestListCount()) {
String message = requestsSize + " entries in RequestList, but maximal " + reqOpt.getMaxRequestListCount() + " is allowed";
LOG.warn(message);
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
OcspRespControl repControl = new OcspRespControl();
repControl.canCacheInfo = true;
List<ExtendedExtension> reqExtensions = req.getExtensions();
List<Extension> respExtensions = new LinkedList<>();
ExtendedExtension nonceExtn = removeExtension(reqExtensions, OID.ID_PKIX_OCSP_NONCE);
if (nonceExtn != null) {
if (reqOpt.getNonceOccurrence() == TripleState.FORBIDDEN) {
LOG.warn("nonce forbidden, but is present in the request");
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
int len = nonceExtn.getExtnValueLength();
int min = reqOpt.getNonceMinLen();
int max = reqOpt.getNonceMaxLen();
if (len < min || len > max) {
LOG.warn("length of nonce {} not within [{},{}]", len, min, max);
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
repControl.canCacheInfo = false;
respExtensions.add(nonceExtn);
} else {
if (reqOpt.getNonceOccurrence() == TripleState.REQUIRED) {
LOG.warn("nonce required, but is not present in the request");
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
}
ConcurrentContentSigner concurrentSigner = null;
if (responder.getResponderOption().getMode() != OcspMode.RFC2560) {
ExtendedExtension extn = removeExtension(reqExtensions, OID.ID_PKIX_OCSP_PREFSIGALGS);
if (extn != null) {
ASN1InputStream asn1Stream = new ASN1InputStream(extn.getExtnValueStream());
List<AlgorithmIdentifier> prefSigAlgs;
try {
ASN1Sequence seq = ASN1Sequence.getInstance(asn1Stream.readObject());
final int size = seq.size();
prefSigAlgs = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
prefSigAlgs.add(AlgorithmIdentifier.getInstance(seq.getObjectAt(i)));
}
} finally {
asn1Stream.close();
}
concurrentSigner = signer.getSignerForPreferredSigAlgs(prefSigAlgs);
}
}
if (!reqExtensions.isEmpty()) {
boolean flag = false;
for (ExtendedExtension m : reqExtensions) {
if (m.isCritical()) {
flag = true;
break;
}
}
if (flag) {
if (LOG.isWarnEnabled()) {
List<OID> oids = new LinkedList<>();
for (ExtendedExtension m : reqExtensions) {
if (m.isCritical()) {
oids.add(m.getExtnType());
}
}
LOG.warn("could not process critial request extensions: {}", oids);
}
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
}
if (concurrentSigner == null) {
concurrentSigner = signer.getFirstSigner();
}
AlgorithmCode cacheDbSigAlgCode = null;
BigInteger cacheDbSerialNumber = null;
Integer cacheDbIssuerId = null;
boolean canCacheDb = (requestsSize == 1) && (responseCacher != null) && (nonceExtn == null) && responseCacher.isOnService();
if (canCacheDb) {
// try to find the cached response
CertID certId = requestList.get(0);
HashAlgo reqHashAlgo = certId.getIssuer().hashAlgorithm();
if (!reqOpt.allows(reqHashAlgo)) {
LOG.warn("CertID.hashAlgorithm {} not allowed", reqHashAlgo != null ? reqHashAlgo : certId.getIssuer().hashAlgorithmOID());
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
cacheDbSigAlgCode = concurrentSigner.getAlgorithmCode();
cacheDbIssuerId = responseCacher.getIssuerId(certId.getIssuer());
cacheDbSerialNumber = certId.getSerialNumber();
if (cacheDbIssuerId != null) {
OcspRespWithCacheInfo cachedResp = responseCacher.getOcspResponse(cacheDbIssuerId.intValue(), cacheDbSerialNumber, cacheDbSigAlgCode);
if (cachedResp != null) {
return cachedResp;
}
} else if (master) {
// store the issuer certificate in cache database.
X509Certificate issuerCert = null;
for (OcspStore store : responder.getStores()) {
issuerCert = store.getIssuerCert(certId.getIssuer());
if (issuerCert != null) {
break;
}
}
if (issuerCert != null) {
cacheDbIssuerId = responseCacher.storeIssuer(issuerCert);
}
}
if (cacheDbIssuerId == null) {
canCacheDb = false;
}
}
ResponderID responderId = signer.getResponderId(repOpt.isResponderIdByName());
OCSPRespBuilder builder = new OCSPRespBuilder(responderId);
for (int i = 0; i < requestsSize; i++) {
OcspRespWithCacheInfo failureOcspResp = processCertReq(requestList.get(i), builder, responder, reqOpt, repOpt, repControl);
if (failureOcspResp != null) {
return failureOcspResp;
}
}
if (repControl.includeExtendedRevokeExtension) {
respExtensions.add(extension_pkix_ocsp_extendedRevoke);
}
if (!respExtensions.isEmpty()) {
Extensions extns = new Extensions(respExtensions);
builder.setResponseExtensions(extns);
}
TaggedCertSequence certsInResp;
EmbedCertsMode certsMode = repOpt.getEmbedCertsMode();
if (certsMode == EmbedCertsMode.SIGNER) {
certsInResp = signer.getSequenceOfCert();
} else if (certsMode == EmbedCertsMode.NONE) {
certsInResp = null;
} else {
// certsMode == EmbedCertsMode.SIGNER_AND_CA
certsInResp = signer.getSequenceOfCertChain();
}
byte[] encodeOcspResponse;
try {
encodeOcspResponse = builder.buildOCSPResponse(concurrentSigner, certsInResp, new Date());
} catch (NoIdleSignerException ex) {
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.tryLater);
} catch (OCSPException ex) {
LogUtil.error(LOG, ex, "answer() basicOcspBuilder.build");
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.internalError);
}
// cache response in database
if (canCacheDb && repControl.canCacheInfo) {
// Don't cache the response with status UNKNOWN, since this may result in DDoS
// of storage
responseCacher.storeOcspResponse(cacheDbIssuerId.intValue(), cacheDbSerialNumber, repControl.cacheThisUpdate, repControl.cacheNextUpdate, cacheDbSigAlgCode, encodeOcspResponse);
}
if (viaGet && repControl.canCacheInfo) {
ResponseCacheInfo cacheInfo = new ResponseCacheInfo(repControl.cacheThisUpdate);
if (repControl.cacheNextUpdate != Long.MAX_VALUE) {
cacheInfo.setNextUpdate(repControl.cacheNextUpdate);
}
return new OcspRespWithCacheInfo(encodeOcspResponse, cacheInfo);
} else {
return new OcspRespWithCacheInfo(encodeOcspResponse, null);
}
} catch (Throwable th) {
LogUtil.error(LOG, th);
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.internalError);
}
}
use of org.xipki.ocsp.api.OcspStore in project xipki by xipki.
the class OcspServerImpl method processCertReq.
// method ask
private OcspRespWithCacheInfo processCertReq(CertID certId, OCSPRespBuilder builder, ResponderImpl responder, RequestOption reqOpt, ResponseOption repOpt, OcspRespControl repControl) throws IOException {
HashAlgo reqHashAlgo = certId.getIssuer().hashAlgorithm();
if (!reqOpt.allows(reqHashAlgo)) {
LOG.warn("CertID.hashAlgorithm {} not allowed", reqHashAlgo);
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.malformedRequest);
}
CertStatusInfo certStatusInfo = null;
boolean exceptionOccurs = false;
BigInteger serial = certId.getSerialNumber();
Date now = new Date();
for (OcspStore store : responder.getStores()) {
try {
certStatusInfo = store.getCertStatus(now, certId.getIssuer(), serial, repOpt.isIncludeCerthash(), repOpt.isIncludeInvalidityDate(), responder.getResponderOption().isInheritCaRevocation());
if (certStatusInfo != null) {
break;
}
} catch (OcspStoreException ex) {
exceptionOccurs = true;
LogUtil.error(LOG, ex, "getCertStatus() of CertStatusStore " + store.getName());
}
}
if (certStatusInfo == null) {
if (exceptionOccurs) {
return unsuccesfulOCSPRespMap.get(OcspResponseStatus.tryLater);
} else {
certStatusInfo = CertStatusInfo.getIssuerUnknownCertStatusInfo(new Date(), null);
}
}
// end if
// certStatusInfo must not be null in any case, since at least one store is configured
Date thisUpdate = certStatusInfo.getThisUpdate();
if (thisUpdate == null) {
thisUpdate = new Date();
}
Date nextUpdate = certStatusInfo.getNextUpdate();
List<Extension> extensions = new LinkedList<>();
boolean unknownAsRevoked = false;
byte[] certStatus;
switch(certStatusInfo.getCertStatus()) {
case GOOD:
certStatus = bytes_certstatus_good;
break;
case ISSUER_UNKNOWN:
repControl.canCacheInfo = false;
certStatus = bytes_certstatus_unknown;
break;
case UNKNOWN:
case IGNORE:
repControl.canCacheInfo = false;
if (responder.getResponderOption().getMode() == OcspMode.RFC2560) {
certStatus = bytes_certstatus_unknown;
} else {
// (ocspMode == OCSPMode.RFC6960)
unknownAsRevoked = true;
certStatus = bytes_certstatus_rfc6960_unknown;
}
break;
case REVOKED:
CertRevocationInfo revInfo = certStatusInfo.getRevocationInfo();
certStatus = Template.getEncodeRevokedInfo(repOpt.isIncludeRevReason() ? revInfo.getReason() : null, revInfo.getRevocationTime());
Date invalidityDate = revInfo.getInvalidityTime();
if (repOpt.isIncludeInvalidityDate() && invalidityDate != null && !invalidityDate.equals(revInfo.getRevocationTime())) {
extensions.add(Template.getInvalidityDateExtension(invalidityDate));
}
break;
default:
throw new RuntimeException("unknown CertificateStatus:" + certStatusInfo.getCertStatus());
}
if (responder.getResponderOption().getMode() != OcspMode.RFC2560) {
repControl.includeExtendedRevokeExtension = true;
}
byte[] certHash = certStatusInfo.getCertHash();
if (certHash != null) {
extensions.add(Template.getCertHashExtension(certStatusInfo.getCertHashAlgo(), certHash));
}
if (certStatusInfo.getArchiveCutOff() != null) {
extensions.add(Template.getArchiveOffExtension(certStatusInfo.getArchiveCutOff()));
}
if (LOG.isDebugEnabled()) {
String certStatusText = null;
if (Arrays.equals(certStatus, bytes_certstatus_good)) {
certStatusText = "good";
} else if (Arrays.equals(certStatus, bytes_certstatus_unknown)) {
certStatusText = "unknown";
} else if (Arrays.equals(certStatus, bytes_certstatus_rfc6960_unknown)) {
certStatusText = "RFC6969_unknown";
} else {
certStatusText = unknownAsRevoked ? "unknown_as_revoked" : "revoked";
}
String msg = StringUtil.concatObjectsCap(250, "issuer: ", certId.getIssuer(), ", serialNumber: ", LogUtil.formatCsn(certId.getSerialNumber()), ", certStatus: ", certStatusText, ", thisUpdate: ", thisUpdate, ", nextUpdate: ", nextUpdate);
StringBuilder sb = new StringBuilder(msg.length() + 80);
sb.append(msg);
if (certHash != null) {
sb.append(", certHash: ").append(Hex.encode(certHash));
}
LOG.debug(sb.toString());
}
if (CollectionUtil.isEmpty(extensions)) {
builder.addResponse(certId, certStatus, thisUpdate, nextUpdate, null);
} else {
builder.addResponse(certId, certStatus, thisUpdate, nextUpdate, new Extensions(extensions));
}
repControl.cacheThisUpdate = Math.max(repControl.cacheThisUpdate, thisUpdate.getTime());
if (nextUpdate != null) {
repControl.cacheNextUpdate = Math.min(repControl.cacheNextUpdate, nextUpdate.getTime());
}
return null;
}
use of org.xipki.ocsp.api.OcspStore in project xipki by xipki.
the class OcspServerImpl method newStore.
// method initSigner
private OcspStore newStore(StoreType conf, Map<String, DataSourceWrapper> datasources) throws InvalidConfException {
OcspStore store;
String type = conf.getSource().getType();
if ("CRL".equalsIgnoreCase(type)) {
store = new CrlDbCertStatusStore();
} else if ("XIPKI-DB".equals(type)) {
store = new DbCertStatusStore();
} else {
try {
store = ocspStoreFactoryRegister.newOcspStore(conf.getSource().getType());
} catch (ObjectCreationException ex) {
throw new InvalidConfException("ObjectCreationException of store " + conf.getName() + ":" + ex.getMessage(), ex);
}
}
store.setName(conf.getName());
Integer interval = conf.getRetentionInterval();
int retentionInterva = (interval == null) ? -1 : interval.intValue();
store.setRetentionInterval(retentionInterva);
store.setUnknownSerialAsGood(getBoolean(conf.isUnknownSerialAsGood(), false));
store.setIncludeArchiveCutoff(getBoolean(conf.isIncludeArchiveCutoff(), true));
store.setIncludeCrlId(getBoolean(conf.isIncludeCrlID(), true));
store.setIgnoreExpiredCert(getBoolean(conf.isIgnoreExpiredCert(), true));
store.setIgnoreNotYetValidCert(getBoolean(conf.isIgnoreNotYetValidCert(), true));
String datasourceName = conf.getSource().getDatasource();
DataSourceWrapper datasource = null;
if (datasourceName != null) {
datasource = datasources.get(datasourceName);
if (datasource == null) {
throw new InvalidConfException("datasource named '" + datasourceName + "' not defined");
}
}
try {
store.init(conf.getSource().getConf(), datasource);
} catch (OcspStoreException ex) {
throw new InvalidConfException("CertStatusStoreException of store " + conf.getName() + ":" + ex.getMessage(), ex);
}
return store;
}
Aggregations