/******************************************************************************* * 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.protocols.pvp2x.verification; import java.util.ArrayList; import java.util.List; import org.joda.time.DateTime; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.core.Conditions; import org.opensaml.saml2.core.EncryptedAssertion; import org.opensaml.saml2.core.RequestAbstractType; import org.opensaml.saml2.core.Response; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.core.StatusResponseType; import org.opensaml.saml2.encryption.Decrypter; import org.opensaml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver; import org.opensaml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml2.metadata.SPSSODescriptor; import org.opensaml.security.MetadataCriteria; import org.opensaml.security.SAMLSignatureProfileValidator; import org.opensaml.xml.encryption.ChainingEncryptedKeyResolver; import org.opensaml.xml.encryption.DecryptionException; import org.opensaml.xml.encryption.InlineEncryptedKeyResolver; import org.opensaml.xml.encryption.SimpleRetrievalMethodEncryptedKeyResolver; import org.opensaml.xml.security.CriteriaSet; import org.opensaml.xml.security.credential.UsageType; import org.opensaml.xml.security.criteria.EntityIDCriteria; import org.opensaml.xml.security.criteria.UsageCriteria; import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.signature.SignatureTrustEngine; import org.opensaml.xml.validation.ValidationException; import at.gv.egovernment.moa.id.auth.exception.InvalidProtocolRequestException; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.protocols.pvp2x.config.PVPConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AssertionValidationExeption; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.InboundMessage; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOARequest; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOAResponse; import at.gv.egovernment.moa.id.protocols.pvp2x.signer.CredentialProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.signer.CredentialsNotAvailableException; import at.gv.egovernment.moa.logging.Logger; public class SAMLVerificationEngine { public void verify(InboundMessage msg, SignatureTrustEngine sigTrustEngine ) throws org.opensaml.xml.security.SecurityException, Exception { if (msg instanceof MOARequest && ((MOARequest)msg).getSamlRequest() instanceof RequestAbstractType) verifyRequest(((RequestAbstractType)((MOARequest)msg).getSamlRequest()), sigTrustEngine); else verifyResponse(((MOAResponse)msg).getResponse(), sigTrustEngine); } public void verifyResponse(StatusResponseType samlObj, SignatureTrustEngine sigTrustEngine ) throws org.opensaml.xml.security.SecurityException, Exception { SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator(); try { profileValidator.validate(samlObj.getSignature()); } catch (ValidationException e) { // Indicates signature did not conform to SAML Signature profile e.printStackTrace(); } CriteriaSet criteriaSet = new CriteriaSet(); criteriaSet.add( new EntityIDCriteria(samlObj.getIssuer().getValue()) ); criteriaSet.add( new MetadataCriteria(IDPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS) ); criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); try { if (!sigTrustEngine.validate(samlObj.getSignature(), criteriaSet)) { throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}); } } catch (SecurityException e) { e.printStackTrace(); throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}); } } public void verifyRequest(RequestAbstractType samlObj, SignatureTrustEngine sigTrustEngine ) throws org.opensaml.xml.security.SecurityException, Exception { SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator(); try { profileValidator.validate(samlObj.getSignature()); } catch (ValidationException e) { // Indicates signature did not conform to SAML Signature profile e.printStackTrace(); } CriteriaSet criteriaSet = new CriteriaSet(); criteriaSet.add( new EntityIDCriteria(samlObj.getIssuer().getValue()) ); criteriaSet.add( new MetadataCriteria(SPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS) ); criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); try { if (!sigTrustEngine.validate(samlObj.getSignature(), criteriaSet)) { throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}); } } catch (SecurityException e) { e.printStackTrace(); throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}); } } public static void validateAssertion(Response samlResp, boolean validateDestination) throws AssertionValidationExeption { try { if (samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) { List saml2assertions = new ArrayList(); if (validateDestination && !samlResp.getDestination().startsWith( PVPConfiguration.getInstance().getIDPPublicPath())) { Logger.warn("PVP 2.1 assertion destination does not match to IDP URL"); throw new AssertionValidationExeption("PVP 2.1 assertion destination does not match to IDP URL", null); } //check encrypted Assertion List encryAssertionList = samlResp.getEncryptedAssertions(); if (encryAssertionList != null && encryAssertionList.size() > 0) { //decrypt assertions Logger.debug("Found encryped assertion. Start decryption ..."); X509Credential authDecCredential = CredentialProvider.getIDPAssertionEncryptionCredential(); StaticKeyInfoCredentialResolver skicr = new StaticKeyInfoCredentialResolver(authDecCredential); ChainingEncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(); encryptedKeyResolver.getResolverChain().add( new InlineEncryptedKeyResolver() ); encryptedKeyResolver.getResolverChain().add( new EncryptedElementTypeEncryptedKeyResolver() ); encryptedKeyResolver.getResolverChain().add( new SimpleRetrievalMethodEncryptedKeyResolver() ); Decrypter samlDecrypter = new Decrypter(null, skicr, encryptedKeyResolver); for (EncryptedAssertion encAssertion : encryAssertionList) { saml2assertions.add(samlDecrypter.decrypt(encAssertion)); } Logger.debug("Assertion decryption finished. "); } else { saml2assertions.addAll(samlResp.getAssertions()); } List validatedassertions = new ArrayList(); for (org.opensaml.saml2.core.Assertion saml2assertion : saml2assertions) { Conditions conditions = saml2assertion.getConditions(); DateTime notbefore = conditions.getNotBefore(); DateTime notafter = conditions.getNotOnOrAfter(); if ( notbefore.isAfterNow() || notafter.isBeforeNow() ) { Logger.warn("PVP2 Assertion is out of Date"); } else { validatedassertions.add(saml2assertion); } } if (validatedassertions.isEmpty()) { Logger.info("No valid PVP 2.1 assertion received."); throw new AssertionValidationExeption("No valid PVP 2.1 assertion received.", null); } samlResp.getAssertions().clear(); samlResp.getEncryptedAssertions().clear(); samlResp.getAssertions().addAll(validatedassertions); } else { Logger.info("PVP 2.1 assertion includes an error. Receive errorcode " + samlResp.getStatus().getStatusCode().getValue()); throw new AssertionValidationExeption("PVP 2.1 assertion includes an error. Receive errorcode " + samlResp.getStatus().getStatusCode().getValue(), null); } } catch (CredentialsNotAvailableException e) { Logger.warn("Assertion decrypt FAILED - No Credentials", e); throw new AssertionValidationExeption("Assertion decrypt FAILED - No Credentials", null, e); } catch (DecryptionException e) { Logger.warn("Assertion decrypt FAILED.", e); throw new AssertionValidationExeption("Assertion decrypt FAILED.", null, e); } catch (ConfigurationException e) { throw new AssertionValidationExeption("pvp.12", null, e); } } }