From 2b13590261375b56c272945b159d67986162be77 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Fri, 7 Mar 2014 09:44:53 +0100 Subject: PVP 2.1: add Transient and Unspecified NameIDFormat * only return a persistent subjectNameID if it is requested or metadata includes only one NameIDFormat with value persistent --- .../moa/id/demoOA/servlet/pvp2/Authenticate.java | 5 +- .../moa/id/entrypoints/DispatcherServlet.java | 8 +- .../moa/id/protocols/pvp2x/PVP2XProtocol.java | 39 +++++- .../builder/assertion/PVP2AssertionBuilder.java | 149 ++++++++++++--------- .../exceptions/AuthnRequestValidatorException.java | 62 +++++++++ .../NameIDFormatNotSupportedException.java | 2 +- .../pvp2x/validation/AuthnRequestValidator.java | 58 ++++++++ 7 files changed, 247 insertions(+), 76 deletions(-) create mode 100644 id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/AuthnRequestValidatorException.java create mode 100644 id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/validation/AuthnRequestValidator.java diff --git a/id/oa/src/main/java/at/gv/egovernment/moa/id/demoOA/servlet/pvp2/Authenticate.java b/id/oa/src/main/java/at/gv/egovernment/moa/id/demoOA/servlet/pvp2/Authenticate.java index 5bf9c4970..00d82296c 100644 --- a/id/oa/src/main/java/at/gv/egovernment/moa/id/demoOA/servlet/pvp2/Authenticate.java +++ b/id/oa/src/main/java/at/gv/egovernment/moa/id/demoOA/servlet/pvp2/Authenticate.java @@ -129,10 +129,11 @@ public class Authenticate extends HttpServlet { authReq.setSubject(subject); issuer.setFormat(NameIDType.ENTITY); authReq.setIssuer(issuer); + NameIDPolicy policy = SAML2Utils .createSAMLObject(NameIDPolicy.class); - policy.setAllowCreate(true); - policy.setFormat(NameID.PERSISTENT); + policy.setAllowCreate(true); + policy.setFormat(NameID.PERSISTENT); authReq.setNameIDPolicy(policy); String entityname = config.getPVP2IDPMetadataEntityName(); diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/entrypoints/DispatcherServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/entrypoints/DispatcherServlet.java index 34366b790..d58b7b267 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/entrypoints/DispatcherServlet.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/entrypoints/DispatcherServlet.java @@ -54,6 +54,7 @@ import at.gv.egovernment.moa.id.moduls.ModulStorage; import at.gv.egovernment.moa.id.moduls.NoPassivAuthenticationException; import at.gv.egovernment.moa.id.moduls.RequestStorage; import at.gv.egovernment.moa.id.moduls.SSOManager; +import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AuthnRequestValidatorException; import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage; import at.gv.egovernment.moa.id.storage.DBExceptionStoreImpl; import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; @@ -320,7 +321,12 @@ public class DispatcherServlet extends AuthServlet{ resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); return; - + } catch (AuthnRequestValidatorException e) { + //log Error Message + StatisticLogger logger = StatisticLogger.getInstance(); + logger.logErrorOperation(e, e.getErrorRequest()); + return; + } catch (MOAIDException e) { Logger.error("Failed to generate a valid protocol request!"); resp.setContentType("text/html;charset=UTF-8"); diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java index e5158f4bf..804688de5 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java @@ -40,6 +40,7 @@ import org.opensaml.saml2.core.Response; import org.opensaml.saml2.core.Status; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.core.StatusMessage; +import org.opensaml.saml2.core.impl.AuthnRequestImpl; import org.opensaml.saml2.metadata.AssertionConsumerService; import org.opensaml.saml2.metadata.AttributeConsumingService; import org.opensaml.saml2.metadata.EntityDescriptor; @@ -58,11 +59,14 @@ import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IEncoder; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.MOARequest; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.PostBinding; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.RedirectBinding; +import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AuthnRequestValidatorException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.MandateAttributesNotHandleAbleException; +import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.NameIDFormatNotSupportedException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.NoMetadataInformationException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.PVP2Exception; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.CheckMandateAttributes; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.SAML2Utils; +import at.gv.egovernment.moa.id.protocols.pvp2x.validation.AuthnRequestValidator; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.SAMLVerificationEngine; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.TrustEngineFactory; import at.gv.egovernment.moa.id.util.VelocityLogAdapter; @@ -169,7 +173,7 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { if(!(samlReq instanceof AuthnRequest)) { throw new MOAIDException("Unsupported request", new Object[] {}); } - + EntityDescriptor metadata = moaRequest.getEntityMetadata(); if(metadata == null) { throw new NoMetadataInformationException(); @@ -203,7 +207,7 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { spSSODescriptor.getAttributeConsumingServices().size() > 0) { attributeConsumer = spSSODescriptor.getAttributeConsumingServices().get(attributeIdx); } - + String oaURL = moaRequest.getEntityMetadata().getEntityID(); String binding = consumerService.getBinding(); // String entityID = moaRequest.getEntityMetadata().getEntityID(); @@ -225,11 +229,30 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { } } } - - request.getSession().setAttribute(PARAM_OA, oaURL); + //validate AuthnRequest + try { + AuthnRequestValidator.validate((AuthnRequestImpl) samlReq); + + } catch (AuthnRequestValidatorException e) { + if (generateErrorMessage(e, request, response, config)) { + throw new AuthnRequestValidatorException(e.getMessage(), + new Object[] {}, config); + + } else { + throw new MOAIDException(e.getMessage(), new Object[] {}); + + } + } + + request.getSession().setAttribute(PARAM_OA, oaURL); + return config; - } catch (Exception e) { + + } catch (PVP2Exception e) { + throw e; + + } catch (Throwable e) { e.printStackTrace(); throw new MOAIDException(e.getMessage(), new Object[] {}); } @@ -256,6 +279,11 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { if(e instanceof NoPassivAuthenticationException) { statusCode.setValue(StatusCode.NO_PASSIVE_URI); statusMessage.setMessage(e.getLocalizedMessage()); + + } else if (e instanceof NameIDFormatNotSupportedException) { + statusCode.setValue(StatusCode.INVALID_NAMEID_POLICY_URI); + statusMessage.setMessage(e.getLocalizedMessage()); + } else if(e instanceof PVP2Exception) { PVP2Exception ex = (PVP2Exception) e; statusCode.setValue(ex.getStatusCodeValue()); @@ -263,6 +291,7 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { if(statusMessageValue != null) { statusMessage.setMessage(statusMessageValue); } + } else { statusCode.setValue(StatusCode.RESPONDER_URI); statusMessage.setMessage(e.getLocalizedMessage()); diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java index bc90da8df..e834797b1 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java @@ -22,6 +22,7 @@ *******************************************************************************/ package at.gv.egovernment.moa.id.protocols.pvp2x.builder.assertion; +import java.security.MessageDigest; import java.util.Iterator; import java.util.List; @@ -43,6 +44,7 @@ import org.opensaml.saml2.core.RequestedAuthnContext; import org.opensaml.saml2.core.Subject; import org.opensaml.saml2.core.SubjectConfirmation; import org.opensaml.saml2.core.SubjectConfirmationData; +import org.opensaml.saml2.core.impl.AuthnRequestImpl; import org.opensaml.saml2.metadata.AssertionConsumerService; import org.opensaml.saml2.metadata.AttributeConsumingService; import org.opensaml.saml2.metadata.EntityDescriptor; @@ -75,7 +77,9 @@ import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.UnprovideableAttribut import at.gv.egovernment.moa.id.protocols.pvp2x.utils.SAML2Utils; import at.gv.egovernment.moa.id.util.MandateBuilder; import at.gv.egovernment.moa.id.util.QAALevelVerifier; +import at.gv.egovernment.moa.id.util.Random; import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Base64Utils; import at.gv.egovernment.moa.util.Constants; public class PVP2AssertionBuilder implements PVPConstants { @@ -95,54 +99,55 @@ public class PVP2AssertionBuilder implements PVPConstants { peerEntity.getEntityID()); if (reqAuthnContext == null) { - authnContextClassRef.setAuthnContextClassRef(STORK_QAA_1_4); + authnContextClassRef.setAuthnContextClassRef(authSession.getQAALevel()); - } + } else { - boolean stork_qaa_1_4_found = false; + boolean stork_qaa_1_4_found = false; - List reqAuthnContextClassRefIt = reqAuthnContext - .getAuthnContextClassRefs(); + List reqAuthnContextClassRefIt = reqAuthnContext + .getAuthnContextClassRefs(); - if (reqAuthnContextClassRefIt.size() == 0) { + if (reqAuthnContextClassRefIt.size() == 0) { - QAALevelVerifier.verifyQAALevel(authSession.getQAALevel(), - STORK_QAA_1_4); + QAALevelVerifier.verifyQAALevel(authSession.getQAALevel(), + STORK_QAA_1_4); - stork_qaa_1_4_found = true; - authnContextClassRef.setAuthnContextClassRef(STORK_QAA_1_4); + stork_qaa_1_4_found = true; + authnContextClassRef.setAuthnContextClassRef(STORK_QAA_1_4); - } else { - for (AuthnContextClassRef authnClassRef : reqAuthnContextClassRefIt) { - String qaa_uri = authnClassRef.getAuthnContextClassRef(); - if (qaa_uri.trim().equals(STORK_QAA_1_4) - || qaa_uri.trim().equals(STORK_QAA_1_3) - || qaa_uri.trim().equals(STORK_QAA_1_2) - || qaa_uri.trim().equals(STORK_QAA_1_1)) { + } else { + for (AuthnContextClassRef authnClassRef : reqAuthnContextClassRefIt) { + String qaa_uri = authnClassRef.getAuthnContextClassRef(); + if (qaa_uri.trim().equals(STORK_QAA_1_4) + || qaa_uri.trim().equals(STORK_QAA_1_3) + || qaa_uri.trim().equals(STORK_QAA_1_2) + || qaa_uri.trim().equals(STORK_QAA_1_1)) { - if (authSession.isForeigner()) { - QAALevelVerifier.verifyQAALevel(authSession.getQAALevel(), - STORK_QAA_PREFIX + oaParam.getQaaLevel()); - - stork_qaa_1_4_found = true; - authnContextClassRef.setAuthnContextClassRef(authSession.getQAALevel()); - - } else { - - QAALevelVerifier.verifyQAALevel(authSession.getQAALevel(), - qaa_uri.trim()); - - stork_qaa_1_4_found = true; - authnContextClassRef.setAuthnContextClassRef(authSession.getQAALevel()); - + if (authSession.isForeigner()) { + QAALevelVerifier.verifyQAALevel(authSession.getQAALevel(), + STORK_QAA_PREFIX + oaParam.getQaaLevel()); + + stork_qaa_1_4_found = true; + authnContextClassRef.setAuthnContextClassRef(authSession.getQAALevel()); + + } else { + + QAALevelVerifier.verifyQAALevel(authSession.getQAALevel(), + qaa_uri.trim()); + + stork_qaa_1_4_found = true; + authnContextClassRef.setAuthnContextClassRef(authSession.getQAALevel()); + + } + break; } - break; } } - } - - if (!stork_qaa_1_4_found) { - throw new QAANotSupportedException(STORK_QAA_1_4); + + if (!stork_qaa_1_4_found) { + throw new QAANotSupportedException(STORK_QAA_1_4); + } } // reqAuthnContextClassRefIt = reqAuthnContext.getAuthnContextClassRefs() @@ -196,33 +201,6 @@ public class PVP2AssertionBuilder implements PVPConstants { .createSAMLObject(AttributeStatement.class); Subject subject = SAML2Utils.createSAMLObject(Subject.class); - NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class); - boolean foundFormat = false; - - // TL: AuthData generation is moved to Assertion generation. - - Iterator formatIt = spSSODescriptor.getNameIDFormats() - .iterator(); - while (formatIt.hasNext()) { - if (formatIt.next().getFormat().equals(NameID.PERSISTENT)) { - foundFormat = true; - break; - } - } - if (!foundFormat) { - // TODO use correct exception - throw new NameIDFormatNotSupportedException(""); - } - - // TODO: Check if we need to hide source pin - /* - * if(authSession.getUseMandate()) { Element mandate = - * authSession.getMandate(); if(authSession.getBusinessService()) { // - * Hide Source PIN! ParepUtils.HideStammZahlen(mandate, true, null, - * authSession.getDomainIdentifier(), true); } else { - * ParepUtils.HideStammZahlen(mandate, false, authSession.getTarget(), - * null, true); } } - */ AuthenticationData authData = AuthenticationServer .buildAuthenticationData(authSession, oaParam, @@ -263,9 +241,9 @@ public class PVP2AssertionBuilder implements PVPConstants { if (attributeStatement.getAttributes().size() > 0) { assertion.getAttributeStatements().add(attributeStatement); } - - subjectNameID.setFormat(NameID.PERSISTENT); + NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class); + //TLenz: set correct bPK Type and Value from AuthData if (authSession.getUseMandate()) { Element mandate = authSession.getMandate(); @@ -295,6 +273,7 @@ public class PVP2AssertionBuilder implements PVPConstants { String bpktype = id.getType(); String bpk = id.getValue().getValue(); + if (bpktype.equals(Constants.URN_PREFIX_BASEID)) { if (authSession.getBusinessService()) { subjectNameID.setValue(new BPKBuilder().buildWBPK(bpk, oaParam.getIdentityLinkDomainIdentifier())); @@ -322,7 +301,43 @@ public class PVP2AssertionBuilder implements PVPConstants { subjectNameID.setValue(authData.getBPK()); } - + String nameIDFormat = NameID.TRANSIENT; + + //get NameIDFormat from request + AuthnRequest authnReq = (AuthnRequestImpl) authnRequest; + if (authnReq.getNameIDPolicy() != null) { + nameIDFormat = authnReq.getNameIDPolicy().getFormat(); + + } else { + //get NameIDFormat from metadata + List metadataNameIDFormats = spSSODescriptor.getNameIDFormats(); + + if (metadataNameIDFormats != null) { + if (metadataNameIDFormats.size() == 1) + nameIDFormat = metadataNameIDFormats.get(0).getFormat(); + + } + } + + if (NameID.TRANSIENT.equals(nameIDFormat) || NameID.UNSPECIFIED.equals(nameIDFormat)) { + String random = Random.nextRandom(); + String nameID = subjectNameID.getValue(); + + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] hash = md.digest((nameID + random).getBytes("ISO-8859-1")); + subjectNameID.setValue(Base64Utils.encode(hash)); + subjectNameID.setFormat(NameID.TRANSIENT); + + } catch (Exception e) { + Logger.warn("PVP2 subjectNameID error", e); + throw new MOAIDException("pvp2.13", null, e); + } + + } else + subjectNameID.setFormat(nameIDFormat); + + subject.setNameID(subjectNameID); SubjectConfirmation subjectConfirmation = SAML2Utils diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/AuthnRequestValidatorException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/AuthnRequestValidatorException.java new file mode 100644 index 000000000..7ed438471 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/AuthnRequestValidatorException.java @@ -0,0 +1,62 @@ +/* + * 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.exceptions; + +import at.gv.egovernment.moa.id.moduls.IRequest; + +/** + * @author tlenz + * + */ +public class AuthnRequestValidatorException extends PVP2Exception { + + private IRequest errorRequest = null; + + /** + * + */ + private static final long serialVersionUID = 4939651000658508576L; + + /** + * @param messageId + * @param parameters + */ + public AuthnRequestValidatorException(String messageId, Object[] parameters) { + super(messageId, parameters); + + } + + public AuthnRequestValidatorException(String messageId, Object[] parameters, IRequest errorRequest) { + super(messageId, parameters); + this.errorRequest = errorRequest; + + } + + /** + * @return the errorRequest + */ + public IRequest getErrorRequest() { + return errorRequest; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/NameIDFormatNotSupportedException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/NameIDFormatNotSupportedException.java index 5a393062f..b5facde34 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/NameIDFormatNotSupportedException.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/exceptions/NameIDFormatNotSupportedException.java @@ -22,7 +22,7 @@ *******************************************************************************/ package at.gv.egovernment.moa.id.protocols.pvp2x.exceptions; -public class NameIDFormatNotSupportedException extends PVP2Exception { +public class NameIDFormatNotSupportedException extends AuthnRequestValidatorException { public NameIDFormatNotSupportedException(String nameIDFormat) { super("pvp2.12", new Object[] {nameIDFormat}); diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/validation/AuthnRequestValidator.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/validation/AuthnRequestValidator.java new file mode 100644 index 000000000..ab8fab5d1 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/validation/AuthnRequestValidator.java @@ -0,0 +1,58 @@ +/* + * 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.validation; + +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.NameID; +import org.opensaml.saml2.core.NameIDPolicy; + +import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AuthnRequestValidatorException; +import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.NameIDFormatNotSupportedException; + +/** + * @author tlenz + * + */ +public class AuthnRequestValidator { + + public static void validate(AuthnRequest req) throws AuthnRequestValidatorException{ + + //validate NameIDPolicy + NameIDPolicy nameIDPolicy = req.getNameIDPolicy(); + if (nameIDPolicy != null) { + String nameIDFormat = nameIDPolicy.getFormat(); + + if ( !(nameIDFormat != null && + (NameID.TRANSIENT.equals(nameIDFormat) || + NameID.PERSISTENT.equals(nameIDFormat) || + NameID.UNSPECIFIED.equals(nameIDFormat))) ) { + + throw new NameIDFormatNotSupportedException(nameIDFormat); + + } + } + + + + } +} -- cgit v1.2.3