/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare, 2006-2014.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.italiangrid.voms.test.ac;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.aaCertNotFound;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.canlError;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.invalidAcCert;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.lscDescriptionDoesntMatchAcCert;
import static org.italiangrid.voms.error.VOMSValidationErrorMessage.newErrorMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.operator.OperatorCreationException;
import org.italiangrid.voms.VOMSAttribute;
import org.italiangrid.voms.VOMSError;
import org.italiangrid.voms.VOMSGenericAttribute;
import org.italiangrid.voms.VOMSValidators;
import org.italiangrid.voms.ac.VOMSACValidator;
import org.italiangrid.voms.ac.VOMSValidationResult;
import org.italiangrid.voms.ac.ValidationResultListener;
import org.italiangrid.voms.ac.impl.VOMSGenericAttributeImpl;
import org.italiangrid.voms.asn1.VOMSACGenerator;
import org.italiangrid.voms.asn1.VOMSACUtils;
import org.italiangrid.voms.error.VOMSValidationErrorMessage;
import org.italiangrid.voms.store.VOMSTrustStore;
import org.italiangrid.voms.store.impl.DefaultVOMSTrustStore;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import eu.emi.security.authn.x509.impl.OpensslCertChainValidator;
import eu.emi.security.authn.x509.impl.PEMCredential;
import eu.emi.security.authn.x509.proxy.ProxyCertificate;
import eu.emi.security.authn.x509.proxy.ProxyCertificateOptions;
import eu.emi.security.authn.x509.proxy.ProxyGenerator;
public class TestACGeneration {
static final String keyPassword = "pass";
static final String aaCert = "src/test/resources/certs/test_host_cnaf_infn_it.cert.pem";
static final String aaKey = "src/test/resources/certs/test_host_cnaf_infn_it.key.pem";
static final String aaCert2 = "src/test/resources/certs/wilco_cnaf_infn_it.cert.pem";
static final String aaKey2 = "src/test/resources/certs/wilco_cnaf_infn_it.key.pem";
static final String expiredCert = "src/test/resources/certs/expired.cert.pem";
static final String expiredKey = "src/test/resources/certs/expired.key.pem";
static final String revokedCert = "src/test/resources/certs/revoked.cert.pem";
static final String revokedKey = "src/test/resources/certs/revoked.key.pem";
static final String holderCert = "src/test/resources/certs/test0.cert.pem";
static final String holderKey = "src/test/resources/certs/test0.key.pem";
static final String defaultVO = "test.vo";
static final String defaultHost = "test-host.cnaf.infn.it";
static final int port = 15000;
static final String vomsdir = "src/test/resources/vomsdir";
static final String trustAnchorsDir = "src/test/resources/trust-anchors";
static final List<String> defaultFQANs = Arrays.asList("/test.vo",
"/test.vo/G1", "/test.vo/G2");
final List<VOMSGenericAttribute> defaultGAs = Arrays.asList(
buildGA("test", "value", defaultVO), buildGA("test2", "value", defaultVO));
static PEMCredential aaCredential = null;
static PEMCredential aaCredential2 = null;
static PEMCredential expiredCredential = null;
static PEMCredential revokedCredential = null;
static PEMCredential holderCredential = null;
static VOMSTrustStore trustStore;
static OpensslCertChainValidator certValidator = null;
static VOMSValidationErrorMessage expiredCertErrorMessage;
static VOMSValidationErrorMessage expiredCertCRLErrorMessage;
static VOMSValidationErrorMessage revokedCertErrorMessage;
static VOMSACGenerator defaultGenerator;
@BeforeClass
static public void classTestSetup() throws KeyStoreException,
CertificateException, FileNotFoundException, IOException {
aaCredential = new PEMCredential(new FileInputStream(aaKey),
new FileInputStream(aaCert), (char[]) null);
aaCredential2 = new PEMCredential(new FileInputStream(aaKey2),
new FileInputStream(aaCert2), (char[]) null);
expiredCredential = new PEMCredential(new FileInputStream(expiredKey),
new FileInputStream(expiredCert), keyPassword.toCharArray());
revokedCredential = new PEMCredential(new FileInputStream(revokedKey),
new FileInputStream(revokedCert), keyPassword.toCharArray());
holderCredential = new PEMCredential(new FileInputStream(holderKey),
new FileInputStream(holderCert), keyPassword.toCharArray());
trustStore = new DefaultVOMSTrustStore(Arrays.asList(vomsdir));
certValidator = new OpensslCertChainValidator(trustAnchorsDir);
final String expirationMessage = String.format(
"Certificate has expired on: %s", expiredCredential.getCertificate()
.getNotAfter());
expiredCertErrorMessage = newErrorMessage(canlError, expirationMessage);
expiredCertCRLErrorMessage = newErrorMessage(
canlError,
"CRL for an expired certificate was not resolved Cause: No CRLs found for issuer \"CN=Test CA, O=IGI, C=IT\"");
final Date revocationDate = new Date(1348673124000L);
final String revocationMessage = String.format(
"Certificate was revoked at: "
+ "%s, the reason reported is: unspecified", revocationDate);
revokedCertErrorMessage = newErrorMessage(canlError, revocationMessage);
defaultGenerator = new VOMSACGenerator(aaCredential);
}
@AfterClass
static public void classTestShutdown() {
certValidator.dispose();
}
private AttributeCertificate createAC(PEMCredential aaCredential,
List<String> fqans, List<VOMSGenericAttribute> gas, String vo, String host) {
VOMSACGenerator gen = new VOMSACGenerator(aaCredential);
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.HOUR, 12);
Date expiration = cal.getTime();
X509AttributeCertificateHolder ac = gen.generateVOMSAttributeCertificate(
fqans, gas, null, holderCredential.getCertificate(), BigInteger.ONE, now,
expiration, vo, host, port);
return ac.toASN1Structure();
}
private VOMSGenericAttribute buildGA(String name, String value, String context) {
VOMSGenericAttributeImpl ga = new VOMSGenericAttributeImpl();
ga.setName(name);
ga.setValue(value);
ga.setContext(context);
return ga;
}
@Test
public void testGeneratedACParsing() throws KeyStoreException,
CertificateException, FileNotFoundException, IOException,
OperatorCreationException {
AttributeCertificate ac = createAC(aaCredential, defaultFQANs, defaultGAs,
defaultVO, defaultHost);
VOMSAttribute attrs = VOMSACUtils.deserializeVOMSAttributes(ac);
// Check holder
assertEquals(holderCredential.getCertificate().getSubjectX500Principal(),
attrs.getHolder());
// Check holder serial number
assertEquals(holderCredential.getCertificate().getSerialNumber(),
attrs.getHolderSerialNumber());
// Check issuer
assertEquals(aaCredential.getCertificate().getSubjectX500Principal(),
attrs.getIssuer());
// Check policyAuthority
assertEquals(defaultVO, attrs.getVO());
assertEquals(defaultHost, attrs.getHost());
assertEquals(port, attrs.getPort());
// Check FQANs ordered equality
for (int i = 0; i < defaultFQANs.size(); i++)
assertEquals(defaultFQANs.get(i), attrs.getFQANs().get(i));
// Check GAs ordered equality
for (int i = 0; i < defaultGAs.size(); i++)
assertEquals(defaultGAs.get(i), attrs.getGenericAttributes().get(i));
// Check targets
assertTrue(attrs.getTargets().isEmpty());
}
@Test
public void testACValidation() {
ValidationResultChecker c = new ValidationResultChecker(true);
VOMSACValidator validator = VOMSValidators.newValidator(trustStore,
certValidator, c);
AttributeCertificate ac = createAC(aaCredential, defaultFQANs, defaultGAs,
defaultVO, defaultHost);
List<AttributeCertificate> validatedAttrs = validator.validateACs(Arrays
.asList(ac));
assertEquals(validatedAttrs.size(), 1);
}
@Test
public void testLSCValidationFailure() {
ValidationResultChecker c = new ValidationResultChecker(false,
newErrorMessage(lscDescriptionDoesntMatchAcCert),
newErrorMessage(aaCertNotFound));
VOMSACValidator validator = VOMSValidators.newValidator(trustStore,
certValidator, c);
AttributeCertificate ac = createAC(aaCredential2,
Arrays.asList("/test.vo.1"), defaultGAs, "test.vo.1",
"wilco.cnaf.infn.it");
List<AttributeCertificate> validatedAttrs = validator.validateACs(Arrays
.asList(ac));
assertEquals(validatedAttrs.size(), 0);
}
@Test
public void testExpiredAACertValidationFailure()
throws OperatorCreationException {
ValidationResultChecker c = new ValidationResultChecker(false,
expiredCertErrorMessage, expiredCertCRLErrorMessage,
newErrorMessage(invalidAcCert), newErrorMessage(aaCertNotFound));
VOMSACValidator validator = VOMSValidators.newValidator(trustStore,
certValidator, c);
AttributeCertificate ac = createAC(expiredCredential,
Arrays.asList("/test.vo.1"), defaultGAs, defaultVO,
"test-expired.cnaf.infn.it");
List<AttributeCertificate> validatedAttrs = validator.validateACs(Arrays
.asList(ac));
assertEquals(validatedAttrs.size(), 0);
}
@Test
public void testRevokedAACertValidationFailure() {
ValidationResultChecker c = new ValidationResultChecker(false,
revokedCertErrorMessage, newErrorMessage(invalidAcCert),
newErrorMessage(aaCertNotFound));
VOMSACValidator validator = VOMSValidators.newValidator(trustStore,
certValidator, c);
AttributeCertificate ac = createAC(revokedCredential,
Arrays.asList("/test.vo.1"), defaultGAs, defaultVO,
"test-revoked.cnaf.infn.it");
List<AttributeCertificate> validatedAttrs = validator.validateACs(Arrays
.asList(ac));
assertEquals(validatedAttrs.size(), 0);
}
@Test
public void testSuccesfullACExtractionFromProxy() {
ValidationResultChecker c = new ValidationResultChecker(true);
VOMSACValidator validator = VOMSValidators.newValidator(trustStore,
certValidator, c);
AttributeCertificate ac = createAC(aaCredential, defaultFQANs, defaultGAs,
defaultVO, defaultHost);
X509Certificate[] chain;
try {
chain = createVOMSProxy(holderCredential,
new AttributeCertificate[] { ac });
} catch (Exception e) {
throw new VOMSError("Error generating VOMS proxy:" + e.getMessage(), e);
}
List<VOMSAttribute> attrs = validator.validate(chain);
assertEquals(1, attrs.size());
}
private X509Certificate[] createVOMSProxy(PEMCredential holder,
AttributeCertificate[] acs) throws InvalidKeyException,
CertificateParsingException, SignatureException, NoSuchAlgorithmException,
IOException {
ProxyCertificateOptions proxyOptions = new ProxyCertificateOptions(
holder.getCertificateChain());
proxyOptions.setAttributeCertificates(acs);
ProxyCertificate proxy = ProxyGenerator.generate(proxyOptions,
holder.getKey());
return proxy.getCertificateChain();
}
}
class ValidationResultChecker implements ValidationResultListener {
final List<VOMSValidationErrorMessage> expectedErrorMessages;
boolean expectedValidationResult;
public ValidationResultChecker(boolean valid,
VOMSValidationErrorMessage... expectedMessages) {
expectedValidationResult = valid;
expectedErrorMessages = Arrays.asList(expectedMessages);
}
private String errorMessage(String message, VOMSValidationResult result) {
return String.format("%s. VOMSValidationResult: <%s>", message, result);
}
public void notifyValidationResult(VOMSValidationResult result) {
assertEquals(
errorMessage("ValidationResult validity check failed.", result),
expectedValidationResult, result.isValid());
assertEquals(errorMessage("ValidationResult error message size check "
+ "failed.", result), expectedErrorMessages.size(), result
.getValidationErrors().size());
List<VOMSValidationErrorMessage> errorMessages = new ArrayList<VOMSValidationErrorMessage>(
result.getValidationErrors());
for (VOMSValidationErrorMessage expectedMessage : expectedErrorMessages) {
String failureMessage = errorMessage(String.format(
"<%s> was not found in error messages. Error messages: <%s>",
expectedMessage, result.getValidationErrors()), result);
assertTrue(failureMessage,
result.getValidationErrors().contains(expectedMessage));
}
if (errorMessages.size() > 0) {
errorMessages.removeAll(expectedErrorMessages);
assertTrue(errorMessage("ValidationResult check failed. "
+ "Got more error messages than expected.", result),
errorMessages.isEmpty());
}
}
}