All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.italiangrid.voms.ac.impl.DefaultVOMSValidationStrategy Maven / Gradle / Ivy

There is a newer version: 3.3.2
Show newest version
/**
 * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2006-2012.
 *
 * 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.ac.impl;

import static org.italiangrid.voms.error.VOMSValidationErrorCode.aaCertFailsSignatureVerification;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.aaCertNotFound;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.acCertFailsSignatureVerification;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.acHolderDoesntMatchCertChain;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.acNotValidAtCurrentTime;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.canlError;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.emptyAcCertsExtension;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.invalidAaCert;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.invalidAcCert;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.localhostDoesntMatchAcTarget;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.lscDescriptionDoesntMatchAcCert;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.lscFileNotFound;
import static org.italiangrid.voms.error.VOMSValidationErrorCode.other;
import static org.italiangrid.voms.error.VOMSValidationErrorMessage.newErrorMessage;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.bc.BcRSAContentVerifierProviderBuilder;
import org.italiangrid.voms.VOMSAttribute;
import org.italiangrid.voms.VOMSError;
import org.italiangrid.voms.ac.VOMSACValidationStrategy;
import org.italiangrid.voms.ac.VOMSValidationResult;
import org.italiangrid.voms.asn1.VOMSConstants;
import org.italiangrid.voms.error.VOMSValidationErrorMessage;
import org.italiangrid.voms.store.LSCInfo;
import org.italiangrid.voms.store.VOMSTrustStore;

import eu.emi.security.authn.x509.ValidationError;
import eu.emi.security.authn.x509.ValidationResult;
import eu.emi.security.authn.x509.X509CertChainValidatorExt;
import eu.emi.security.authn.x509.impl.X500NameUtils;
import eu.emi.security.authn.x509.proxy.ProxyUtils;

/**
 * The Default VOMS validation strategy.
 * @author andreaceccanti
 *
 */
public class DefaultVOMSValidationStrategy implements VOMSACValidationStrategy{

	private final VOMSTrustStore store;
	private final X509CertChainValidatorExt certChainValidator;
	
	
	public DefaultVOMSValidationStrategy(VOMSTrustStore store, X509CertChainValidatorExt validator) {
		this.store = store;
		this.certChainValidator = validator;
		
	}
	
	private boolean checkACHolder(VOMSAttribute attributes, X509Certificate[] chain, List validationErrors){
		
		X500Principal chainHolder = ProxyUtils.getOriginalUserDN(chain);
		
		boolean holderDoesMatch = chainHolder.equals(attributes.getHolder());
		
		if (!holderDoesMatch){
			
			String acHolderSubject = X500NameUtils.getReadableForm(attributes.getHolder());
			String certChainSubject =X500NameUtils.getReadableForm(chainHolder);
			
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(acHolderDoesntMatchCertChain,
					acHolderSubject,
					certChainSubject));
		}
		
