/** * 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()); } } }