/*
*
* 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.ocsp.server.impl;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import org.xipki.commons.common.InvalidConfException;
import org.xipki.commons.common.TripleState;
import org.xipki.commons.common.util.IoUtil;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.commons.security.CertpathValidationModel;
import org.xipki.commons.security.HashAlgoType;
import org.xipki.commons.security.util.KeyUtil;
import org.xipki.commons.security.util.X509Util;
import org.xipki.pki.ocsp.server.impl.jaxb.CertCollectionType;
import org.xipki.pki.ocsp.server.impl.jaxb.CertCollectionType.Keystore;
import org.xipki.pki.ocsp.server.impl.jaxb.NonceType;
import org.xipki.pki.ocsp.server.impl.jaxb.RequestOptionType;
import org.xipki.pki.ocsp.server.impl.jaxb.RequestOptionType.CertpathValidation;
import org.xipki.pki.ocsp.server.impl.jaxb.RequestOptionType.HashAlgorithms;
import org.xipki.pki.ocsp.server.impl.jaxb.VersionsType;
/**
* @author Lijun Liao
* @since 2.0.0
*/
class RequestOption {
static final Set<HashAlgoType> SUPPORTED_HASH_ALGORITHMS = new HashSet<>();
static {
SUPPORTED_HASH_ALGORITHMS.add(HashAlgoType.SHA1);
SUPPORTED_HASH_ALGORITHMS.add(HashAlgoType.SHA224);
SUPPORTED_HASH_ALGORITHMS.add(HashAlgoType.SHA256);
SUPPORTED_HASH_ALGORITHMS.add(HashAlgoType.SHA384);
SUPPORTED_HASH_ALGORITHMS.add(HashAlgoType.SHA512);
}
private final boolean supportsHttpGet;
private final boolean signatureRequired;
private final boolean validateSignature;
private final int maxRequestListCount;
private final int maxRequestSize;
private final Collection<Integer> versions;
private final TripleState nonceOccurrence;
private final int nonceMinLen;
private final int nonceMaxLen;
private final Set<HashAlgoType> hashAlgos;
private final Set<CertWithEncoded> trustAnchors;
private final Set<X509Certificate> certs;
private final CertpathValidationModel certpathValidationModel;
RequestOption(final RequestOptionType conf) throws InvalidConfException {
ParamUtil.requireNonNull("conf", conf);
supportsHttpGet = conf.isSupportsHttpGet();
signatureRequired = conf.isSignatureRequired();
validateSignature = conf.isValidateSignature();
// Request nonce
NonceType nonceConf = conf.getNonce();
int minLen = 4;
int maxLen = 32;
String str = nonceConf.getOccurrence().toLowerCase();
if ("forbidden".equals(str)) {
nonceOccurrence = TripleState.FORBIDDEN;
} else if ("optional".equals(str)) {
nonceOccurrence = TripleState.OPTIONAL;
} else if ("required".equals(str)) {
nonceOccurrence = TripleState.REQUIRED;
} else {
throw new InvalidConfException("invalid nonce.occurrence '" + str
+ "', only forbidded, optional, and required are allowed");
}
if (nonceConf.getMinLen() != null) {
minLen = nonceConf.getMinLen();
}
if (nonceConf.getMaxLen() != null) {
maxLen = nonceConf.getMaxLen();
}
this.maxRequestListCount = conf.getMaxRequestListCount();
if (this.maxRequestListCount < 1) {
throw new InvalidConfException("invalid maxRequestListCount " + maxRequestListCount);
}
this.maxRequestSize = conf.getMaxRequestSize();
if (this.maxRequestSize < 100) {
throw new InvalidConfException("invalid maxRequestSize " + maxRequestSize);
}
this.nonceMinLen = minLen;
this.nonceMaxLen = maxLen;
// Request versions
VersionsType versionsConf = conf.getVersions();
this.versions = new HashSet<>();
for (String m : versionsConf.getVersion()) {
if ("v1".equalsIgnoreCase(m)) {
this.versions.add(0);
} else {
throw new InvalidConfException("invalid OCSP request version '" + m + "'");
}
}
// Request hash algorithms
hashAlgos = new HashSet<>();
HashAlgorithms reqHashAlgosConf = conf.getHashAlgorithms();
if (reqHashAlgosConf != null) {
for (String token : reqHashAlgosConf.getAlgorithm()) {
HashAlgoType algo = HashAlgoType.getHashAlgoType(token);
if (algo != null && SUPPORTED_HASH_ALGORITHMS.contains(algo)) {
hashAlgos.add(algo);
} else {
throw new InvalidConfException("hash algorithm " + token + " is unsupported");
}
}
} else {
hashAlgos.addAll(SUPPORTED_HASH_ALGORITHMS);
}
// certpath validation
CertpathValidation certpathConf = conf.getCertpathValidation();
if (certpathConf == null) {
if (validateSignature) {
throw new InvalidConfException("certpathValidation is not specified");
}
trustAnchors = null;
certs = null;
certpathValidationModel = CertpathValidationModel.PKIX;
return;
}
switch (certpathConf.getValidationModel()) {
case CHAIN:
certpathValidationModel = CertpathValidationModel.CHAIN;
break;
case PKIX:
certpathValidationModel = CertpathValidationModel.PKIX;
break;
default:
throw new RuntimeException("should not reach here, unknown ValidationModel "
+ certpathConf.getValidationModel());
} // end switch
try {
Set<X509Certificate> tmpCerts = doGetCerts(certpathConf.getTrustAnchors());
trustAnchors = new HashSet<>(tmpCerts.size());
for (X509Certificate m : tmpCerts) {
trustAnchors.add(new CertWithEncoded(m));
}
} catch (Exception ex) {
throw new InvalidConfException(
"could not initialize the trustAnchors: " + ex.getMessage(), ex);
}
CertCollectionType certsType = certpathConf.getCerts();
try {
this.certs = (certsType == null) ? null : doGetCerts(certsType);
} catch (Exception ex) {
throw new InvalidConfException(
"could not initialize the certs: " + ex.getMessage(), ex);
}
} // constructor
public Set<HashAlgoType> getHashAlgos() {
return hashAlgos;
}
public boolean isSignatureRequired() {
return signatureRequired;
}
public boolean isValidateSignature() {
return validateSignature;
}
public boolean supportsHttpGet() {
return supportsHttpGet;
}
public TripleState getNonceOccurrence() {
return nonceOccurrence;
}
public int getMaxRequestListCount() {
return maxRequestListCount;
}
public int getMaxRequestSize() {
return maxRequestSize;
}
public int getNonceMinLen() {
return nonceMinLen;
}
public int getNonceMaxLen() {
return nonceMaxLen;
}
public boolean allows(final HashAlgoType hashAlgo) {
return hashAlgos.contains(hashAlgo);
}
public CertpathValidationModel getCertpathValidationModel() {
return certpathValidationModel;
}
public Set<CertWithEncoded> getTrustAnchors() {
return trustAnchors;
}
public Set<X509Certificate> getCerts() {
return certs;
}
public boolean isVersionAllowed(final Integer version) {
return versions == null || versions.contains(version);
}
private static Set<X509Certificate> doGetCerts(final CertCollectionType conf)
throws KeyStoreException, NoSuchAlgorithmException, NoSuchProviderException,
CertificateException, IOException {
ParamUtil.requireNonNull("conf", conf);
Set<X509Certificate> tmpCerts = new HashSet<>();
if (conf.getKeystore() != null) {
Keystore ksConf = conf.getKeystore();
KeyStore trustStore = KeyUtil.getKeyStore(ksConf.getType());
String fileName = ksConf.getKeystore().getFile();
InputStream is = (fileName != null)
? new FileInputStream(IoUtil.expandFilepath(fileName))
: new ByteArrayInputStream(ksConf.getKeystore().getValue());
char[] password = (ksConf.getPassword() == null) ? null
: ksConf.getPassword().toCharArray();
trustStore.load(is, password);
Enumeration<String> aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (trustStore.isCertificateEntry(alias)) {
tmpCerts.add((X509Certificate) trustStore.getCertificate(alias));
}
}
} else if (conf.getDir() != null) {
File dir = new File(conf.getDir());
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.exists() && file.isFile()) {
tmpCerts.add(X509Util.parseCert(file));
}
}
}
} else {
throw new RuntimeException("should not happen, neither keystore nor dir is defined");
}
return tmpCerts;
} // method getCerts
}