		return holderDoesMatch;
	}
	
	private boolean checkACValidity(VOMSAttribute attributes, List validationErrors){
		Date now = new Date();
		
		boolean valid = attributes.validAt(now); 
		
		if (!valid){
			VOMSValidationErrorMessage m = VOMSValidationErrorMessage.newErrorMessage(acNotValidAtCurrentTime,
					attributes.getNotBefore(),
					attributes.getNotAfter(),
					now);
			
			validationErrors.add(m);
		}
		
		return valid;
	}
	
	
	private boolean checkLocalAACertSignature(VOMSAttribute attributes, List validationErrors){
		
		X509Certificate localAACert = store.getAACertificateBySubject(attributes.getIssuer());
		if (localAACert == null){
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(aaCertNotFound));
			return false;
		}
		
		if (!validateCertificate(localAACert, validationErrors)){
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(invalidAaCert));
			return false;
		}
		
		boolean signatureValid = verifyACSignature(attributes, localAACert);
		
		if (!signatureValid){
			String readableSubject = X500NameUtils.getReadableForm(localAACert.getSubjectX500Principal());
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(aaCertFailsSignatureVerification, readableSubject));
		}
		
		return signatureValid;
			
	}
	
	private boolean checkLSCSignature(VOMSAttribute attributes, List validationErrors){
		
		LSCInfo lsc = store.getLSC(attributes.getVO(), attributes.getHost());
		X509Certificate[] aaCerts = attributes.getAACertificates();
		
		if (lsc == null){
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(lscFileNotFound));
			return false;
		}
		
		if (aaCerts == null || aaCerts.length == 0){
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(emptyAcCertsExtension));
			return false;
		}
		
		if (!lsc.matches(aaCerts)){
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(lscDescriptionDoesntMatchAcCert));
			return false;
		}
		
		// LSC matches aa certs, verify certificates extracted from the AC
		if (!validateCertificateChain(aaCerts, validationErrors)){
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(invalidAcCert));
			return false;
		}
		
		boolean signatureValid = verifyACSignature(attributes, aaCerts[0]);
		
		if (!signatureValid){
			String readableSubject = X500NameUtils.getReadableForm(aaCerts[0].getSubjectX500Principal());
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(acCertFailsSignatureVerification, readableSubject));
		}
			
		return signatureValid; 
	}
	
	private boolean checkSignature(VOMSAttribute attributes, List validationErrors){
		
		boolean valid = checkLSCSignature(attributes, validationErrors);
		
		if (!valid)
			valid = checkLocalAACertSignature(attributes, validationErrors);
		
		return valid;

	}
	
	private boolean checkTargets(VOMSAttribute attributes, List validationErrors){
		
		if (attributes.getTargets() == null || attributes.getTargets().size() == 0)
			return true;
		
		String localhostName;
		
		try {
			localhostName = InetAddress.getLocalHost().getCanonicalHostName();
		
		} catch (UnknownHostException e) {
			validationErrors.add(newErrorMessage(other, "Error resolving localhost name: "+e.getMessage()));
			return false;
		}
		
		if (!attributes.getTargets().contains(localhostName)){
			validationErrors.add(newErrorMessage(localhostDoesntMatchAcTarget, localhostName, attributes.getTargets().toString()));
			return false;
		}
		
		return true;
	}
	
	private boolean checkNoRevAvailExtension(VOMSAttribute attributes, List validationErrors){
		
		X509Extension noRevAvail = attributes.getVOMSAC().getExtension(X509Extension.noRevAvail);
		if (noRevAvail != null && noRevAvail.isCritical()){
			validationErrors.add(newErrorMessage(other, "NoRevAvail AC extension cannot be critical!"));
			return false;
		}
		return true;
	}
	
	private boolean checkAuthorityKeyIdentifierExtension(VOMSAttribute attributes, List validationErrors){
		
		X509Extension authKeyId = attributes.getVOMSAC().getExtension(X509Extension.authorityKeyIdentifier);
		if (authKeyId != null && authKeyId.isCritical()){
			validationErrors.add(newErrorMessage(other, "AuthorityKeyIdentifier AC extension cannot be critical!"));
			return false;
		}
		return true;
	}
	
	private boolean checkUnhandledCriticalExtensions(VOMSAttribute attributes, List validationErrors){
		
		@SuppressWarnings("unchecked")
		List acExtensions = attributes.getVOMSAC().getExtensionOIDs();
		
		for (ASN1ObjectIdentifier extId: acExtensions){
			if (!VOMSConstants.VOMS_HANDLED_EXTENSIONS.contains(extId) && 
					attributes.getVOMSAC().getExtension(extId).isCritical()){
				validationErrors.add(newErrorMessage(other, "unknown critical extension found in VOMS AC: "+extId.getId()));
				return false;
			}
		}
		return true;
	}

	public VOMSValidationResult validateAC(VOMSAttribute attributes) {
		boolean valid = true;
		List validationErrors = new ArrayList();

		// Check temporal validity
		valid = checkACValidity(attributes, validationErrors);

		if (valid)
			// Verify signature on AC checking LSC file or local AA certificate
			valid = checkSignature(attributes, validationErrors);
		
		if (valid)
			// Check targets
			valid = checkTargets(attributes, validationErrors);
		
		// AC extension checking to be compliant with rfc 3281
		if (valid)
			valid = checkAuthorityKeyIdentifierExtension(attributes, validationErrors);
				
		if (valid)
			valid = checkNoRevAvailExtension(attributes, validationErrors);
				
		if (valid)
			valid = checkUnhandledCriticalExtensions(attributes, validationErrors);
		
		return new VOMSValidationResult(attributes,valid, validationErrors);
	}
	
	public synchronized VOMSValidationResult validateAC(VOMSAttribute attributes, X509Certificate[] chain) {
		
		boolean valid = true;
		List validationErrors = new ArrayList();
		
		// Check temporal validity
		valid = checkACValidity(attributes, validationErrors);
		
		if (valid)
			// Verify signature on AC checking LSC file or local AA certificate
			valid = checkSignature(attributes, validationErrors);
		
		if (valid)
			// Check AC holder
			valid = checkACHolder(attributes, chain, validationErrors);
		
		if (valid)
			// Check targets
			valid = checkTargets(attributes, validationErrors);
		
		
		// AC extension checking to be compliant with rfc 3281
		if (valid)
			valid = checkAuthorityKeyIdentifierExtension(attributes, validationErrors);
		
		if (valid)
			valid = checkNoRevAvailExtension(attributes, validationErrors);
		
		if (valid)
			valid = checkUnhandledCriticalExtensions(attributes, validationErrors);
		
		return new VOMSValidationResult(attributes, valid, validationErrors);
	}
	
	
	private boolean validateCertificate(X509Certificate c, List validationErrors){
		
		return validateCertificateChain(new X509Certificate[]{c}, validationErrors);
	}
	
	private boolean validateCertificateChain(X509Certificate[] chain, List validationErrors){
		
		ValidationResult result = certChainValidator.validate(chain);
		
		for (ValidationError e: result.getErrors())
			validationErrors.add(VOMSValidationErrorMessage.newErrorMessage(canlError, e.getMessage()));
		
		return result.isValid();
	}

	private boolean verifyACSignature(VOMSAttribute attributes, X509Certificate cert){
		try{
			
			X509CertificateHolder certHolder = new JcaX509CertificateHolder(cert);
			ContentVerifierProvider cvp = new BcRSAContentVerifierProviderBuilder(new DefaultDigestAlgorithmIdentifierFinder()).build(certHolder);
			return attributes.getVOMSAC().isSignatureValid(cvp);
			
		}catch (Exception e) {
			throw new VOMSError("Error verifying AC signature: "+e.getMessage(),e);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy