/* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * 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.gv.egovernment.moa.id.auth.modules.eidas.config; import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.xml.security.signature.XMLSignature; import org.opensaml.Configuration; import org.opensaml.common.impl.SAMLObjectContentReference; import org.opensaml.security.SAMLSignatureProfileValidator; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.security.SecurityConfiguration; import org.opensaml.xml.security.SecurityException; import org.opensaml.xml.security.SecurityHelper; import org.opensaml.xml.security.credential.Credential; import org.opensaml.xml.security.keyinfo.KeyInfoGenerator; import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory; import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorManager; import org.opensaml.xml.security.keyinfo.NamedKeyInfoGeneratorManager; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.signature.KeyInfo; import org.opensaml.xml.signature.SignableXMLObject; import org.opensaml.xml.signature.Signature; import org.opensaml.xml.signature.SignatureException; import org.opensaml.xml.signature.SignatureValidator; import org.opensaml.xml.signature.Signer; import org.opensaml.xml.validation.ValidationException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.sun.istack.Nullable; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAWhiteListConfigurator; import at.gv.egovernment.moa.logging.Logger; import eu.eidas.auth.commons.EidasErrorKey; import eu.eidas.auth.engine.configuration.SamlEngineConfigurationException; import eu.eidas.auth.engine.configuration.dom.ConfigurationAdapter; import eu.eidas.auth.engine.configuration.dom.ConfigurationKey; import eu.eidas.auth.engine.configuration.dom.KeyStoreSignatureConfigurator; import eu.eidas.auth.engine.configuration.dom.SignatureConfiguration; import eu.eidas.auth.engine.core.ProtocolSignerI; import eu.eidas.auth.engine.core.impl.BouncyCastleBootstrap; import eu.eidas.auth.engine.core.impl.CertificateValidator; import eu.eidas.auth.engine.metadata.MetadataSignerI; import eu.eidas.auth.engine.xml.opensaml.CertificateUtil; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; import eu.eidas.samlengineconfig.CertificateConfigurationManager; import eu.eidas.util.Preconditions; /** * @author tlenz * */ public class MOAExtendedSWSigner implements ProtocolSignerI, MetadataSignerI { private static final String DEFAULT_SIGNATURE_ALGORITHM = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; private static final ImmutableSet ALLOWED_ALGORITHMS_FOR_SIGNING; private static final ImmutableSet ALLOWED_ALGORITHMS_FOR_VERIFYING; private static final ImmutableSet DEFAULT_ALGORITHM_WHITE_LIST; private static final String DEFAULT_DIGEST_ALGORITHM = "http://www.w3.org/2001/04/xmlenc#sha512"; private static final ImmutableMap SIGNATURE_TO_DIGEST_ALGORITHM_MAP; private final boolean checkedValidityPeriod; private final boolean disallowedSelfSignedCertificate; private final boolean responseSignAssertions; private final ImmutableSet signatureAlgorithmWhiteList; private final X509Credential privateSigningCredential; private final X509Credential publicSigningCredential; private final X509Credential privateMetadataSigningCredential; private final X509Credential publicMetadataSigningCredential; private final ImmutableList trustedCredentials; private final String signatureAlgorithm; public MOAExtendedSWSigner(Map properties) throws SamlEngineConfigurationException { this(new KeyStoreSignatureConfigurator().getSignatureConfiguration(properties)); } /** * @param configManager * @throws SamlEngineConfigurationException */ public MOAExtendedSWSigner(CertificateConfigurationManager configManager) throws SamlEngineConfigurationException { this(new KeyStoreSignatureConfigurator().getSignatureConfiguration( ConfigurationAdapter.adapt(configManager).getInstances().get(Constants.eIDAS_SAML_ENGINE_NAME).getConfigurationEntries().get(ConfigurationKey.SIGNATURE_CONFIGURATION.getKey()).getParameters())); } protected MOAExtendedSWSigner(SignatureConfiguration signatureConfiguration) throws SamlEngineConfigurationException { this(signatureConfiguration.isCheckedValidityPeriod(), signatureConfiguration.isDisallowedSelfSignedCertificate(), signatureConfiguration.isResponseSignAssertions(), signatureConfiguration.getSignatureKeyAndCertificate(), signatureConfiguration.getTrustedCertificates(), signatureConfiguration.getSignatureAlgorithm(), signatureConfiguration.getSignatureAlgorithmWhiteList(), signatureConfiguration.getMetadataSigningKeyAndCertificate()); } protected MOAExtendedSWSigner(boolean checkedValidityPeriod, boolean disallowedSelfSignedCertificate, boolean responseSignAssertions, KeyStore.PrivateKeyEntry signatureKeyAndCertificate, ImmutableSet trustedCertificates, String signatureAlgorithmVal, String signatureAlgorithmWhiteListStr, KeyStore.PrivateKeyEntry metadataSigningKeyAndCertificate) throws SamlEngineConfigurationException { Preconditions.checkNotNull(signatureKeyAndCertificate, "signatureKeyAndCertificate"); Preconditions.checkNotNull(trustedCertificates, "trustedCertificates"); String signatureAlg = signatureAlgorithmVal; if (StringUtils.isBlank(signatureAlg)) signatureAlg = DEFAULT_SIGNATURE_ALGORITHM; else { signatureAlg = validateSigningAlgorithm(signatureAlg); } ImmutableSet signatureAlgorithmWhiteSet = MOAWhiteListConfigurator.getAllowedAlgorithms( DEFAULT_ALGORITHM_WHITE_LIST, ALLOWED_ALGORITHMS_FOR_VERIFYING, signatureAlgorithmWhiteListStr); this.checkedValidityPeriod = checkedValidityPeriod; this.disallowedSelfSignedCertificate = disallowedSelfSignedCertificate; this.responseSignAssertions = responseSignAssertions; this.trustedCredentials = CertificateUtil.getListOfCredential(trustedCertificates); this.signatureAlgorithmWhiteList = signatureAlgorithmWhiteSet; this.signatureAlgorithm = signatureAlg; this.privateSigningCredential = CertificateUtil.createCredential(signatureKeyAndCertificate); this.publicSigningCredential = CertificateUtil .toCredential((X509Certificate) signatureKeyAndCertificate.getCertificate()); if (null != metadataSigningKeyAndCertificate) { this.privateMetadataSigningCredential = CertificateUtil.createCredential(metadataSigningKeyAndCertificate); this.publicMetadataSigningCredential = CertificateUtil .toCredential((X509Certificate) metadataSigningKeyAndCertificate.getCertificate()); } else { this.privateMetadataSigningCredential = null; this.publicMetadataSigningCredential = null; } } private static X509Certificate getSignatureCertificate(Signature signature) throws EIDASSAMLEngineException { KeyInfo keyInfo = signature.getKeyInfo(); return CertificateUtil.toCertificate(keyInfo); } public static String validateDigestAlgorithm(String signatureAlgorithmName) throws SamlEngineConfigurationException { if (StringUtils.isBlank(signatureAlgorithmName)) { return DEFAULT_DIGEST_ALGORITHM; } //BUGFIX: toLowerCase from original eIDAS SAML-engine produce problems with MGF1 signature algorithms String canonicalAlgorithm = signatureAlgorithmName.trim(); String digestAlgorithm = (String) SIGNATURE_TO_DIGEST_ALGORITHM_MAP.get(canonicalAlgorithm); if (null != digestAlgorithm) { return digestAlgorithm; } String msg = "Signing algorithm \"" + signatureAlgorithmName + "\" does not contain an allowed digest algorithm"; Logger.error(msg); throw new SamlEngineConfigurationException(msg); } public static String validateSigningAlgorithm(@Nullable String signatureAlgorithmName) throws SamlEngineConfigurationException { if ((signatureAlgorithmName == null) || (StringUtils.isBlank(signatureAlgorithmName))) { return DEFAULT_SIGNATURE_ALGORITHM; } //BUGFIX:: toLowerCase from original eIDAS SAML-engine produce problems with MGF1 signature algorithms String canonicalAlgorithm = signatureAlgorithmName.trim(); if (ALLOWED_ALGORITHMS_FOR_SIGNING.contains(canonicalAlgorithm)) { return canonicalAlgorithm; } String msg = "Signing algorithm \"" + signatureAlgorithmName + "\" is not allowed"; Logger.error(msg); throw new SamlEngineConfigurationException(msg); } protected void checkCertificateIssuer(X509Certificate certificate) throws EIDASSAMLEngineException { CertificateValidator.checkCertificateIssuer(this.disallowedSelfSignedCertificate, certificate); } protected void checkCertificateValidityPeriod(X509Certificate certificate) throws EIDASSAMLEngineException { CertificateValidator.checkCertificateValidityPeriod(this.checkedValidityPeriod, certificate); } protected Signature createSignature(X509Credential credential) throws EIDASSAMLEngineException { checkCertificateValidityPeriod(credential.getEntityCertificate()); checkCertificateIssuer(credential.getEntityCertificate()); Signature signature; try { Logger.debug("Creating an OpenSAML signature object"); signature = (Signature) Configuration.getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME) .buildObject(Signature.DEFAULT_ELEMENT_NAME); signature.setSigningCredential(credential); signature.setSignatureAlgorithm(getSignatureAlgorithm()); SecurityConfiguration secConfiguration = Configuration.getGlobalSecurityConfiguration(); NamedKeyInfoGeneratorManager keyInfoManager = secConfiguration.getKeyInfoGeneratorManager(); KeyInfoGeneratorManager keyInfoGenManager = keyInfoManager.getDefaultManager(); KeyInfoGeneratorFactory keyInfoGenFac = keyInfoGenManager.getFactory(credential); KeyInfoGenerator keyInfoGenerator = keyInfoGenFac.newInstance(); KeyInfo keyInfo = keyInfoGenerator.generate(credential); signature.setKeyInfo(keyInfo); signature.setCanonicalizationAlgorithm("http://www.w3.org/2001/10/xml-exc-c14n#"); } catch (SecurityException e) { Logger.error("ERROR : Security exception: " + e, e); throw new EIDASSAMLEngineException(e); } return signature; } public X509Credential getPublicMetadataSigningCredential() { return this.publicMetadataSigningCredential; } public X509Credential getPublicSigningCredential() { return this.publicSigningCredential; } protected String getSignatureAlgorithm() { return this.signatureAlgorithm; } protected ImmutableSet getSignatureAlgorithmWhiteList() { return this.signatureAlgorithmWhiteList; } private X509Credential getTrustedCertificate(Signature signature, List trustedCredentialList) throws EIDASSAMLEngineException { X509Certificate cert = getSignatureCertificate(signature); X509Credential entityX509Cred = CertificateUtil.toCredential(cert); CertificateUtil.checkTrust(entityX509Cred, trustedCredentialList); checkCertificateValidityPeriod(cert); checkCertificateIssuer(cert); return entityX509Cred; } protected ImmutableList getTrustedCredentials() { return this.trustedCredentials; } private boolean isAlgorithmAllowedForVerifying(String signatureAlgorithm) { return ((StringUtils.isNotBlank(signatureAlgorithm)) && (getSignatureAlgorithmWhiteList().contains(signatureAlgorithm.trim()))); } public T sign(T signableObject) throws EIDASSAMLEngineException { return sign(signableObject, this.privateSigningCredential); } protected T sign(T signableObject, X509Credential signingCredential) throws EIDASSAMLEngineException { Logger.trace("Start Sign process."); try { Signature signature = createSignature(signingCredential); signableObject.setSignature(signature); String digestAlgorithm = validateDigestAlgorithm(getSignatureAlgorithm()); List contentReferences = signature.getContentReferences(); if (CollectionUtils.isNotEmpty(contentReferences)) ((SAMLObjectContentReference) contentReferences.get(0)).setDigestAlgorithm(digestAlgorithm); else { Logger.error("Unable to set DigestMethodAlgorithm - algorithm " + digestAlgorithm + " not set"); } Logger.trace("Marshall samlToken."); Configuration.getMarshallerFactory().getMarshaller(signableObject).marshall(signableObject); Logger.trace("Sign samlToken."); Signer.signObject(signature); } catch (MarshallingException e) { Logger.error("ERROR : MarshallingException: " + e, e); throw new EIDASSAMLEngineException(e); } catch (SignatureException e) { Logger.error("ERROR : Signature exception: " + e, e); throw new EIDASSAMLEngineException(e); } return signableObject; } public T signMetadata(T signableObject) throws EIDASSAMLEngineException { if (null == this.privateMetadataSigningCredential) { throw new SamlEngineConfigurationException("No metadataSigningCredential configured"); } return sign(signableObject, this.privateMetadataSigningCredential); } public T validateMetadataSignature(T signedMetadata) throws EIDASSAMLEngineException { return validateSignature(signedMetadata, null); } private SAMLSignatureProfileValidator validateSamlSignatureStructure(SignableXMLObject signableObject) throws EIDASSAMLEngineException { SAMLSignatureProfileValidator sigProfValidator = new SAMLSignatureProfileValidator(); try { sigProfValidator.validate(signableObject.getSignature()); } catch (ValidationException e) { Logger.error("ERROR : ValidationException: signature isn't conform to SAML Signature profile: " + e, e); throw new EIDASSAMLEngineException(e); } return sigProfValidator; } public T validateSignature(T signedObject, Collection trustedCertificateCollection) throws EIDASSAMLEngineException { List trustedCreds; if (CollectionUtils.isEmpty(trustedCertificateCollection)) trustedCreds = getTrustedCredentials(); else { trustedCreds = CertificateUtil.getListOfCredential(trustedCertificateCollection); } return (T) validateSignatureWithCredentials(signedObject, trustedCreds); } private T validateSignatureWithCredentials(T signedObject, List trustedCredentialList) throws EIDASSAMLEngineException { Logger.debug("Start signature validation."); validateSamlSignatureStructure(signedObject); verifyCryptographicSignature(signedObject.getSignature(), trustedCredentialList); return signedObject; } private void verifyCryptographicSignature(Signature signature, List trustedCredentialList) throws EIDASSAMLEngineException { String signatureAlgorithmVal = signature.getSignatureAlgorithm(); Logger.trace("Key algorithm " + SecurityHelper.getKeyAlgorithmFromURI(signatureAlgorithmVal)); if (!(isAlgorithmAllowedForVerifying(signatureAlgorithmVal))) { Logger.error("ERROR : the algorithm " + signatureAlgorithmVal + " used by the signature is not allowed"); throw new EIDASSAMLEngineException(EidasErrorKey.INVALID_SIGNATURE_ALGORITHM.errorCode()); } X509Credential entityX509Cred = getTrustedCertificate(signature, trustedCredentialList); SignatureValidator sigValidator = new SignatureValidator(entityX509Cred); try { sigValidator.validate(signature); } catch (ValidationException e) { Logger.error("ERROR : Signature Validation Exception: " + e, e); throw new EIDASSAMLEngineException(e); } } public boolean isResponseSignAssertions() { return this.responseSignAssertions; } static { BouncyCastleBootstrap.bootstrap(); ALLOWED_ALGORITHMS_FOR_SIGNING = ImmutableSet.of("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", //Set other algorithms which are not supported by openSAML in default XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1); ALLOWED_ALGORITHMS_FOR_VERIFYING = ImmutableSet.of("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "http://www.w3.org/2001/04/xmldsig-more#rsa-ripemd160", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", //Set other algorithms which are not supported by openSAML in default XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1_MGF1, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA224_MGF1, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384_MGF1, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1); DEFAULT_ALGORITHM_WHITE_LIST = ImmutableSet.of("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", //Set other algorithms which are not supported by openSAML in default XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1); SIGNATURE_TO_DIGEST_ALGORITHM_MAP = ImmutableMap.builder() .put("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2001/04/xmlenc#sha256") .put("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#sha384") .put("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "http://www.w3.org/2001/04/xmlenc#sha512") .put("http://www.w3.org/2001/04/xmldsig-more#rsa-ripemd160", "http://www.w3.org/2001/04/xmlenc#ripemd160") .put("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", "http://www.w3.org/2001/04/xmlenc#sha256") .put("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", "http://www.w3.org/2001/04/xmldsig-more#sha384") .put("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", "http://www.w3.org/2001/04/xmlenc#sha512") //Set other algorithms which are not supported by openSAML in default .put(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1, "http://www.w3.org/2001/04/xmlenc#sha256") .put(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1, "http://www.w3.org/2001/04/xmlenc#sha512") .build(); } }