diff options
Diffstat (limited to 'id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java')
-rw-r--r-- | id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java | 1019 |
1 files changed, 1019 insertions, 0 deletions
diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java new file mode 100644 index 000000000..5f4ec2d29 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java @@ -0,0 +1,1019 @@ +package at.gv.egovernment.moa.id.auth; + +import iaik.pki.PKIException; +import iaik.x509.X509Certificate; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import javax.xml.transform.TransformerException; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.AuthenticationException; +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.ServiceException; +import at.gv.egovernment.moa.id.auth.builder.AuthenticationBlockAssertionBuilder; +import at.gv.egovernment.moa.id.auth.builder.AuthenticationDataAssertionBuilder; +import at.gv.egovernment.moa.id.auth.builder.BPKBuilder; +import at.gv.egovernment.moa.id.auth.builder.CertInfoVerifyXMLSignatureRequestBuilder; +import at.gv.egovernment.moa.id.auth.builder.CreateXMLSignatureRequestBuilder; +import at.gv.egovernment.moa.id.auth.builder.DataURLBuilder; +import at.gv.egovernment.moa.id.auth.builder.GetIdentityLinkFormBuilder; +import at.gv.egovernment.moa.id.auth.builder.InfoboxReadRequestBuilder; +import at.gv.egovernment.moa.id.auth.builder.InfoboxValidatorParamsBuilder; +import at.gv.egovernment.moa.id.auth.builder.PersonDataBuilder; +import at.gv.egovernment.moa.id.auth.builder.SAMLArtifactBuilder; +import at.gv.egovernment.moa.id.auth.builder.SelectBKUFormBuilder; +import at.gv.egovernment.moa.id.auth.builder.VerifyXMLSignatureRequestBuilder; +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.id.auth.data.CreateXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.id.auth.data.InfoboxValidationResult; +import at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams; +import at.gv.egovernment.moa.id.auth.data.VerifyXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.invoke.SignatureVerificationInvoker; +import at.gv.egovernment.moa.id.auth.parser.CreateXMLSignatureResponseParser; +import at.gv.egovernment.moa.id.auth.parser.ExtendedInfoboxReadResponseParser; +import at.gv.egovernment.moa.id.auth.parser.InfoboxReadResponseParser; +import at.gv.egovernment.moa.id.auth.parser.SAMLArtifactParser; +import at.gv.egovernment.moa.id.auth.parser.VerifyXMLSignatureResponseParser; +import at.gv.egovernment.moa.id.auth.servlet.AuthServlet; +import at.gv.egovernment.moa.id.auth.validator.CreateXMLSignatureResponseValidator; +import at.gv.egovernment.moa.id.auth.validator.IdentityLinkValidator; +import at.gv.egovernment.moa.id.auth.validator.InfoboxValidator; +import at.gv.egovernment.moa.id.auth.validator.ValidateException; +import at.gv.egovernment.moa.id.auth.validator.VerifyXMLSignatureResponseValidator; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConfigurationProvider; +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; +import at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameter; +import at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameters; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.id.util.HTTPUtils; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.id.util.Random; +import at.gv.egovernment.moa.id.util.SSLUtils; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.BoolUtils; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.DateTimeUtils; +import at.gv.egovernment.moa.util.FileUtils; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * API for MOA ID Authentication Service.<br> + * {@link AuthenticationSession} is stored in a session store and retrieved + * by giving the session ID. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationServer implements MOAIDAuthConstants { + + /** single instance */ + private static AuthenticationServer instance; + /** session data store (session ID -> AuthenticationSession) */ + private static Map sessionStore = new HashMap(); + /** authentication data store (assertion handle -> AuthenticationData) */ + private static Map authenticationDataStore = new HashMap(); + /** + * time out in milliseconds used by {@link cleanup} for session store + */ + private long sessionTimeOut = 10 * 60 * 1000; // default 10 minutes + /** + * time out in milliseconds used by {@link cleanup} for authentication data store + */ + private long authDataTimeOut = 2 * 60 * 1000; // default 2 minutes + + /** + * Returns the single instance of <code>AuthenticationServer</code>. + * + * @return the single instance of <code>AuthenticationServer</code> + */ + public static AuthenticationServer getInstance() { + if (instance == null) + instance = new AuthenticationServer(); + return instance; + } + /** + * Constructor for AuthenticationServer. + */ + public AuthenticationServer() { + super(); + } + /** + * Processes request to select a BKU. + * <br/>Processing depends on value of {@link AuthConfigurationProvider#getBKUSelectionType}. + * <br/>For <code>bkuSelectionType==HTMLComplete</code>, a <code>returnURI</code> for the + * "BKU Auswahl" service is returned. + * <br/>For <code>bkuSelectionType==HTMLSelect</code>, an HTML form for BKU selection is returned. + * @param authURL base URL of MOA-ID Auth component + * @param target "Geschäftsbereich" + * @param oaURL online application URL requested + * @param bkuSelectionTemplateURL template for BKU selection form to be used + * in case of <code>HTMLSelect</code>; may be null + * @param templateURL URL providing an HTML template for the HTML form to be used + * for call <code>startAuthentication</code> + * @return for <code>bkuSelectionType==HTMLComplete</code>, the <code>returnURI</code> for the + * "BKU Auswahl" service; + * for <code>bkuSelectionType==HTMLSelect</code>, an HTML form for BKU selection + * @throws WrongParametersException upon missing parameters + * @throws AuthenticationException when the configured BKU selection service cannot be reached, + * and when the given bkuSelectionTemplateURL cannot be reached + * @throws ConfigurationException on missing configuration data + * @throws BuildException while building the HTML form + */ + public String selectBKU( + String authURL, + String target, + String oaURL, + String bkuSelectionTemplateURL, + String templateURL) + throws WrongParametersException, AuthenticationException, ConfigurationException, BuildException { + + //check if HTTP Connection may be allowed (through FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY) + String boolStr = AuthConfigurationProvider.getInstance().getGenericConfigurationParameter( + AuthConfigurationProvider.FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY); + if ((!authURL.startsWith("https:")) && (false == BoolUtils.valueOf(boolStr))) + throw new AuthenticationException("auth.07", new Object[] { authURL + "*" }); + if (isEmpty(authURL)) + throw new WrongParametersException("StartAuthentication", "AuthURL"); + if (isEmpty(oaURL)) + throw new WrongParametersException("StartAuthentication", PARAM_OA); + + ConnectionParameter bkuConnParam = + AuthConfigurationProvider.getInstance().getBKUConnectionParameter(); + if (bkuConnParam == null) + throw new ConfigurationException( + "config.08", + new Object[] { "BKUSelection/ConnectionParameter" }); + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(oaURL); + if (oaParam == null) + throw new AuthenticationException("auth.00", new Object[] { oaURL }); + + if (!oaParam.getBusinessService()) { + if (isEmpty(target)) + throw new WrongParametersException("StartAuthentication", PARAM_TARGET); + } else { + if (!isEmpty(target)) { + Logger.info("Ignoring target parameter thus application type is \"businessService\""); + } + target = null; + } + + AuthenticationSession session = newSession(); + Logger.info("MOASession " + session.getSessionID() + " angelegt"); + session.setTarget(target); + session.setOAURLRequested(oaURL); + session.setPublicOAURLPrefix(oaParam.getPublicURLPrefix()); + session.setAuthURL(authURL); + session.setTemplateURL(templateURL); + session.setBusinessService(oaParam.getBusinessService()); + String returnURL = + new DataURLBuilder().buildDataURL(authURL, REQ_START_AUTHENTICATION, session.getSessionID()); + String bkuSelectionType = AuthConfigurationProvider.getInstance().getBKUSelectionType(); + if (bkuSelectionType.equals(AuthConfigurationProvider.BKU_SELECTION_TYPE_HTMLCOMPLETE)) { + // bkuSelectionType==HTMLComplete + String redirectURL = bkuConnParam.getUrl() + "?" + AuthServlet.PARAM_RETURN + "=" + returnURL; + return redirectURL; + } else { + // bkuSelectionType==HTMLSelect + String bkuSelectTag; + try { + bkuSelectTag = readBKUSelectTag(AuthConfigurationProvider.getInstance(), bkuConnParam); + } catch (Throwable ex) { + throw new AuthenticationException( + "auth.11", + new Object[] { bkuConnParam.getUrl(), ex.toString()}, + ex); + } + String bkuSelectionTemplate = null; + // override template url by url from configuration file + if (oaParam.getBkuSelectionTemplateURL() != null) { + bkuSelectionTemplateURL = oaParam.getBkuSelectionTemplateURL(); + } + if (bkuSelectionTemplateURL != null) { + try { + bkuSelectionTemplate = new String(FileUtils.readURL(bkuSelectionTemplateURL)); + } catch (IOException ex) { + throw new AuthenticationException( + "auth.03", + new Object[] { bkuSelectionTemplateURL, ex.toString()}, + ex); + } + } + String htmlForm = + new SelectBKUFormBuilder().build(bkuSelectionTemplate, returnURL, bkuSelectTag); + return htmlForm; + } + } + /** + * Method readBKUSelectTag. + * @param conf the ConfigurationProvider + * @param connParam the ConnectionParameter for that connection + * @return String + * @throws ConfigurationException on config-errors + * @throws PKIException on PKI errors + * @throws IOException on any data error + * @throws GeneralSecurityException on security errors + */ + private String readBKUSelectTag(ConfigurationProvider conf, ConnectionParameter connParam) + throws ConfigurationException, PKIException, IOException, GeneralSecurityException { + + if (connParam.isHTTPSURL()) + return SSLUtils.readHttpsURL(conf, connParam); + else + return HTTPUtils.readHttpURL(connParam.getUrl()); + } + /** + * Processes the beginning of an authentication session. + * <ul> + * <li>Starts an authentication session</li> + * <li>Creates an <code><InfoboxReadRequest></code></li> + * <li>Creates an HTML form for querying the identity link from the + * security layer implementation. + * <br>Form parameters include + * <ul> + * <li>the <code><InfoboxReadRequest></code></li> + * <li>the data URL where the security layer implementation sends it response to</li> + * </ul> + * </ul> + * @param authURL URL of the servlet to be used as data URL + * @param target "Geschäftsbereich" of the online application requested + * @param oaURL online application URL requested + * @param bkuURL URL of the "Bürgerkartenumgebung" to be used; + * may be <code>null</code>; in this case, the default location will be used + * @param templateURL URL providing an HTML template for the HTML form generated + * @return HTML form + * @throws AuthenticationException + * @see GetIdentityLinkFormBuilder + * @see InfoboxReadRequestBuilder + */ + public String startAuthentication( + String authURL, + String target, + String oaURL, + String templateURL, + String bkuURL, + String sessionID) + throws WrongParametersException, AuthenticationException, ConfigurationException, BuildException { + + if (isEmpty(sessionID)) { + if (isEmpty(authURL)) + throw new WrongParametersException("StartAuthentication", "AuthURL"); + + //check if HTTP Connection may be allowed (through FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY) + String boolStr = + AuthConfigurationProvider.getInstance().getGenericConfigurationParameter( + AuthConfigurationProvider.FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY); + if ((!authURL.startsWith("https:")) && (false == BoolUtils.valueOf(boolStr))) + throw new AuthenticationException("auth.07", new Object[] { authURL + "*" }); + if (isEmpty(oaURL)) + throw new WrongParametersException("StartAuthentication", PARAM_OA); + } + AuthenticationSession session; + OAAuthParameter oaParam; + if (sessionID != null) { + session = getSession(sessionID); + oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter( + session.getPublicOAURLPrefix()); + } else { + oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(oaURL); + if (oaParam == null) + throw new AuthenticationException("auth.00", new Object[] { oaURL }); + if (!oaParam.getBusinessService()) { + if (isEmpty(target)) + throw new WrongParametersException("StartAuthentication", PARAM_TARGET); + } else { + target = null; + } + session = newSession(); + Logger.info("MOASession " + session.getSessionID() + " angelegt"); + session.setTarget(target); + session.setOAURLRequested(oaURL); + session.setPublicOAURLPrefix(oaParam.getPublicURLPrefix()); + session.setAuthURL(authURL); + session.setTemplateURL(templateURL); + session.setBusinessService(oaParam.getBusinessService()); + } + // BKU URL has not been set yet, even if session already exists + if (bkuURL == null) { + bkuURL = DEFAULT_BKU; + } + session.setBkuURL(bkuURL); + String infoboxReadRequest = + new InfoboxReadRequestBuilder().build(oaParam.getSlVersion12(), + oaParam.getBusinessService(), + oaParam.getIdentityLinkDomainIdentifier()); + String dataURL = + new DataURLBuilder().buildDataURL( + session.getAuthURL(), + REQ_VERIFY_IDENTITY_LINK, + session.getSessionID()); + String template = null; + // override template url by url from configuration file + if (oaParam.getTemplateURL() != null) { + templateURL = oaParam.getTemplateURL(); + } else { + templateURL = session.getTemplateURL(); + } + if (templateURL != null) { + try { + template = new String(FileUtils.readURL(templateURL)); + } catch (IOException ex) { + throw new AuthenticationException( + "auth.03", + new Object[] { templateURL, ex.toString()}, + ex); + } + } + String pushInfobox = ""; + VerifyInfoboxParameters verifyInfoboxParameters = oaParam.getVerifyInfoboxParameters(); + if (verifyInfoboxParameters != null) { + pushInfobox = verifyInfoboxParameters.getPushInfobox(); + } + String certInfoRequest = new CertInfoVerifyXMLSignatureRequestBuilder().build(oaParam.getSlVersion12()); + String certInfoDataURL = + new DataURLBuilder().buildDataURL( + session.getAuthURL(), + REQ_START_AUTHENTICATION, + session.getSessionID()); + String htmlForm = + new GetIdentityLinkFormBuilder().build( + template, + bkuURL, + infoboxReadRequest, + dataURL, + certInfoRequest, + certInfoDataURL, + pushInfobox); + return htmlForm; + } + /** + * Processes an <code><InfoboxReadResponse></code> sent by the + * security layer implementation.<br> + * <ul> + * <li>Validates given <code><InfoboxReadResponse></code></li> + * <li>Parses identity link enclosed in <code><InfoboxReadResponse></code></li> + * <li>Verifies identity link by calling the MOA SP component</li> + * <li>Checks certificate authority of identity link</li> + * <li>Stores identity link in the session</li> + * <li>Verifies all additional infoboxes returned from the BKU</li> + * <li>Creates an authentication block to be signed by the user</li> + * <li>Creates and returns a <code><CreateXMLSignatureRequest></code> + * containg the authentication block, meant to be returned to the + * security layer implementation</li> + * </ul> + * + * @param sessionID ID of associated authentication session data + * @param infoboxReadResponseParameters The parameters from the response returned from + * the BKU including the <code><InfoboxReadResponse></code> + * @return String representation of the <code><CreateXMLSignatureRequest></code> + */ + public String verifyIdentityLink(String sessionID, Map infoboxReadResponseParameters) + throws + AuthenticationException, + BuildException, + ParseException, + ConfigurationException, + ValidateException, + ServiceException { + + if (isEmpty(sessionID)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_IDENTITY_LINK, PARAM_SESSIONID}); + + String xmlInfoboxReadResponse = (String)infoboxReadResponseParameters.get(PARAM_XMLRESPONSE); + if (isEmpty(xmlInfoboxReadResponse)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_IDENTITY_LINK, PARAM_XMLRESPONSE}); + + AuthenticationSession session = getSession(sessionID); + if (session.getTimestampIdentityLink() != null) + throw new AuthenticationException("auth.01", new Object[] { sessionID }); + session.setTimestampIdentityLink(); + AuthConfigurationProvider authConf = AuthConfigurationProvider.getInstance(); + // parses the <InfoboxReadResponse> + IdentityLink identityLink = + new InfoboxReadResponseParser(xmlInfoboxReadResponse).parseIdentityLink(); + // validates the identity link + IdentityLinkValidator.getInstance().validate(identityLink); + // builds a <VerifyXMLSignatureRequest> for a call of MOA-SP + Element domVerifyXMLSignatureRequest = + new VerifyXMLSignatureRequestBuilder().build( + identityLink, + authConf.getMoaSpIdentityLinkTrustProfileID()); + + // invokes the call + Element domVerifyXMLSignatureResponse = + new SignatureVerificationInvoker().verifyXMLSignature(domVerifyXMLSignatureRequest); + // parses the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponse verifyXMLSignatureResponse = + new VerifyXMLSignatureResponseParser(domVerifyXMLSignatureResponse).parseData(); + + if (identityLink.getIdentificationType().equalsIgnoreCase(Constants.URN_PREFIX_BASEID)) { + } + + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter( + session.getPublicOAURLPrefix()); + + // if OA is type is business service the manifest validation result has to be ignored + boolean ignoreManifestValidationResult = oaParam.getBusinessService() ? true : false; + + // validates the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponseValidator.getInstance().validate( + verifyXMLSignatureResponse, + authConf.getIdentityLinkX509SubjectNames(), + VerifyXMLSignatureResponseValidator.CHECK_IDENTITY_LINK, + ignoreManifestValidationResult); + + session.setIdentityLink(identityLink); + // now validate the extended infoboxes + verifyInfoboxes(session, infoboxReadResponseParameters, !oaParam.getProvideStammzahl()); + // builds the AUTH-block + String authBlock = buildAuthenticationBlock(session); +// session.setAuthBlock(authBlock); + // builds the <CreateXMLSignatureRequest> + String[] transformsInfos = oaParam.getTransformsInfos(); + if ((transformsInfos == null) || (transformsInfos.length == 0)) { + // no OA specific transforms specified, use default ones + transformsInfos = authConf.getTransformsInfos(); + } + String createXMLSignatureRequest = + new CreateXMLSignatureRequestBuilder().build(authBlock, + oaParam.getKeyBoxIdentifier(), + transformsInfos, + oaParam.getSlVersion12()); + return createXMLSignatureRequest; + } + /** + * Builds an authentication block <code><saml:Assertion></code> from given session data. + * @param session authentication session + * + * @return <code><saml:Assertion></code> as a String + * + * @throws BuildException If an error occurs on serializing an extended SAML attribute + * to be appended to the AUTH-Block. + */ + private String buildAuthenticationBlock(AuthenticationSession session) throws BuildException { + IdentityLink identityLink = session.getIdentityLink(); + String issuer = identityLink.getName(); + String gebDat = identityLink.getDateOfBirth(); + String identificationValue = identityLink.getIdentificationValue(); + String identificationType = identityLink.getIdentificationType(); + String issueInstant = DateTimeUtils.buildDateTime(Calendar.getInstance()); + session.setIssueInstant(issueInstant); + String authURL = session.getAuthURL(); + String target = session.getTarget(); + String oaURL = session.getPublicOAURLPrefix(); + List extendedSAMLAttributes = session.getExtendedSAMLAttributesAUTH(); + String authBlock = new AuthenticationBlockAssertionBuilder().buildAuthBlock( + issuer, + issueInstant, + authURL, + target, + identificationValue, + identificationType, + oaURL, + gebDat, + extendedSAMLAttributes, + session); + + return authBlock; + } + + /** + * Verifies the infoboxes (except of the identity link infobox) returned by the BKU by + * calling appropriate validator classes. + * + * @param session The actual authentication session. + * @param infoboxReadResponseParams The parameters returned from the BKU as response + * to an infobox read request (including the infobox + * tokens to be verified). + * @param hideStammzahl Indicates whether source pins (<code>Stammzahl</code>en) + * should be hidden in any SAML attribute that may be + * returned by a validator. + * + * @throws AuthenticationException If the verification of at least one infobox fails. + * @throws ConfigurationException If the OAuthParameter cannot be extracted. + */ + private void verifyInfoboxes( + AuthenticationSession session, Map infoboxReadResponseParams, boolean hideStammzahl) + throws ValidateException, ConfigurationException + { + + AuthConfigurationProvider authConfigurationProvider = AuthConfigurationProvider.getInstance(); + // get the default VerifyInfobox parameters + Map defaultInfoboxParameters = null; + VerifyInfoboxParameters defaultVerifyInfoboxParameters = + authConfigurationProvider.getDefaultVerifyInfoboxParameters(); + if (defaultVerifyInfoboxParameters != null) { + defaultInfoboxParameters = defaultVerifyInfoboxParameters.getInfoboxParameters(); + } + // get the OA specific VerifyInfobox parameters + Map infoboxParameters = null; + OAAuthParameter oaParam = + authConfigurationProvider.getOnlineApplicationParameter(session.getPublicOAURLPrefix()); + VerifyInfoboxParameters verifyInfoboxParameters = oaParam.getVerifyInfoboxParameters(); + if (verifyInfoboxParameters != null) { + Vector authAttributes = new Vector(); + Vector oaAttributes = new Vector(); + infoboxParameters = verifyInfoboxParameters.getInfoboxParameters(); + // get the list of infobox identifiers + List identifiers = verifyInfoboxParameters.getIdentifiers(); + if (identifiers != null) { + // step through the identifiers and verify the infoboxes + Iterator it = identifiers.iterator(); + while (it.hasNext()) { + String identifier = (String)it.next(); + // get the infobox read response from the map of parameters + String infoboxReadResponse = (String)infoboxReadResponseParams.get(identifier); + // get the configuration parameters + VerifyInfoboxParameter verifyInfoboxParameter = null; + Object object = infoboxParameters.get(identifier); + // if not present, use default + if ((object == null) && (defaultInfoboxParameters != null)) { + object = defaultInfoboxParameters.get(identifier); + } + if (object != null) { + verifyInfoboxParameter = (VerifyInfoboxParameter)object; + } + if (infoboxReadResponse != null) { + if (verifyInfoboxParameter == null) { + // should not happen because of the pushinfobox mechanism; check it anyway + Logger.error("No validator for verifying \"" + identifier + "\"-infobox configured."); + throw new ValidateException("validator.41", new Object[] {identifier}); + } else { + String friendlyName = verifyInfoboxParameter.getFriendlyName(); + // get the class for validating the infobox + InfoboxValidator infoboxValidator = null; + try { + Class validatorClass = Class.forName(verifyInfoboxParameter.getValidatorClassName()); + infoboxValidator = (InfoboxValidator) validatorClass.newInstance(); + } catch (Exception e) { + Logger.error("Could not load validator class \"" + verifyInfoboxParameter.getValidatorClassName() + + "\" for \"" + identifier + "\"-infobox: " + e.getMessage()); + throw new ValidateException("validator.42", new Object[] {friendlyName}); + } + Logger.debug("Successfully loaded validator class \"" + verifyInfoboxParameter.getValidatorClassName() + + "\" for \"" + identifier + "\"-infobox."); + // parse the infobox read reponse + List infoboxTokenList = null; + try { + infoboxTokenList = + ExtendedInfoboxReadResponseParser.parseInfoboxReadResponse(infoboxReadResponse, friendlyName); + } catch (ParseException e) { + Logger.error("InfoboxReadResponse for \"" + identifier + + "\"-infobox could not be parsed successfully: " + e.getMessage()); + throw new ValidateException("validator.43", new Object[] {friendlyName}); + } + // build the parameters for validating the infobox + InfoboxValidatorParams infoboxValidatorParams = + InfoboxValidatorParamsBuilder.buildInfoboxValidatorParams( + session, verifyInfoboxParameter, infoboxTokenList, hideStammzahl); + // now validate the infobox + InfoboxValidationResult infoboxValidationResult = null; + try { + infoboxValidationResult = infoboxValidator.validate(infoboxValidatorParams); + } catch (ValidateException e) { + Logger.error("Error validating " + identifier + " infobox:" + e.getMessage()); + throw new ValidateException( + "validator.44", new Object[] {friendlyName}); + } + if (!infoboxValidationResult.isValid()) { + Logger.info("Validation of " + identifier + " infobox failed."); + throw new ValidateException( + "validator.40", new Object[] {friendlyName, infoboxValidationResult.getErrorMessage()}); + } + + Logger.info(identifier + " infobox successfully validated."); + + // get the SAML attributes to be appended to the AUTHBlock or to the final + // SAML Assertion + ExtendedSAMLAttribute[] extendedSAMLAttributes = infoboxValidationResult.getExtendedSamlAttributes(); + if (extendedSAMLAttributes != null) { + int length = extendedSAMLAttributes.length; + for (int i=0; i<length; i++) { + ExtendedSAMLAttribute samlAttribute = extendedSAMLAttributes[i]; + String name = samlAttribute.getName(); + if (name == null) { + Logger.info("The name of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is null."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName, "Name", String.valueOf((i+1)), "null"}); + } + if (name == "") { + Logger.info("The name of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is empty."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName, "Name", String.valueOf((i+1)), "leer"}); + } + if (samlAttribute.getNameSpace() == null) { + Logger.info("The namespace of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is null."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName, "Namespace", String.valueOf((i+1)), "null"}); + } + Object value = samlAttribute.getValue(); + if (value == null) { + Logger.info("The value of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is null."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName ,"Wert", String.valueOf((i+1)), "null"}); + } + if ((value instanceof String) || (value instanceof Element)) { + + switch (samlAttribute.getAddToAUTHBlock()) { + case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK: + authAttributes.add(samlAttribute); + oaAttributes.add(samlAttribute); + break; + case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY: + authAttributes.add(samlAttribute); + break; + case ExtendedSAMLAttribute.NOT_ADD_TO_AUTHBLOCK: + oaAttributes.add(samlAttribute); + break; + default: + Logger.info("Invalid return value from method \"getAddToAUTHBlock()\" (" + + samlAttribute.getAddToAUTHBlock() + ") in SAML attribute number " + + (i+1) + " for infobox " + identifier); + throw new ValidateException( + "validator.47", new Object[] {friendlyName, String.valueOf((i+1))}); + } + } else { + Logger.info("The type of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is not valid. Must be either \"java.Lang.String\"" + + " or \"org.w3c.dom.Element\""); + throw new ValidateException( + "validator.46", new Object[] {identifier, String.valueOf((i+1))}); + + } + } + + } + } + } else { + if ((verifyInfoboxParameter !=null) && (verifyInfoboxParameter.isRequired())) { + Logger.info("Infobox \"" + identifier + "\" is required, but not returned from the BKU"); + throw new ValidateException( + "validator.48", new Object[] {verifyInfoboxParameter.getFriendlyName()}); + + } + Logger.debug("Infobox \"" + identifier + "\" not returned from BKU."); + } + } + session.setExtendedSAMLAttributesAUTH(authAttributes); + session.setExtendedSAMLAttributesOA(oaAttributes); + } + } + } + + /** + * Processes a <code><CreateXMLSignatureResponse></code> sent by the + * security layer implementation.<br> + * <ul> + * <li>Validates given <code><CreateXMLSignatureResponse></code></li> + * <li>Parses <code><CreateXMLSignatureResponse></code> for error codes</li> + * <li>Parses authentication block enclosed in + * <code><CreateXMLSignatureResponse></code></li> + * <li>Verifies authentication block by calling the MOA SP component</li> + * <li>Creates authentication data</li> + * <li>Creates a corresponding SAML artifact</li> + * <li>Stores authentication data in the authentication data store + * indexed by the SAML artifact</li> + * <li>Deletes authentication session</li> + * <li>Returns the SAML artifact, encoded BASE64</li> + * </ul> + * + * @param sessionID session ID of the running authentication session + * @param xmlCreateXMLSignatureReadResponse String representation of the + * <code><CreateXMLSignatureResponse></code> + * @return SAML artifact needed for retrieving authentication data, encoded BASE64 + */ + public String verifyAuthenticationBlock( + String sessionID, + String xmlCreateXMLSignatureReadResponse) + throws + AuthenticationException, + BuildException, + ParseException, + ConfigurationException, + ServiceException, + ValidateException { + + if (isEmpty(sessionID)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_SESSIONID}); + if (isEmpty(xmlCreateXMLSignatureReadResponse)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_XMLRESPONSE}); + AuthenticationSession session = getSession(sessionID); + AuthConfigurationProvider authConf = AuthConfigurationProvider.getInstance(); + // parses <CreateXMLSignatureResponse> + CreateXMLSignatureResponse csresp = + new CreateXMLSignatureResponseParser(xmlCreateXMLSignatureReadResponse).parseResponse(); + try { + String serializedAssertion = DOMUtils.serializeNode(csresp.getSamlAssertion()); + session.setAuthBlock(serializedAssertion); + } catch (TransformerException e) { + throw new ParseException("parser.04", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_XMLRESPONSE}); + } catch (IOException e) { + throw new ParseException("parser.04", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_XMLRESPONSE}); + } + // validates <CreateXMLSignatureResponse> + new CreateXMLSignatureResponseValidator().validate(csresp, session); + // builds a <VerifyXMLSignatureRequest> for a MOA-SPSS call + String[] vtids = authConf.getMoaSpAuthBlockVerifyTransformsInfoIDs(); + String tpid = authConf.getMoaSpAuthBlockTrustProfileID(); + Element domVsreq = new VerifyXMLSignatureRequestBuilder().build(csresp, vtids, tpid); + // debug output + + // invokes the call + Element domVsresp = new SignatureVerificationInvoker().verifyXMLSignature(domVsreq); + // debug output + + // parses the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponse vsresp = new VerifyXMLSignatureResponseParser(domVsresp).parseData(); + // validates the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponseValidator.getInstance().validate( + vsresp, + null, + VerifyXMLSignatureResponseValidator.CHECK_AUTH_BLOCK, + false); + + + // TODO See Bug #144 + // Compare AuthBlock Data with information stored in session, especially date and time + + + // compares the public keys from the identityLink with the AuthBlock + VerifyXMLSignatureResponseValidator.getInstance().validateCertificate( + vsresp, + session.getIdentityLink()); + + // builds authentication data and stores it together with a SAML artifact + AuthenticationData authData = buildAuthenticationData(session, vsresp); + String samlArtifact = + new SAMLArtifactBuilder().build(session.getAuthURL(), session.getSessionID()); + storeAuthenticationData(samlArtifact, authData); + // invalidates the authentication session + sessionStore.remove(sessionID); + Logger.info( + "Anmeldedaten zu MOASession " + sessionID + " angelegt, SAML Artifakt " + samlArtifact); + return samlArtifact; + } + /** + * Builds the AuthenticationData object together with the + * corresponding <code><saml:Assertion></code> + * @param session authentication session + * @param verifyXMLSigResp VerifyXMLSignatureResponse from MOA-SP + * @return AuthenticationData object + * @throws ConfigurationException while accessing configuration data + * @throws BuildException while building the <code><saml:Assertion></code> + */ + private AuthenticationData buildAuthenticationData( + AuthenticationSession session, + VerifyXMLSignatureResponse verifyXMLSigResp) + throws ConfigurationException, BuildException { + + IdentityLink identityLink = session.getIdentityLink(); + AuthenticationData authData = new AuthenticationData(); + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter( + session.getPublicOAURLPrefix()); + boolean businessService = oaParam.getBusinessService(); + authData.setMajorVersion(1); + authData.setMinorVersion(0); + authData.setAssertionID(Random.nextRandom()); + authData.setIssuer(session.getAuthURL()); + authData.setIssueInstant(DateTimeUtils.buildDateTime(Calendar.getInstance())); + + authData.setIdentificationType(identityLink.getIdentificationType()); + authData.setGivenName(identityLink.getGivenName()); + authData.setFamilyName(identityLink.getFamilyName()); + authData.setDateOfBirth(identityLink.getDateOfBirth()); + authData.setQualifiedCertificate(verifyXMLSigResp.isQualifiedCertificate()); + authData.setPublicAuthority(verifyXMLSigResp.isPublicAuthority()); + authData.setPublicAuthorityCode(verifyXMLSigResp.getPublicAuthorityCode()); + authData.setBkuURL(session.getBkuURL()); + boolean provideStammzahl = oaParam.getProvideStammzahl(); + if (provideStammzahl) { + authData.setIdentificationValue(identityLink.getIdentificationValue()); + } + String prPerson = new PersonDataBuilder().build(identityLink, provideStammzahl); + try { + String signerCertificateBase64 = ""; + if (oaParam.getProvideCertifcate()) { + X509Certificate signerCertificate = verifyXMLSigResp.getX509certificate(); + if (signerCertificate != null) { + signerCertificateBase64 = Base64Utils.encode(signerCertificate.getEncoded()); + } else { + Logger.info("\"provideCertificate\" is \"true\", but no signer certificate available"); + } + } + authData.setSignerCertificate(signerCertificateBase64); + if (businessService) { + authData.setWBPK(identityLink.getIdentificationValue()); + } else { + // only compute bPK if online applcation is a public service + String bpkBase64 = + new BPKBuilder().buildBPK( + identityLink.getIdentificationValue(), + session.getTarget()); + authData.setBPK(bpkBase64); + } + String ilAssertion = + oaParam.getProvideIdentityLink() + ? identityLink.getSerializedSamlAssertion() + : ""; + if (!oaParam.getProvideStammzahl()) { + ilAssertion = StringUtils.replaceAll(ilAssertion, identityLink.getIdentificationValue(), ""); + } + String authBlock = oaParam.getProvideAuthBlock() ? session.getAuthBlock() : ""; + String samlAssertion = + new AuthenticationDataAssertionBuilder().build( + authData, + prPerson, + authBlock, + ilAssertion, + session.getBkuURL(), + signerCertificateBase64, + businessService, + session.getExtendedSAMLAttributesOA()); + authData.setSamlAssertion(samlAssertion); + return authData; + } catch (Throwable ex) { + throw new BuildException( + "builder.00", + new Object[] { "AuthenticationData", ex.toString()}, + ex); + } + } + /** + * Retrieves <code>AuthenticationData</code> indexed by the SAML artifact. + * The <code>AuthenticationData</code> is deleted from the store upon end of this call. + * + * @return <code>AuthenticationData</code> + */ + public AuthenticationData getAuthenticationData(String samlArtifact) + throws AuthenticationException { + String assertionHandle; + try { + assertionHandle = new SAMLArtifactParser(samlArtifact).parseAssertionHandle(); + } catch (ParseException ex) { + throw new AuthenticationException("1205", new Object[] { samlArtifact, ex.toString()}); + } + AuthenticationData authData = null; + synchronized (authenticationDataStore) { + authData = (AuthenticationData) authenticationDataStore.get(assertionHandle); + if (authData == null) { + Logger.error("Assertion not found for SAML Artifact: " + samlArtifact); + throw new AuthenticationException("1206", new Object[] { samlArtifact }); + } + authenticationDataStore.remove(assertionHandle); + } + long now = new Date().getTime(); + if (now - authData.getTimestamp().getTime() > authDataTimeOut) + throw new AuthenticationException("1207", new Object[] { samlArtifact }); + Logger.debug("Assertion delivered for SAML Artifact: " + samlArtifact); + return authData; + } + /** + * Stores authentication data indexed by the assertion handle contained in the + * given saml artifact. + * @param samlArtifact SAML artifact + * @param authData authentication data + * @throws AuthenticationException when SAML artifact is invalid + */ + private void storeAuthenticationData(String samlArtifact, AuthenticationData authData) + throws AuthenticationException { + + try { + SAMLArtifactParser parser = new SAMLArtifactParser(samlArtifact); + // check type code 0x0001 + byte[] typeCode = parser.parseTypeCode(); + if (typeCode[0] != 0 || typeCode[1] != 1) + throw new AuthenticationException("auth.06", new Object[] { samlArtifact }); + String assertionHandle = parser.parseAssertionHandle(); + synchronized (authenticationDataStore) { + Logger.debug("Assertion stored for SAML Artifact: " + samlArtifact); + authenticationDataStore.put(assertionHandle, authData); + } + } catch (AuthenticationException ex) { + throw ex; + } catch (Throwable ex) { + throw new AuthenticationException("auth.06", new Object[] { samlArtifact }); + } + } + /** + * Creates a new session and puts it into the session store. + * + * @param id Session ID + * @return AuthenticationSession created + * @exception AuthenticationException + * thrown when an <code>AuthenticationSession</code> is running + * already for the given session ID + */ + private static AuthenticationSession newSession() throws AuthenticationException { + String sessionID = Random.nextRandom(); + AuthenticationSession newSession = new AuthenticationSession(sessionID); + synchronized (sessionStore) { + AuthenticationSession session = (AuthenticationSession) sessionStore.get(sessionID); + if (session != null) + throw new AuthenticationException("auth.01", new Object[] { sessionID }); + sessionStore.put(sessionID, newSession); + } + return newSession; + } + /** + * Retrieves a session from the session store. + * + * @param id session ID + * @return <code>AuthenticationSession</code> stored with given session ID, + * <code>null</code> if session ID unknown + */ + public static AuthenticationSession getSession(String id) throws AuthenticationException { + AuthenticationSession session = (AuthenticationSession) sessionStore.get(id); + if (session == null) + throw new AuthenticationException("auth.02", new Object[] { id }); + return session; + } + /** + * Cleans up expired session and authentication data stores. + */ + public void cleanup() { + long now = new Date().getTime(); + synchronized (sessionStore) { + Set keys = new HashSet(sessionStore.keySet()); + for (Iterator iter = keys.iterator(); iter.hasNext();) { + String sessionID = (String) iter.next(); + AuthenticationSession session = (AuthenticationSession) sessionStore.get(sessionID); + if (now - session.getTimestampStart().getTime() > sessionTimeOut) { + Logger.info( + MOAIDMessageProvider.getInstance().getMessage( + "cleaner.02", + new Object[] { sessionID })); + sessionStore.remove(sessionID); + } + } + } + synchronized (authenticationDataStore) { + Set keys = new HashSet(authenticationDataStore.keySet()); + for (Iterator iter = keys.iterator(); iter.hasNext();) { + String samlArtifact = (String) iter.next(); + AuthenticationData authData = + (AuthenticationData) authenticationDataStore.get(samlArtifact); + if (now - authData.getTimestamp().getTime() > authDataTimeOut) { + Logger.info( + MOAIDMessageProvider.getInstance().getMessage( + "cleaner.03", + new Object[] { samlArtifact })); + authenticationDataStore.remove(samlArtifact); + } + } + } + } + + /** + * Sets the sessionTimeOut. + * @param seconds Time out of the session in seconds + */ + public void setSecondsSessionTimeOut(long seconds) { + sessionTimeOut = 1000 * seconds; + } + /** + * Sets the authDataTimeOut. + * @param seconds Time out for signing AuthData in seconds + */ + public void setSecondsAuthDataTimeOut(long seconds) { + authDataTimeOut = 1000 * seconds; + } + + /** + * Checks a parameter. + * @param param parameter + * @return true if the parameter is null or empty + */ + private boolean isEmpty(String param) { + return param == null || param.length() == 0; + } + + +} |