/*
* This file is part of rockframework.
*
* rockframework is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* rockframework 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>;.
*/
package br.net.woodstock.rockframework.security.cert.impl;
import java.io.Serializable;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import br.net.woodstock.rockframework.core.RockFrameworkLogger;
import br.net.woodstock.rockframework.core.RockFrameworkVersion;
import br.net.woodstock.rockframework.core.util.Assert;
import br.net.woodstock.rockframework.core.utils.Collections;
import br.net.woodstock.rockframework.core.utils.Conditions;
import br.net.woodstock.rockframework.security.cert.CertificateException;
import br.net.woodstock.rockframework.security.cert.CertificateValidator;
import br.net.woodstock.rockframework.security.cert.ValidationError;
import br.net.woodstock.rockframework.security.util.BouncyCastleProviderHelper;
public class HierarchyCertificateValidator implements CertificateValidator, Serializable {
private static final long serialVersionUID = RockFrameworkVersion.VERSION;
public static final String VALIDATOR_NAME = "Hierarchy Validator";
private Certificate[] certificates;
public HierarchyCertificateValidator() {
super();
}
public HierarchyCertificateValidator(final Certificate certificate) {
super();
Assert.notNull(certificate, "certificate");
this.certificates = new Certificate[] { certificate };
}
public HierarchyCertificateValidator(final Certificate[] certificates) {
super();
Assert.notEmpty(certificates, "certificates");
this.certificates = certificates;
}
@Override
public ValidationError[] validate(final Certificate[] chain) {
Assert.notEmpty(chain, "chain");
if (chain.length < 2) {
return new ValidationError[] { new ValidationError(HierarchyCertificateValidator.VALIDATOR_NAME, "Certificate chain must be greater than 1(certificate and issuer certificate") };
}
List<ValidationError> list = new ArrayList<ValidationError>();
for (int i = 0; i < (chain.length - 1); i++) {
X509Certificate certificate = (X509Certificate) chain[i];
X509Certificate issuer = (X509Certificate) chain[i + 1];
try {
certificate.verify(issuer.getPublicKey());
int pathLen = issuer.getBasicConstraints();
if (pathLen == -1) {
list.add(new ValidationError(HierarchyCertificateValidator.VALIDATOR_NAME, "Certificate issuer '" + BouncyCastleProviderHelper.toString(issuer.getSubjectX500Principal()) + "' is not a CA"));
}
boolean[] keyUsage = issuer.getKeyUsage();
boolean keyCertSign = keyUsage[5];
if (!keyCertSign) {
list.add(new ValidationError(HierarchyCertificateValidator.VALIDATOR_NAME, "Certificate issuer '" + BouncyCastleProviderHelper.toString(issuer.getSubjectX500Principal()) + "' dont have 'keyCertSign' in key usage"));
}
} catch (SignatureException e) {
list.add(new ValidationError(HierarchyCertificateValidator.VALIDATOR_NAME, "Certificate '" + BouncyCastleProviderHelper.toString(certificate.getSubjectX500Principal()) + "' not signed by '" + BouncyCastleProviderHelper.toString(issuer.getSubjectX500Principal()) + "'"));
} catch (Exception e) {
throw new CertificateException(e);
}
}
try {
if (Conditions.isNotEmpty(this.certificates)) {
boolean requiredOk = false;
outer: for (int i = 1; i < chain.length; i++) {
X509Certificate issuer = (X509Certificate) chain[i];
for (Certificate required : this.certificates) {
X509Certificate x509Required = (X509Certificate) required;
if (Arrays.equals(issuer.getEncoded(), x509Required.getEncoded())) {
RockFrameworkLogger.getLogger().info("Matches " + BouncyCastleProviderHelper.toString(x509Required.getSubjectX500Principal()));
requiredOk = true;
break outer;
}
}
}
if (!requiredOk) {
list.add(new ValidationError(HierarchyCertificateValidator.VALIDATOR_NAME, "Certificate chain is invalid, a required certificate could not be found"));
}
}
} catch (CertificateEncodingException e) {
throw new CertificateException(e);
}
return Collections.toArray(list, ValidationError.class);
}
}