/******************************************************************************* * Copyright 2018 A-SIT Plus GmbH * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "License"); * You may not use this work except in compliance with the License. * You may obtain a copy of the License at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * * 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. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ /******************************************************************************* *******************************************************************************/ package at.asitplus.eidas.specific.connector.verification; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import org.opensaml.common.SignableSAMLObject; import org.opensaml.saml2.metadata.EntitiesDescriptor; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.security.SAMLSignatureProfileValidator; import org.opensaml.xml.security.x509.BasicX509Credential; import org.opensaml.xml.signature.SignatureValidator; import org.opensaml.xml.validation.ValidationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; import at.gv.egiz.eaaf.modules.pvp2.idp.exception.SamlRequestNotSignedException; import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.AbstractMetadataSignatureFilter; public class MetadataSignatureVerificationFilter extends AbstractMetadataSignatureFilter{ private static final Logger log = LoggerFactory.getLogger(MetadataSignatureVerificationFilter.class); private String metadataURL; private List trustedCredential = new ArrayList(); public MetadataSignatureVerificationFilter(String trustStorePath, String trustStorePassword, String metadataURL) throws Pvp2MetadataException { this.metadataURL = metadataURL; log.trace("Initialize metadata signature-verification filter with truststore: " + trustStorePath + " ... "); try { KeyStore keyStore = KeyStoreUtils.loadKeyStore(trustStorePath, trustStorePassword); if (keyStore != null) { //load trusted certificates Enumeration aliases = keyStore.aliases(); while(aliases.hasMoreElements()) { String el = aliases.nextElement(); log.trace("Process TrustStoreEntry: " + el); if (keyStore.isCertificateEntry(el)) { Certificate cert = keyStore.getCertificate(el); if (cert != null && cert instanceof X509Certificate) { BasicX509Credential trustedCert = new BasicX509Credential(); trustedCert.setEntityCertificate((X509Certificate) cert); this.trustedCredential.add(trustedCert); log.debug("Add cert: " + ((X509Certificate) cert).getSubjectDN() + " as trusted for metadata: " + metadataURL); } else log.info("Can not process entry: " + el + ". Reason: " + cert.toString()); } } } else throw new Pvp2MetadataException("pvp2.26", new Object[] {"Can not open trustStore: " + trustStorePath + " for metadata: " + metadataURL}); } catch (KeyStoreException | IOException e) { log.warn("Can not open trustStore: " + trustStorePath + " for metadata: " + metadataURL + " Reason: " + e.getMessage(), e); throw new Pvp2MetadataException("pvp2.26", new Object[] {"Can not open trustStore: " + trustStorePath + " for metadata"}, e); } } @Override protected void verify(EntityDescriptor desc) throws Pvp2MetadataException { try { internalVerify(desc); } catch (EaafException e) { log.info("Metadata verification FAILED for: " + metadataURL + " Reason: " +e.getMessage()); throw new Pvp2MetadataException("pvp2.26", new Object[] {"Metadata verification FAILED for: " + metadataURL + " Reason: " +e.getMessage()}, e); } } @Override protected void verify(EntitiesDescriptor desc) throws Pvp2MetadataException { throw new Pvp2MetadataException("pvp2.26", new Object[] {"EntitiesDescritors are NOT supported"}); } @Override protected void verify(EntityDescriptor entity, EntitiesDescriptor desc) throws Pvp2MetadataException { throw new Pvp2MetadataException("pvp2.26", new Object[] {"EntitiesDescritors are NOT supported"}); } private void internalVerify(SignableSAMLObject signedElement) throws EaafException { if (signedElement.getSignature() == null) { throw new SamlRequestNotSignedException(); } try { SAMLSignatureProfileValidator sigValidator = new SAMLSignatureProfileValidator(); sigValidator.validate(signedElement.getSignature()); } catch (ValidationException e) { log.error("Failed to validate Signature", e); throw new SamlRequestNotSignedException(e); } boolean isTrusted = false; for (BasicX509Credential cred : trustedCredential) { SignatureValidator sigValidator = new SignatureValidator(cred); try { sigValidator.validate(signedElement.getSignature()); isTrusted = true; } catch (ValidationException e) { log.info("Failed to verfiy Signature with cert: " + cred.getEntityCertificate().getSubjectDN() + " Reason: " + e.getMessage()); } } if (!isTrusted) { log.warn("PVP2 metadata: " + metadataURL + " are NOT trusted!"); throw new SamlRequestNotSignedException(); } } }