diff options
author | rudolf <rudolf@d688527b-c9ab-4aba-bd8d-4036d912da1d> | 2003-10-24 08:34:56 +0000 |
---|---|---|
committer | rudolf <rudolf@d688527b-c9ab-4aba-bd8d-4036d912da1d> | 2003-10-24 08:34:56 +0000 |
commit | dd45e938564249a5e6897bd92dd29808d8990868 (patch) | |
tree | 372d8a4b128cff09262ad09d6a4cf5765d672d61 /id.server/src/at/gv/egovernment/moa/id/auth | |
parent | 59f78a67d7357fd31de68fc2b623f95b3d654ebc (diff) | |
download | moa-id-spss-dd45e938564249a5e6897bd92dd29808d8990868.tar.gz moa-id-spss-dd45e938564249a5e6897bd92dd29808d8990868.tar.bz2 moa-id-spss-dd45e938564249a5e6897bd92dd29808d8990868.zip |
MOA-ID version 1.1 (initial)
git-svn-id: https://joinup.ec.europa.eu/svn/moa-idspss/trunk@19 d688527b-c9ab-4aba-bd8d-4036d912da1d
Diffstat (limited to 'id.server/src/at/gv/egovernment/moa/id/auth')
43 files changed, 5117 insertions, 0 deletions
diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/AuthenticationServer.java b/id.server/src/at/gv/egovernment/moa/id/auth/AuthenticationServer.java new file mode 100644 index 000000000..e9d9c7175 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/AuthenticationServer.java @@ -0,0 +1,648 @@ +package at.gv.egovernment.moa.id.auth; + +import iaik.pki.PKIException; + +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.Map; +import java.util.Set; + +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.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.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.VPKBuilder; +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.IdentityLink; +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.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.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.data.AuthenticationData; +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.DOMUtils; +import at.gv.egovernment.moa.util.DateTimeUtils; +import at.gv.egovernment.moa.util.FileUtils; + +/** + * 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 { + + if (isEmpty(authURL)) + throw new WrongParametersException("StartAuthentication", "AuthURL"); + if (isEmpty(target)) + throw new WrongParametersException("StartAuthentication", PARAM_TARGET); + if (isEmpty(oaURL)) + throw new WrongParametersException("StartAuthentication", PARAM_OA); + if (! authURL.startsWith("https:")) + throw new AuthenticationException("auth.07", null); + 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}); + 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); + 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.03", new Object[] {bkuConnParam.getUrl(), ex.toString()}, ex); + } + String bkuSelectionTemplate = null; + 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 new String(SSLUtils.readHttpsURL(conf, connParam)); + else + return new String(FileUtils.readURL(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"); + if (! authURL.startsWith("https:")) + throw new AuthenticationException("auth.07", null); + if (isEmpty(target)) + throw new WrongParametersException("StartAuthentication", PARAM_TARGET); + if (isEmpty(oaURL)) + throw new WrongParametersException("StartAuthentication", PARAM_OA); + } + AuthenticationSession session; + if (sessionID != null) + session = getSession(sessionID); + else { + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(oaURL); + if (oaParam == null) + throw new AuthenticationException("auth.00", new Object[] {oaURL}); + session = newSession(); + Logger.info("MOASession " + session.getSessionID() + " angelegt"); + session.setTarget(target); + session.setOAURLRequested(oaURL); + session.setPublicOAURLPrefix(oaParam.getPublicURLPrefix()); + session.setAuthURL(authURL); + session.setTemplateURL(templateURL); + } + String infoboxReadRequest = new InfoboxReadRequestBuilder().build(); + String dataURL = new DataURLBuilder().buildDataURL( + session.getAuthURL(), REQ_VERIFY_IDENTITY_LINK, session.getSessionID()); + String template = null; + if (session.getTemplateURL() != null) { + try { + template = new String(FileUtils.readURL(session.getTemplateURL())); + } + catch (IOException ex) { + throw new AuthenticationException("auth.03", new Object[] {session.getTemplateURL(), ex.toString()}, ex); + } + } + String certInfoRequest = new CertInfoVerifyXMLSignatureRequestBuilder().build(); + String certInfoDataURL = new DataURLBuilder().buildDataURL( + session.getAuthURL(), REQ_START_AUTHENTICATION, session.getSessionID()); + String htmlForm = new GetIdentityLinkFormBuilder().build( + template, bkuURL, infoboxReadRequest, dataURL, certInfoRequest, certInfoDataURL); + 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>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 xmlInfoboxReadResponse String representation of the + * <code><InfoboxReadResponse></code> + * @return String representation of the <code><CreateXMLSignatureRequest></code> + */ + public String verifyIdentityLink (String sessionID, String xmlInfoboxReadResponse) + throws AuthenticationException, ParseException, ConfigurationException, ValidateException, ServiceException, WrongParametersException { + + if (isEmpty(sessionID)) + throw new WrongParametersException("VerifyAuthenticationBlock", PARAM_SESSIONID); + if (isEmpty(xmlInfoboxReadResponse)) + throw new WrongParametersException("VerifyAuthenticationBlock", 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()); + // debug output + debugOutputXMLFile("VerifyIdentityLinkRequest.xml", domVerifyXMLSignatureRequest); + // invokes the call + Element domVerifyXMLSignatureResponse = new SignatureVerificationInvoker(). + verifyXMLSignature(domVerifyXMLSignatureRequest); + // parses the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponse verifyXMLSignatureResponse = + new VerifyXMLSignatureResponseParser(domVerifyXMLSignatureResponse).parseData(); + // debug output + debugOutputXMLFile("VerifyIdentityLinkResponse.xml", domVerifyXMLSignatureResponse); + // validates the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponseValidator.getInstance().validate( + verifyXMLSignatureResponse, + authConf.getIdentityLinkX509SubjectNames(), + VerifyXMLSignatureResponseValidator.CHECK_IDENTITY_LINK); + + session.setIdentityLink(identityLink); + // builds the AUTH-block + String authBlock = buildAuthenticationBlock(session); + session.setAuthBlock(authBlock); + // builds the <CreateXMLSignatureRequest> + String[] transformInfos = authConf.getTransformsInfos(); + String createXMLSignatureRequest = new CreateXMLSignatureRequestBuilder(). + build(authBlock, transformInfos); + 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 + */ + private String buildAuthenticationBlock(AuthenticationSession session) { + IdentityLink identityLink = session.getIdentityLink(); + String issuer = identityLink.getGivenName() + " " + identityLink.getFamilyName(); + String issueInstant = DateTimeUtils.buildDateTime(Calendar.getInstance()); + String authURL = session.getAuthURL(); + String target = session.getTarget(); + String oaURL = session.getPublicOAURLPrefix(); + String authBlock = new AuthenticationBlockAssertionBuilder(). + build(issuer, issueInstant, authURL, target, oaURL); + return authBlock; + } + /** + * 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, WrongParametersException { + + if (isEmpty(sessionID)) + throw new WrongParametersException("VerifyAuthenticationBlock", PARAM_SESSIONID); + if (isEmpty(xmlCreateXMLSignatureReadResponse)) + throw new WrongParametersException("VerifyAuthenticationBlock", PARAM_XMLRESPONSE); + AuthenticationSession session = getSession(sessionID); + AuthConfigurationProvider authConf = AuthConfigurationProvider.getInstance(); + // parses <CreateXMLSignatureResponse> + CreateXMLSignatureResponse csresp = + new CreateXMLSignatureResponseParser(xmlCreateXMLSignatureReadResponse).parseResponse(); + // validates <CreateXMLSignatureResponse> + new CreateXMLSignatureResponseValidator().validate(csresp, session.getTarget(), session.getPublicOAURLPrefix()); + // 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 + AuthenticationServer.debugOutputXMLFile("VerifyAuthenticationBlockRequest.xml", domVsreq); + // invokes the call + Element domVsresp = new SignatureVerificationInvoker().verifyXMLSignature(domVsreq); + // parses the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponse vsresp = new VerifyXMLSignatureResponseParser(domVsresp).parseData(); + // debug output + AuthenticationServer.debugOutputXMLFile("VerifyAuthenticationBlockResponse.xml", domVsresp); + // validates the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponseValidator.getInstance().validate( + vsresp, null,VerifyXMLSignatureResponseValidator.CHECK_AUTH_BLOCK); + // 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(); + authData.setMajorVersion(1); + authData.setMinorVersion(0); + authData.setAssertionID(Random.nextRandom()); + authData.setIssuer(session.getAuthURL()); + authData.setIssueInstant(DateTimeUtils.buildDateTime(Calendar.getInstance())); + String vpkBase64 = new VPKBuilder().buildVPK( + identityLink.getIdentificationValue(), identityLink.getDateOfBirth(), session.getTarget()); + authData.setVPK(vpkBase64); + authData.setGivenName(identityLink.getGivenName()); + authData.setFamilyName(identityLink.getFamilyName()); + authData.setDateOfBirth(identityLink.getDateOfBirth()); + authData.setQualifiedCertificate(verifyXMLSigResp.isQualifiedCertificate()); + authData.setPublicAuthority(verifyXMLSigResp.isPublicAuthority()); + authData.setPublicAuthorityCode(verifyXMLSigResp.getPublicAuthorityCode()); + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter( + session.getPublicOAURLPrefix()); + String prPerson = new PersonDataBuilder().build( + identityLink, oaParam.getProvideZMRZahl()); + + try { + String ilAssertion = + oaParam.getProvideIdentityLink() ? DOMUtils.serializeNode(identityLink.getSamlAssertion()) : ""; + String authBlock = oaParam.getProvideAuthBlock() ? session.getAuthBlock() : ""; + String samlAssertion = new AuthenticationDataAssertionBuilder().build( + authData, prPerson, authBlock, ilAssertion); + 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 sessionTimeOut time out in seconds + */ + public void setSecondsSessionTimeOut(long seconds) { + sessionTimeOut = 1000 * seconds; + } + /** + * Sets the authDataTimeOut. + * @param authDataTimeOut time out 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; + } + + /** + * Writes an XML structure to file for debugging purposes, encoding UTF-8. + * + * @param filename file name + * @param rootElem root element in DOM tree + */ + public static void debugOutputXMLFile(String filename, Element rootElem) { + if (Logger.isDebugEnabled(DEBUG_OUTPUT_HIERARCHY)) { + try { + String xmlString = new String(DOMUtils.serializeNode(rootElem)); + debugOutputXMLFile(filename, xmlString); + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + } + /** + * Writes an XML structure to file for debugging purposes, encoding UTF-8. + * + * @param filename file name + * @param xmlString XML string + */ + public static void debugOutputXMLFile(String filename, String xmlString) { + if (Logger.isDebugEnabled(DEBUG_OUTPUT_HIERARCHY)) { + try { + java.io.OutputStream fout = new java.io.FileOutputStream(filename); + byte[] xmlData = xmlString.getBytes("UTF-8"); + fout.write(xmlData); + fout.close(); + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/AuthenticationSessionCleaner.java b/id.server/src/at/gv/egovernment/moa/id/auth/AuthenticationSessionCleaner.java new file mode 100644 index 000000000..7e5ed6ec7 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/AuthenticationSessionCleaner.java @@ -0,0 +1,52 @@ +package at.gv.egovernment.moa.id.auth; + +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; + +/** + * Thread cleaning the <code>AuthenticationServer</code> session store + * and authentication data store from garbage. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationSessionCleaner implements Runnable { + + /** interval the <code>AuthenticationSessionCleaner</code> is run in */ + private static final long SESSION_CLEANUP_INTERVAL = 30 * 60; // 30 min + + /** + * Runs the thread. Cleans the <code>AuthenticationServer</code> session store + * and authentication data store from garbage, then sleeps for given interval, and restarts. + */ + public void run() { + while (true) { + try { + Logger.debug("AuthenticationSessionCleaner run"); + AuthenticationServer.getInstance().cleanup(); + } + catch (Exception e) { + Logger.error(MOAIDMessageProvider.getInstance().getMessage("cleaner.01", null), e); + } + try { + Thread.sleep(SESSION_CLEANUP_INTERVAL * 1000); + } + catch (InterruptedException e) { + } + } + } + + /** + * start the sessionCleaner + */ + public static void start() { + // start the session cleanup thread + Thread sessionCleaner = + new Thread(new AuthenticationSessionCleaner()); + sessionCleaner.setName("SessionCleaner"); + sessionCleaner.setDaemon(true); + sessionCleaner.setPriority(Thread.MIN_PRIORITY); + sessionCleaner.start(); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/MOAIDAuthConstants.java b/id.server/src/at/gv/egovernment/moa/id/auth/MOAIDAuthConstants.java new file mode 100644 index 000000000..ddba20049 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/MOAIDAuthConstants.java @@ -0,0 +1,53 @@ +package at.gv.egovernment.moa.id.auth; + +/** + * Constants used throughout moa-id-auth component. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public interface MOAIDAuthConstants { + + /** servlet parameter "Target" */ + public static final String PARAM_TARGET = "Target"; + /** servlet parameter "OA" */ + public static final String PARAM_OA = "OA"; + /** servlet parameter "bkuURI" */ + public static final String PARAM_BKU = "bkuURI"; + /** servlet parameter "BKUSelectionTemplate" */ + public static final String PARAM_BKUTEMPLATE = "BKUSelectionTemplate"; + /** servlet parameter "returnURI" */ + public static final String PARAM_RETURN = "returnURI"; + /** servlet parameter "Template" */ + public static final String PARAM_TEMPLATE = "Template"; + /** servlet parameter "MOASessionID" */ + public static final String PARAM_SESSIONID = "MOASessionID"; + /** servlet parameter "XMLResponse" */ + public static final String PARAM_XMLRESPONSE = "XMLResponse"; + /** servlet parameter "SAMLArtifact" */ + public static final String PARAM_SAMLARTIFACT = "SAMLArtifact"; + /** Request name {@link at.gv.egovernment.moa.id.auth.servlet.StartAuthenticationServlet} is mapped to */ + public static final String REQ_START_AUTHENTICATION = "StartAuthentication"; + /** Request name {@link at.gv.egovernment.moa.id.auth.servlet.VerifyIdentityLinkServlet} is mapped to */ + public static final String REQ_VERIFY_IDENTITY_LINK = "VerifyIdentityLink"; + /** Request name {@link at.gv.egovernment.moa.id.auth.servlet.VerifyAuthenticationBlockServlet} is mapped to */ + public static final String REQ_VERIFY_AUTH_BLOCK = "VerifyAuthBlock"; + /** Logging hierarchy used for controlling debug output of XML structures to files */ + public static final String DEBUG_OUTPUT_HIERARCHY = "moa.id.auth"; + /** Header Name for controlling the caching mechanism of the browser */ + public static final String HEADER_EXPIRES = "Expires"; + /** Header Value for controlling the caching mechanism of the browser */ + public static final String HEADER_VALUE_EXPIRES = "Sat, 6 May 1995 12:00:00 GMT"; + /** Header Name for controlling the caching mechanism of the browser */ + public static final String HEADER_PRAGMA = "Pragma"; + /** Header Value for controlling the caching mechanism of the browser */ + public static final String HEADER_VALUE_PRAGMA = "no-cache"; + /** Header Name for controlling the caching mechanism of the browser */ + public static final String HEADER_CACHE_CONTROL = "Cache-control"; + /** Header Value for controlling the caching mechanism of the browser */ + public static final String HEADER_VALUE_CACHE_CONTROL = "no-store, no-cache, must-revalidate"; + /** Header Value for controlling the caching mechanism of the browser */ + public static final String HEADER_VALUE_CACHE_CONTROL_IE = "post-check=0, pre-check=0"; + + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/MOAIDAuthInitializer.java b/id.server/src/at/gv/egovernment/moa/id/auth/MOAIDAuthInitializer.java new file mode 100644 index 000000000..f9bec8b76 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/MOAIDAuthInitializer.java @@ -0,0 +1,118 @@ +package at.gv.egovernment.moa.id.auth; + +import iaik.pki.PKIException; +import iaik.pki.jsse.IAIKX509TrustManager; + +import java.security.GeneralSecurityException; + +import java.io.IOException; + +import javax.net.ssl.SSLSocketFactory; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.id.iaik.config.LoggerConfigImpl; +import at.gv.egovernment.moa.id.util.AxisSecureSocketFactory; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.id.util.SSLUtils; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.logging.LoggingContext; +import at.gv.egovernment.moa.logging.LoggingContextManager; +import at.gv.egovernment.moa.spss.server.config.ConfigurationProvider; +import at.gv.egovernment.moa.spss.server.iaik.config.IaikConfigurator; + +/** + * Web application initializer + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class MOAIDAuthInitializer { + + /** a boolean identifying if the MOAIDAuthInitializer has been startet */ + public static boolean initialized = false; + + + + /** + * Initializes the web application components which need initialization: + * logging, JSSE, MOA-ID Auth configuration, Axis, session cleaner. + */ + public static void initialize() + throws ConfigurationException, PKIException, IOException, GeneralSecurityException { + if (initialized) + return; + initialized=true; + Logger.setHierarchy("moa.id.auth"); + // Restricts TLS cipher suites + System.setProperty("https.cipherSuites", "SSL_RSA_WITH_RC4_128_SHA,SSL_RSA_WITH_RC4_128_MD5,SSL_RSA_WITH_3DES_EDE_CBC_SHA"); + // load some jsse classes so that the integrity of the jars can be verified + // before the iaik jce is installed as the security provider + // this workaround is only needed when sun jsse is used in conjunction with + // iaik-jce (on jdk1.3) + ClassLoader cl = MOAIDAuthInitializer.class.getClassLoader(); + try { + cl.loadClass("javax.security.cert.Certificate"); // from jcert.jar + } + catch (ClassNotFoundException e) { + Logger.warn(MOAIDMessageProvider.getInstance().getMessage("init.01", null), e); + } + + // Initializes SSLSocketFactory store + SSLUtils.initialize(); + + // Loads the configuration + AuthConfigurationProvider authConf = AuthConfigurationProvider.reload(); + ConnectionParameter moaSPConnParam = authConf.getMoaSpConnectionParameter(); + + // If MOA-SP API calls: loads MOA-SP configuration and configures IAIK + if (moaSPConnParam == null) { + try { + LoggingContextManager.getInstance().setLoggingContext( + new LoggingContext("startup")); + ConfigurationProvider config = ConfigurationProvider.getInstance(); + new IaikConfigurator().configure(config); + } + catch (at.gv.egovernment.moa.spss.server.config.ConfigurationException ex) { + throw new ConfigurationException("config.10", new Object[] { ex.toString() }, ex); + } + } + + // Initializes IAIKX509TrustManager logging + String log4jConfigURL = System.getProperty("log4j.configuration"); + if (log4jConfigURL != null) { + IAIKX509TrustManager.initLog(new LoggerConfigImpl(log4jConfigURL)); + } + + // Initializes the Axis secure socket factory for use in calling the MOA-SP web service + if (moaSPConnParam != null && moaSPConnParam.isHTTPSURL()) { + SSLSocketFactory ssf = SSLUtils.getSSLSocketFactory(authConf, moaSPConnParam); + AxisSecureSocketFactory.initialize(ssf); + } + + // sets the authentication session and authentication data time outs + String param = authConf.getGenericConfigurationParameter(AuthConfigurationProvider.AUTH_SESSION_TIMEOUT_PROPERTY); + if (param != null) { + long sessionTimeOut = 0; + try { sessionTimeOut = new Long(param).longValue(); } + catch (NumberFormatException ex) { + Logger.error(MOAIDMessageProvider.getInstance().getMessage("config.05", new Object[] {AuthConfigurationProvider.AUTH_SESSION_TIMEOUT_PROPERTY})); + } + if (sessionTimeOut > 0) + AuthenticationServer.getInstance().setSecondsSessionTimeOut(sessionTimeOut); + } + param = authConf.getGenericConfigurationParameter(AuthConfigurationProvider.AUTH_DATA_TIMEOUT_PROPERTY); + if (param != null) { + long authDataTimeOut = 0; + try { authDataTimeOut = new Long(param).longValue(); } + catch (NumberFormatException ex) { + Logger.error(MOAIDMessageProvider.getInstance().getMessage("config.05", new Object[] {AuthConfigurationProvider.AUTH_DATA_TIMEOUT_PROPERTY})); + } + if (authDataTimeOut > 0) + AuthenticationServer.getInstance().setSecondsAuthDataTimeOut(authDataTimeOut); + } + + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/WrongParametersException.java b/id.server/src/at/gv/egovernment/moa/id/auth/WrongParametersException.java new file mode 100644 index 000000000..3ce2798ea --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/WrongParametersException.java @@ -0,0 +1,21 @@ +package at.gv.egovernment.moa.id.auth; + +import at.gv.egovernment.moa.id.MOAIDException; + +/** + * Exception thrown when the <code>AuthenticationServer</code> API is + * called with wrong parameters provided. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class WrongParametersException extends MOAIDException { + + /** + * Constructor + */ + public WrongParametersException(String call, String parameter) { + super("auth.05", new Object[] {call, parameter}); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/AuthenticationBlockAssertionBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/AuthenticationBlockAssertionBuilder.java new file mode 100644 index 000000000..4babf948c --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/AuthenticationBlockAssertionBuilder.java @@ -0,0 +1,56 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; + +import at.gv.egovernment.moa.util.Constants; + +/** + * Builder for the authentication block <code><saml:Assertion></code> + * to be included in a <code><CreateXMLSignatureResponse></code>. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationBlockAssertionBuilder implements Constants { + /** private static String nl contains the NewLine representation in Java*/ + private static String nl = "\n"; + /** private static String AUTH_BLOCK contains an XML-Auth-Block-Template */ + private static String AUTH_BLOCK = + "<saml:Assertion xmlns:saml=''" + SAML_NS_URI + "'' MajorVersion=''1'' MinorVersion=''0'' AssertionID=''any'' Issuer=''{0}'' IssueInstant=''{1}''>" + nl + + " <saml:AttributeStatement>" + nl + + " <saml:Subject>" + nl + + " <saml:NameIdentifier>{2}</saml:NameIdentifier>" + nl + + " </saml:Subject>" + nl + + " <saml:Attribute AttributeName=''Geschäftsbereich'' AttributeNamespace=''" + MOA_NS_URI + "''>" + nl + + " <saml:AttributeValue>{3}</saml:AttributeValue>" + nl + + " </saml:Attribute>" + nl + + " <saml:Attribute AttributeName=''OA'' AttributeNamespace=''" + MOA_NS_URI + "''>" + nl + + " <saml:AttributeValue>{4}</saml:AttributeValue>" + nl + + " </saml:Attribute>" + nl + + " </saml:AttributeStatement>" + nl + + "</saml:Assertion>"; + + /** + * Constructor for AuthenticationBlockAssertionBuilder. + */ + public AuthenticationBlockAssertionBuilder() { + super(); + } + /** + * Builds the authentication block <code><saml:Assertion></code>. + * + * @param issuer authentication block issuer; <code>"GivenName FamilyName"</code> + * @param issueInstant current timestamp + * @param authURL URL of MOA-ID authentication component + * @param target "Geschäftsbereich" + * @param oaURL public URL of online application requested + * @return String representation of authentication block + * <code><saml:Assertion></code> built + */ + public String build(String issuer, String issueInstant, String authURL, String target, String oaURL) { + String assertion = MessageFormat.format( + AUTH_BLOCK, new Object[] { issuer, issueInstant, authURL, target, oaURL }); + return assertion; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataAssertionBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataAssertionBuilder.java new file mode 100644 index 000000000..fd7cb1a9d --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataAssertionBuilder.java @@ -0,0 +1,114 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.util.Constants; + +/** + * Builder for the authentication data <code><saml:Assertion></code> + * to be provided by the MOA ID Auth component. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationDataAssertionBuilder implements Constants { + /** private static String NL contains the NewLine representation in Java*/ + private static final String NL = "\n"; + /** + * XML template for the <code><saml:Assertion></code> to be built + */ + private static final String AUTH_DATA = + "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + NL + + "<saml:Assertion xmlns:saml=''" + SAML_NS_URI + "'' xmlns:pr=''" + PD_NS_URI + "'' xmlns:xsi=''" + XSI_NS_URI + "''" + + " MajorVersion=''1'' MinorVersion=''0'' AssertionID=''{0}'' Issuer=''{1}'' IssueInstant=''{2}''>" + NL + + " <saml:AttributeStatement>" + NL + + " <saml:Subject>" + NL + + " <saml:NameIdentifier NameQualifier=''http://reference.e-government.gv.at/names/vpk/20020221#''>{3}</saml:NameIdentifier>" + NL + + " <saml:SubjectConfirmation>" + NL + + " <saml:ConfirmationMethod>" + MOA_NS_URI + "cm</saml:ConfirmationMethod>" + NL + + " <saml:SubjectConfirmationData>{4}{5}</saml:SubjectConfirmationData>" + NL + + " </saml:SubjectConfirmation>" + NL + + " </saml:Subject>" + NL + + " <saml:Attribute AttributeName=''PersonData'' AttributeNamespace=''" + PD_NS_URI + "''>" + NL + + " <saml:AttributeValue>{6}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL + + " <saml:Attribute AttributeName=''isQualifiedCertificate'' AttributeNamespace=''" + MOA_NS_URI + "''>" + NL + + " <saml:AttributeValue>{7}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL + + "{8}" + + " </saml:AttributeStatement>" + NL + + "</saml:Assertion>"; + /** + * XML template for the <code><saml:Attribute></code> named <code>"isPublicAuthority"</code>, + * to be inserted into the <code><saml:Assertion></code> + */ + private static final String PUBLIC_AUTHORITY_ATT = + " <saml:Attribute AttributeName=''isPublicAuthority'' AttributeNamespace=''urn:oid:1.2.40.0.10.1.1.1''>" + NL + + " <saml:AttributeValue>{0}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL; + + /** + * Constructor for AuthenticationDataAssertionBuilder. + */ + public AuthenticationDataAssertionBuilder() { + super(); + } + + /** + * Builds the authentication data <code><saml:Assertion></code>. + * + * @param authData the <code>AuthenticationData</code> to build the + * <code><saml:Assertion></code> from + * @param xmlPersonData <code>lt;pr:Person></code> element as a String + * @param xmlAuthBlock authentication block to be included in a + * <code>lt;saml:SubjectConfirmationData></code> element; may include + * the <code>"ZMR-Zahl"</code> or not; may be empty + * @param xmlIdentityLink the IdentityLink + * @return the <code><saml:Assertion></code> + * @throws BuildException if an error occurs during the build process + */ + public String build( + AuthenticationData authData, + String xmlPersonData, + String xmlAuthBlock, + String xmlIdentityLink) throws BuildException { + + String isQualifiedCertificate = authData.isQualifiedCertificate() ? "true" : "false"; + String publicAuthorityAttribute = ""; + if (authData.isPublicAuthority()) { + String publicAuthorityIdentification = authData.getPublicAuthorityCode(); + if (publicAuthorityIdentification == null) + publicAuthorityIdentification = "True"; + publicAuthorityAttribute = MessageFormat.format( + PUBLIC_AUTHORITY_ATT, new Object[] { publicAuthorityIdentification }); + } + + String assertion = MessageFormat.format(AUTH_DATA, new Object[] { + authData.getAssertionID(), + authData.getIssuer(), + authData.getIssueInstant(), + authData.getVPK(), + removeXMLDeclaration(xmlAuthBlock), + removeXMLDeclaration(xmlIdentityLink), + removeXMLDeclaration(xmlPersonData), + isQualifiedCertificate, + publicAuthorityAttribute}); + return assertion; + } + + /** + * Removes the XML declaration from an XML expression. + * @param xmlString XML expression as String + * @return XML expression, XML declaration removed + */ + private String removeXMLDeclaration(String xmlString) { + if (xmlString.startsWith("<?xml")) { + int firstElement = xmlString.indexOf("<", 1); + return xmlString.substring(firstElement); + } + else return xmlString; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/Builder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/Builder.java new file mode 100644 index 000000000..e5bbaa585 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/Builder.java @@ -0,0 +1,30 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import at.gv.egovernment.moa.id.BuildException; + +/** + * Base class for HTML/XML builders providing commonly useful functions. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class Builder { + + /** + * Replaces a special tag in an XML or HTML template by a value. + * @param htmlTemplate template + * @param tag special tag + * @param value value replacing the tag + * @return XML or HTML code, the tag replaced + * @throws BuildException when template does not contain the tag + */ + protected String replaceTag(String template, String tag, String value) throws BuildException { + int index = template.indexOf(tag); + if (index < 0) + throw new BuildException( + "builder.01", + new Object[] {"<" + tag.substring(1, tag.length() - 1) + ">"}); + return template.substring(0, index) + value + template.substring(index + tag.length()); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/CertInfoVerifyXMLSignatureRequestBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/CertInfoVerifyXMLSignatureRequestBuilder.java new file mode 100644 index 000000000..5ceb1d1c0 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/CertInfoVerifyXMLSignatureRequestBuilder.java @@ -0,0 +1,51 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.io.IOException; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.util.FileUtils; + +/** + * Builder for the <code><VerifyXMLSignatureRequest></code> structure + * used for presenting certificate information in the secure viewer of the security layer implementation. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class CertInfoVerifyXMLSignatureRequestBuilder extends Builder { + + /** special tag in the VerifyXMLRequest template to be substituted for a <code><dsig:Signature></code> */ + private static final String SIGNATURE_TAG = "<dsig:Signature/>"; + + /** + * Constructor + */ + public CertInfoVerifyXMLSignatureRequestBuilder() { + super(); + } + /** + * Builds the <code><VerifyXMLSignatureRequest></code> structure. + * @return the XML structure + * @throws BuildException + */ + public String build() throws BuildException { + String resCertInfoRequest = "resources/xmldata/CertInfoVerifyXMLSignatureRequest.xml"; + String resDsigSignature = "resources/xmldata/CertInfoDsigSignature.xml"; + String certInfoRequest; + try { + certInfoRequest = FileUtils.readResource(resCertInfoRequest, "UTF-8"); + } + catch (IOException ex) { + throw new BuildException("auth.04", new Object[] {resCertInfoRequest, ex.toString()}); + } + try { + String dsigSignature = FileUtils.readResource(resDsigSignature, "UTF-8"); + certInfoRequest = replaceTag(certInfoRequest, SIGNATURE_TAG, dsigSignature); + return certInfoRequest; + } + catch (IOException ex) { + throw new BuildException("auth.04", new Object[] {resDsigSignature, ex.toString()}); + } + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/CreateXMLSignatureRequestBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/CreateXMLSignatureRequestBuilder.java new file mode 100644 index 000000000..8693c71a9 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/CreateXMLSignatureRequestBuilder.java @@ -0,0 +1,58 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; + +import at.gv.egovernment.moa.util.Constants; + +/** + * Builder for the <code><CreateXMLSignatureRequest></code> structure + * used for requesting a signature under the authentication block from the + * security layer implementation. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class CreateXMLSignatureRequestBuilder implements Constants { + /** private static String nl contains the NewLine representation in Java*/ + private static final String nl = "\n"; + /** + * XML template for the <code><moa:CreateXMLSignatureRequest></code> to be built + */ + private static final String CREATE_XML_SIGNATURE_REQUEST = + "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + nl + + "<sl11:CreateXMLSignatureRequest xmlns:dsig=''" + DSIG_NS_URI + "'' xmlns:sl10=''" + SL10_NS_URI + "'' xmlns:sl11=''" + SL11_NS_URI + "''>" + nl + + " <sl11:KeyboxIdentifier>SecureSignatureKeypair</sl11:KeyboxIdentifier>" + nl + + " <sl11:DataObjectInfo Structure=''detached''>" + nl + + " <sl10:DataObject Reference=''''/>" + nl + + "{1}" + + " </sl11:DataObjectInfo>" + nl + + " <sl11:SignatureInfo>" + nl + + " <sl11:SignatureEnvironment>" + nl + + " <sl10:XMLContent>{0}</sl10:XMLContent>" + nl + + " </sl11:SignatureEnvironment>" + nl + + " <sl11:SignatureLocation Index=''2''>/saml:Assertion</sl11:SignatureLocation>" + nl + + " </sl11:SignatureInfo>" + nl + + "</sl11:CreateXMLSignatureRequest>"; + + /** + * Constructor for CreateXMLSignatureRequestBuilder. + */ + public CreateXMLSignatureRequestBuilder() { + super(); + } + + /** + * Builds the <code><CreateXMLSignatureRequest></code>. + * + * @param authBlock String representation of XML authentication block + * @return String representation of <code><CreateXMLSignatureRequest></code> + */ + public String build(String authBlock, String[] dsigTransformInfos) { + String dsigTransformInfosString = ""; + for (int i = 0; i < dsigTransformInfos.length; i++) + dsigTransformInfosString += dsigTransformInfos[i]; + String request = MessageFormat.format( + CREATE_XML_SIGNATURE_REQUEST, new Object[] { authBlock, dsigTransformInfosString }); + return request; + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/DataURLBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/DataURLBuilder.java new file mode 100644 index 000000000..575149d9e --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/DataURLBuilder.java @@ -0,0 +1,55 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import at.gv.egovernment.moa.id.auth.servlet.AuthServlet; + +/** + * Builds a DataURL parameter meant for the security layer implementation + * to respond to. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class DataURLBuilder { + + /** + * Constructor for DataURLBuilder. + */ + public DataURLBuilder() { + super(); + } + + /** + * Constructs a data URL for <code>VerifyIdentityLink</code> or <code>VerifyAuthenticationBlock</code>, + * including the <code>MOASessionID</code> as a parameter. + * + * @param authBaseURL base URL (context path) of the MOA ID Authentication component, + * including a trailing <code>'/'</code> + * @param authServletName request part of the data URL + * @param sessionID sessionID to be included in the dataURL + * @return String + */ + public String buildDataURL(String authBaseURL, String authServletName, String sessionID) { + String dataURL = authBaseURL + authServletName; + dataURL = addParameter(dataURL, AuthServlet.PARAM_SESSIONID, sessionID); + return dataURL; + } + + /** + * Method addParameter. + * @param urlString represents the url + * @param paramname is the parameter to be added + * @param value is the value of that parameter + * @return String + */ + private String addParameter(String urlString, String paramname, String value) { + String url = urlString; + if (paramname != null) { + if (url.indexOf("?") < 0) + url += "?"; + else + url += "&"; + url += paramname + "=" + value; + } + return url; + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/GetIdentityLinkFormBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/GetIdentityLinkFormBuilder.java new file mode 100644 index 000000000..8391fdd62 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/GetIdentityLinkFormBuilder.java @@ -0,0 +1,137 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; + +import at.gv.egovernment.moa.id.BuildException; + +/** + * Builder for HTML form requesting the security layer implementation + * to get the identity link from smartcard by a <code><InfoboxReadRequest></code>. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class GetIdentityLinkFormBuilder extends Builder { + /** private static String NL contains the NewLine representation in Java*/ + private static final String nl = "\n"; + /** special tag in the HTML template to be substituted for the BKU URL */ + private static final String BKU_TAG = "<BKU>"; + /** special tag in the HTML template to be substituted for the XML request */ + private static final String XMLREQUEST_TAG = "<XMLRequest>"; + /** special tag in the HTML template to be substituted for the data URL */ + private static final String DATAURL_TAG = "<DataURL>"; + /** special tag in the HTML template to be substituted for certificate info XML request */ + private static final String CERTINFO_XMLREQUEST_TAG = "<CertInfoXMLRequest>"; + /** special tag in the HTML template to be substituted for the certificate info data URL */ + private static final String CERTINFO_DATAURL_TAG = "<CertInfoDataURL>"; + + /** default BKU URL */ + private static final String DEFAULT_BKU = "http://localhost:3495/http-security-layer-request"; + /** default HTML template */ + private static final String DEFAULT_HTML_TEMPLATE = + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">" + nl + + "<html>" + nl + + "<head>" + nl + + "<title>Auslesen der Personenbindung</title>" + nl + + "</head>" + nl + + "<body>" + nl + + "<form name=\"GetIdentityLinkForm\"" + nl + + " action=\"" + BKU_TAG + "\"" + nl + + " method=\"post\">" + nl + + " <input type=\"hidden\" " + nl + + " name=\"XMLRequest\"" + nl + + " value=\"" + XMLREQUEST_TAG + "\"/>" + nl + + " <input type=\"hidden\" " + nl + + " name=\"DataURL\"" + nl + + " value=\"" + DATAURL_TAG + "\"/>" + nl + + " <input type=\"submit\" value=\"Auslesen der Personenbindung\"/>" + nl + + "</form>" + nl + + "<form name=\"CertificateInfoForm\"" + nl + + " action=\"" + BKU_TAG + "\"" + nl + + " method=\"post\">" + nl + + " <input type=\"hidden\" " + nl + + " name=\"XMLRequest\"" + nl + + " value=\"" + CERTINFO_XMLREQUEST_TAG + "\"/>" + nl + + " <input type=\"hidden\" " + nl + + " name=\"DataURL\"" + nl + + " value=\"" + CERTINFO_DATAURL_TAG + "\"/>" + nl + + " <input type=\"submit\" value=\"Information zu Wurzelzertifikaten\"/>" + nl + + "</form>" + nl + + "</body>" + nl + + "</html>"; + + /** + * Constructor for GetIdentityLinkFormBuilder. + */ + public GetIdentityLinkFormBuilder() { + super(); + } + /** + * Builds the HTML form, including XML Request and data URL as parameters. + * + * @param htmlTemplate template to be used for the HTML form; + * may be <code>null</code>, in this case a default layout will be produced + * @param xmlRequest XML Request to be sent as a parameter in the form + * @param bkuURL URL of the "Bürgerkartenumgebung" the form will be submitted to; + * may be <code>null</code>, in this case the default URL will be used + * @param dataURL DataURL to be sent as a parameter in the form + */ + public String build( + String htmlTemplate, String bkuURL, String xmlRequest, String dataURL, String certInfoXMLRequest, String certInfoDataURL) + throws BuildException { + + String htmlForm = htmlTemplate == null ? DEFAULT_HTML_TEMPLATE : htmlTemplate; + String bku = bkuURL == null ? DEFAULT_BKU : bkuURL; + htmlForm = replaceTag(htmlForm, BKU_TAG, bku); + htmlForm = replaceTag(htmlForm, XMLREQUEST_TAG, encodeParameter(xmlRequest)); + htmlForm = replaceTag(htmlForm, DATAURL_TAG, dataURL); + htmlForm = replaceTag(htmlForm, BKU_TAG, bku); + htmlForm = replaceTag(htmlForm, CERTINFO_XMLREQUEST_TAG, encodeParameter(certInfoXMLRequest)); + htmlForm = replaceTag(htmlForm, CERTINFO_DATAURL_TAG, certInfoDataURL); + return htmlForm; + } + /** + * Encodes a string for inclusion as a parameter in the form. + * Double quotes are substituted by <code>"&quot;"</code>. + * @param s the string to be encoded + * @return the string encoded + * @throws BuildException on any exception encountered + */ + public static String encodeParameter(String s) throws BuildException { + StringReader in = new StringReader(s); + StringWriter out = new StringWriter(); + try { + for (int ch = in.read(); ch >= 0; ch = in.read()) { + if (ch == '"') + out.write("""); + else if (ch == '<') + out.write("<"); + else if (ch == '>') + out.write(">"); + else if (ch == 'ä') + out.write("ä"); + else if (ch == 'ö') + out.write("ö"); + else if (ch == 'ü') + out.write("ü"); + else if (ch == 'Ä') + out.write("Ä"); + else if (ch == 'Ö') + out.write("Ö"); + else if (ch == 'Ü') + out.write("Ü"); + else if (ch == 'ß') + out.write("ß"); + else + out.write(ch); + } + } + catch (IOException ex) { + throw new BuildException("builder.00", new Object[] {"GetIdentityLinkForm", ex.toString()}); + } + return out.toString(); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/InfoboxReadRequestBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/InfoboxReadRequestBuilder.java new file mode 100644 index 000000000..d3e100671 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/InfoboxReadRequestBuilder.java @@ -0,0 +1,39 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import at.gv.egovernment.moa.util.Constants; + +/** + * Builder for the <code><InfoboxReadRequest></code> structure + * used for requesting the identity link from the security layer implementation. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class InfoboxReadRequestBuilder implements Constants { + + /** + * XML template for the <code><sl10:InfoboxReadRequest></code> to be built + */ + String INFOBOX_READ_REQUEST = + "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + + "<sl10:InfoboxReadRequest xmlns:sl10=\"" + SL10_NS_URI + "\">" + + "<sl10:InfoboxIdentifier>IdentityLink</sl10:InfoboxIdentifier>" + + "<sl10:BinaryFileParameters ContentIsXMLEntity=\"true\"/>" + + "</sl10:InfoboxReadRequest>"; + + /** + * Constructor for InfoboxReadRequestBuilder. + */ + public InfoboxReadRequestBuilder() { + } + /** + * Builds an <code><InfoboxReadRequest></code>. + * + * @return <code><InfoboxReadRequest></code> as String + */ + public String build() { + String request = INFOBOX_READ_REQUEST; + return request; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/PersonDataBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/PersonDataBuilder.java new file mode 100644 index 000000000..85ec1cb7f --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/PersonDataBuilder.java @@ -0,0 +1,58 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Builder for the <code>lt;pr:Person></code> element to be inserted + * in the authentication data <code>lt;saml:Assertion></code>. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class PersonDataBuilder { + + /** + * Constructor for PersonDataBuilder. + */ + public PersonDataBuilder() { + super(); + } + /** + * Builds the <code><pr:Person></code> element.<br/> + * Utilizes the parsed <code><prPerson></code> from the identity link + * and the information regarding inclusion of <code>"ZMR-Zahl"</code> in the + * <code><pr:Person></code> data. + * + * @param identityLink <code>IdentityLink</code> containing the + * attribute <code>prPerson</code> + * @param provideZMRZahl true if <code>"ZMR-Zahl"</code> is to be included; + * false otherwise + * @return the <code><pr:Person></code> element as a String + * @throws BuildException on any error + */ + public String build(IdentityLink identityLink, boolean provideZMRZahl) + throws BuildException { + + try { + Element prPerson = (Element)identityLink.getPrPerson().cloneNode(true); + if (! provideZMRZahl) { + Node prIdentification = XPathUtils.selectSingleNode(prPerson, "pr:Identification"); + prPerson.removeChild(prIdentification); + } + String xmlString = DOMUtils.serializeNode(prPerson); + return xmlString; + } + catch (Exception ex) { + throw new BuildException( + "builder.00", + new Object[] {"PersonData", ex.toString()}, + ex); + } + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/SAMLArtifactBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/SAMLArtifactBuilder.java new file mode 100644 index 000000000..27e19e830 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/SAMLArtifactBuilder.java @@ -0,0 +1,60 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.util.Base64Utils; + +/** + * Builder for the SAML artifact, as defined in the + * Browser/Artifact profile of SAML. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class SAMLArtifactBuilder { + + /** + * Constructor for SAMLArtifactBuilder. + */ + public SAMLArtifactBuilder() { + super(); + } + + /** + * Builds the SAML artifact, encoded BASE64. + * <ul> + * <li><code>TypeCode</code>: <code>0x0001</code>.</li> + * <li><code>SourceID</code>: SHA-1 hash of the authURL</li> + * <li><code>AssertionHandle</code>: SHA-1 hash of the <code>MOASessionID</code></li> + * </ul> + * @param authURL URL auf the MOA-ID Auth component to be used for construction + * of <code>SourceID</code> + * @param sessionID <code>MOASessionID</code> to be used for construction + * of <code>AssertionHandle</code> + * @return the 42-byte SAML artifact, encoded BASE64 + */ + public String build(String authURL, String sessionID) throws BuildException { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] sourceID = md.digest(authURL.getBytes()); + byte[] assertionHandle = md.digest(sessionID.getBytes()); + ByteArrayOutputStream out = new ByteArrayOutputStream(42); + out.write(0); + out.write(1); + out.write(sourceID, 0, 20); + out.write(assertionHandle, 0, 20); + byte[] samlArtifact = out.toByteArray(); + String samlArtifactBase64 = Base64Utils.encode(samlArtifact); + return samlArtifactBase64; + } + catch (Throwable ex) { + throw new BuildException( + "builder.00", + new Object[] {"SAML Artifact, MOASessionID=" + sessionID, ex.toString()}, + ex); + } + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/SAMLResponseBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/SAMLResponseBuilder.java new file mode 100644 index 000000000..a4fb5579e --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/SAMLResponseBuilder.java @@ -0,0 +1,100 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.*; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; + +/** + * Builder for the <code>lt;samlp:Response></code> used for passing + * result and status information from the <code>GetAuthenticationData</code> + * web service. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class SAMLResponseBuilder implements Constants { + /** XML - Template for samlp:Response */ + private static final String RESPONSE = + "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + + "<samlp:Response xmlns:samlp=\"" + SAMLP_NS_URI + "\" xmlns:saml=\"" + SAML_NS_URI + "\"" + + " ResponseID=\"{0}\" InResponseTo=\"{1}\" MajorVersion=\"1\" MinorVersion=\"0\" IssueInstant=\"{2}\">" + + " <samlp:Status>" + + " <samlp:StatusCode Value=\"{3}\">" + + " {4}" + + " </samlp:StatusCode>" + + " <samlp:StatusMessage>{5}</samlp:StatusMessage>" + + " </samlp:Status>" + + " {6}" + + "</samlp:Response>"; + /** XML - Template for samlp:StatusCode */ + private static final String SUB_STATUS_CODE = + "<samlp:StatusCode Value=\"{0}\"></samlp:StatusCode>"; + + /** + * Constructor for SAMLResponseBuilder. + */ + public SAMLResponseBuilder() { + super(); + } + /** + * Builds the SAML response. + * @param responseID response ID + * @param inResponseTo request ID of <code>lt;samlp:Request></code> responded to + * @param issueInstant current timestamp + * @param statusCode status code + * @param subStatusCode sub-status code refining the status code; may be <code>null</code> + * @param statusMessage status message + * @param samlAssertion SAML assertion representing authentication data + * @return SAML response as a DOM element + */ + public Element build( + String responseID, + String inResponseTo, + String issueInstant, + String statusCode, + String subStatusCode, + String statusMessage, + String samlAssertion) + throws BuildException { + + try { + String xmlSubStatusCode = + subStatusCode == null ? + "" : + MessageFormat.format(SUB_STATUS_CODE, new Object[] {subStatusCode}); + String xmlResponse = MessageFormat.format(RESPONSE, new Object[] { + responseID, + inResponseTo, + issueInstant, + statusCode, + xmlSubStatusCode, + statusMessage, + removeXMLDeclaration(samlAssertion) }); + Element domResponse = DOMUtils.parseDocument(xmlResponse, true, ALL_SCHEMA_LOCATIONS, null).getDocumentElement(); + return domResponse; + } + catch (Throwable ex) { + throw new BuildException( + "builder.00", + new Object[] { "samlp:Response", ex.toString() }, + ex); + } + } + /** + * Removes the XML declaration from an XML expression. + * @param xmlString XML expression as String + * @return XML expression, XML declaration removed + */ + private String removeXMLDeclaration(String xmlString) { + if (xmlString.startsWith("<?xml")) { + int firstElement = xmlString.indexOf("<", 1); + return xmlString.substring(firstElement); + } + else return xmlString; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/SelectBKUFormBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/SelectBKUFormBuilder.java new file mode 100644 index 000000000..363cd65a3 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/SelectBKUFormBuilder.java @@ -0,0 +1,63 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import at.gv.egovernment.moa.id.BuildException; + +/** + * Builder for the BKU selection form requesting the user to choose + * a BKU from a list. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class SelectBKUFormBuilder extends Builder { + /** private static String NL contains the NewLine representation in Java*/ + private static final String nl = "\n"; + /** special tag in the HTML template to be substituted for the form action which is + * a URL of MOA-ID Auth */ + private static final String ACTION_TAG = "<StartAuth>"; + /** special tag in the HTML template to be substituted for the <code><select;gt;</code> tag + * containing the BKU selection options */ + private static final String SELECT_TAG = "<BKUSelect>"; + /** + * Template for the default html-code to be returned as security-layer-selection to be built + */ + private static final String DEFAULT_HTML_TEMPLATE = + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">" + nl + + "<html>" + nl + + "<head>" + nl + + "<title>Auswahl der Bürgerkartenumgebung</title>" + nl + + "</head>" + nl + + "<body>" + nl + + "<form name=\"BKUSelectionForm\"" + nl + + " action=\"" + ACTION_TAG + "\"" + nl + + " method=\"post\">" + nl + + SELECT_TAG + nl + + " <input type=\"submit\" value=\"Bürgerkartenumgebung auswählen\"/>" + nl + + "</form>" + nl + + "</body>" + nl + + "</html>"; + + /** + * Constructor + */ + public SelectBKUFormBuilder() { + super(); + } + /** + * Method build. Builds the form + * @param htmlTemplate to be used + * @param startAuthenticationURL the url where the startAuthenticationServlet can be found + * @param bkuSelectTag if a special bku should be used + * @return String + * @throws BuildException on any error + */ + public String build(String htmlTemplate, String startAuthenticationURL, String bkuSelectTag) + throws BuildException { + + String htmlForm = htmlTemplate == null ? DEFAULT_HTML_TEMPLATE : htmlTemplate; + htmlForm = replaceTag(htmlForm, ACTION_TAG, startAuthenticationURL); + htmlForm = replaceTag(htmlForm, SELECT_TAG, bkuSelectTag); + return htmlForm; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/VPKBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/VPKBuilder.java new file mode 100644 index 000000000..c18156a01 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/VPKBuilder.java @@ -0,0 +1,52 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.security.MessageDigest; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.util.Base64Utils; + +/** + * Builder for the VPK, as defined in + * <code>"Ableitung f¨r die verfahrensspezifische Personenkennzeichnung"</code> + * version <code>1.0.1</code> from <code>"reference.e-government.gv.at"</code>. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class VPKBuilder { + + /** + * Builds the VPK from given parameters. + * @param identificationValue "ZMR-Zahl" + * @param dateOfBirth "Geburtsdatum" + * @param target "Verfahrensname"; will be transformed to lower case + * @return VPK in a BASE64 encoding + * @throws BuildException while building the VPK + */ + public String buildVPK(String identificationValue, String dateOfBirth, String target) + throws BuildException { + + if (identificationValue == null || identificationValue.length() == 0 + || dateOfBirth == null || dateOfBirth.length() == 0 + || target == null || target.length() == 0) + throw new BuildException( + "builder.00", + new Object[] {"VPK", + "Unvollständige Parameterangaben: identificationValue=" + identificationValue + + ",dateOfBirth=" + dateOfBirth + ",target=" + target}); + String basisbegriff = identificationValue + "+" + dateOfBirth + "+" + target.toLowerCase(); + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] hash = md.digest(basisbegriff.getBytes()); + String hashBase64 = Base64Utils.encode(hash); + return hashBase64; + } + catch (Exception ex) { + throw new BuildException( + "builder.00", + new Object[] {"VPK", ex.toString()}, + ex); + } + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/builder/VerifyXMLSignatureRequestBuilder.java b/id.server/src/at/gv/egovernment/moa/id/auth/builder/VerifyXMLSignatureRequestBuilder.java new file mode 100644 index 000000000..863162fd9 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/builder/VerifyXMLSignatureRequestBuilder.java @@ -0,0 +1,203 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.*; +import at.gv.egovernment.moa.id.auth.data.CreateXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Builder for the <code><VerifyXMLSignatureRequestBuilder></code> structure + * used for sending the DSIG-Signature of the Security Layer card for validating to MOA-SP. + * + * @author Stefan Knirsch + * @version $Id$ + */ +public class VerifyXMLSignatureRequestBuilder { + /** The MOA-Prefix */ + private static final String MOA = Constants.MOA_PREFIX + ":"; + /** the request as string */ + private String request; + /** the request as DOM-Element */ + private Element reqElem; + + /** + * Constructor for VerifyXMLSignatureRequestBuilder. + */ + public VerifyXMLSignatureRequestBuilder() {} + /** + * Builds a <code><VerifyXMLSignatureRequest></code> + * from an IdentityLink with a known trustProfileID which + * has to exist in MOA-SP + * @param idl - The IdentityLink + * @param trustProfileID - a preconfigured TrustProfile at MOA-SP + * @return Element - The complete request as Dom-Element + * @throws ParseException + */ + public Element build(IdentityLink idl, String trustProfileID) throws ParseException + { //samlAssertionObject + request = + "<?xml version='1.0' encoding='UTF-8' ?>" + + "<VerifyXMLSignatureRequest xmlns=\"http://reference.e-government.gv.at/namespace/moa/20020822#\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\" xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">" + + " <VerifySignatureInfo>" + + " <VerifySignatureEnvironment>" + + " <XMLContent xml:space=\"preserve\"/>" + + " </VerifySignatureEnvironment>" + + " <VerifySignatureLocation>//dsig:Signature</VerifySignatureLocation>" + + " </VerifySignatureInfo>" + + " <SignatureManifestCheckParams ReturnReferenceInputData=\"false\">" // True bei CreateXMLSig Überprüfung + +" <ReferenceInfo>" + " <VerifyTransformsInfoProfile/>" + // Profile ID für create (alle auslesen aus IDCOnfig VerifyAuthBlock) + +" </ReferenceInfo>" + " </SignatureManifestCheckParams>" + + // Testweise ReturnReferenceInputData = False + + +" <ReturnHashInputData/>" + + " <TrustProfileID>" + + trustProfileID + + "</TrustProfileID>" + + "</VerifyXMLSignatureRequest>"; + + try { + InputStream s = new ByteArrayInputStream(request.getBytes("UTF-8")); + reqElem = DOMUtils.parseXmlValidating(s); + + String CONTENT_XPATH = + "//" + + MOA + + "VerifyXMLSignatureRequest/" + + MOA + + "VerifySignatureInfo/" + + MOA + + "VerifySignatureEnvironment/" + + MOA + + "XMLContent"; + + Element insertTo = + (Element) XPathUtils.selectSingleNode(reqElem, CONTENT_XPATH); + insertTo.appendChild( + insertTo.getOwnerDocument().importNode(idl.getSamlAssertion(), true)); + + String SIGN_MANI_CHECK_PARAMS_XPATH = + "//" + + MOA + + "VerifyXMLSignatureRequest/" + + MOA + + "SignatureManifestCheckParams"; + insertTo = + (Element) XPathUtils.selectSingleNode( + reqElem, + SIGN_MANI_CHECK_PARAMS_XPATH); + insertTo.removeChild( + (Element) XPathUtils.selectSingleNode( + reqElem, + SIGN_MANI_CHECK_PARAMS_XPATH + "/" + MOA + "ReferenceInfo")); + Element[] dsigTransforms = idl.getDsigReferenceTransforms(); + for (int i = 0; i < 1; i++) //dsigTransforms.length; i++) + { + Element refInfo = + insertTo.getOwnerDocument().createElementNS( + Constants.MOA_NS_URI, + "ReferenceInfo"); + insertTo.appendChild(refInfo); + Element verifyTransformsInfoProfile = + insertTo.getOwnerDocument().createElementNS( + Constants.MOA_NS_URI, + "VerifyTransformsInfoProfile"); + refInfo.appendChild(verifyTransformsInfoProfile); + verifyTransformsInfoProfile.appendChild( + insertTo.getOwnerDocument().importNode(dsigTransforms[i], true)); + } + } + catch (Throwable t) { + throw new ParseException( //"VerifyXMLSignatureRequest (IdentityLink)"); + "builder.00", + new Object[] { "VerifyXMLSignatureRequest (IdentityLink)" }, + t); + } + + return reqElem; + } + + /** + * Builds a <code><VerifyXMLSignatureRequest></code> + * from an IdentityLink with a known trustProfileID which + * has to exist in MOA-SP + * @param idl - The IdentityLink + * @param trustProfileID - a preconfigured TrustProfile at MOA-SP + * @return Element - The complete request as Dom-Element + * @throws ParseException + */ + public Element build( + CreateXMLSignatureResponse csr, + String[] verifyTransformsInfoProfileID, + String trustProfileID) + throws ParseException { //samlAssertionObject + request = + "<?xml version='1.0' encoding='UTF-8' ?>" + + "<VerifyXMLSignatureRequest xmlns=\"http://reference.e-government.gv.at/namespace/moa/20020822#\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\" xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">" + + " <VerifySignatureInfo>" + + " <VerifySignatureEnvironment>" + + " <XMLContent xml:space=\"preserve\"/>" + + " </VerifySignatureEnvironment>" + + " <VerifySignatureLocation>//dsig:Signature</VerifySignatureLocation>" + + " </VerifySignatureInfo>" + + " <SignatureManifestCheckParams ReturnReferenceInputData=\"true\">" + + " <ReferenceInfo>"; + + for (int i = 0; i < verifyTransformsInfoProfileID.length; i++) { + request += " <VerifyTransformsInfoProfileID>" + + verifyTransformsInfoProfileID[i] + + "</VerifyTransformsInfoProfileID>"; + // Profile ID für create (auslesen aus IDCOnfig VerifyAuthBlock ODER per String übergeben....) + + } + + request += " </ReferenceInfo>" + + " </SignatureManifestCheckParams>" + // Testweise ReturnReferenceInputData = False + +" <ReturnHashInputData/>" + + " <TrustProfileID>" + + trustProfileID + + "</TrustProfileID>" + + "</VerifyXMLSignatureRequest>"; + + try { + // Build a DOM-Tree of the obove String + InputStream s = new ByteArrayInputStream(request.getBytes("UTF-8")); + reqElem = DOMUtils.parseXmlValidating(s); + //Insert the SAML-Assertion-Object + String CONTENT_XPATH = + "//" + + MOA + + "VerifyXMLSignatureRequest/" + + MOA + + "VerifySignatureInfo/" + + MOA + + "VerifySignatureEnvironment/" + + MOA + + "XMLContent"; + + Element insertTo = + (Element) XPathUtils.selectSingleNode(reqElem, CONTENT_XPATH); + insertTo.appendChild( + insertTo.getOwnerDocument().importNode(csr.getSamlAssertion(), true)); + + } + catch (Throwable t) { + throw new ParseException( + "builder.00", + new Object[] { "VerifyXMLSignatureRequest" }, + t); + } + + return reqElem; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/data/AuthenticationSession.java b/id.server/src/at/gv/egovernment/moa/id/auth/data/AuthenticationSession.java new file mode 100644 index 000000000..ba4a9e367 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/data/AuthenticationSession.java @@ -0,0 +1,220 @@ +package at.gv.egovernment.moa.id.auth.data; + +import java.util.Date; + + +/** + * Session data to be stored between <code>AuthenticationServer</code> API calls. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationSession { + /** + * session ID + */ + private String sessionID; + /** + * "Geschäftsbereich" the online application belongs to + */ + private String target; + /** + * public online application URL requested + */ + private String oaURLRequested; + /** + * public online application URL prefix + */ + private String oaPublicURLPrefix; + /** + * URL of MOA ID authentication component + */ + private String authURL; + /** + * HTML template URL + */ + private String templateURL; + /** + * identity link read from smartcard + */ + private IdentityLink identityLink; + /** + * authentication block to be signed by the user + */ + private String authBlock; + /** + * timestamp logging when authentication session has been created + */ + private Date timestampStart; + /** + * timestamp logging when identity link has been received + */ + private Date timestampIdentityLink; + + /** + * Constructor for AuthenticationSession. + * + * @param id Session ID + */ + public AuthenticationSession(String id) { + sessionID = id; + setTimestampStart(); + } + + /** + * Returns the identityLink. + * @return IdentityLink + */ + public IdentityLink getIdentityLink() { + return identityLink; + } + + /** + * Returns the sessionID. + * @return String + */ + public String getSessionID() { + return sessionID; + } + + /** + * Sets the identityLink. + * @param identityLink The identityLink to set + */ + public void setIdentityLink(IdentityLink identityLink) { + this.identityLink = identityLink; + } + + /** + * Sets the sessionID. + * @param sessionID The sessionID to set + */ + public void setSessionID(String sessionId) { + this.sessionID = sessionId; + } + + /** + * Returns the oaURLRequested. + * @return String + */ + public String getOAURLRequested() { + return oaURLRequested; + } + + /** + * Returns the oaURLRequested. + * @return String + */ + public String getPublicOAURLPrefix() { + return oaPublicURLPrefix; + } + + /** + * Returns the target. + * @return String + */ + public String getTarget() { + return target; + } + + /** + * Sets the oaURLRequested. + * @param oaURLRequested The oaURLRequested to set + */ + public void setOAURLRequested(String url) { + this.oaURLRequested = url; + } + + /** + * Sets the oaPublicURLPrefix + * @param url The oaPublicURLPrefix to set + */ + public void setPublicOAURLPrefix(String url) { + this.oaPublicURLPrefix = url; + } + + /** + * Sets the target. + * @param target The target to set + */ + public void setTarget(String target) { + this.target = target; + } + + /** + * Returns the authURL. + * @return String + */ + public String getAuthURL() { + return authURL; + } + + /** + * Sets the authURL. + * @param authURL The authURL to set + */ + public void setAuthURL(String authURL) { + this.authURL = authURL; + } + + /** + * Returns the authBlock. + * @return String + */ + public String getAuthBlock() { + return authBlock; + } + + /** + * Sets the authBlock. + * @param authBlock The authBlock to set + */ + public void setAuthBlock(String authBlock) { + this.authBlock = authBlock; + } + + /** + * Returns the timestampIdentityLink. + * @return Date + */ + public Date getTimestampIdentityLink() { + return timestampIdentityLink; + } + + /** + * Returns the timestampStart. + * @return Date + */ + public Date getTimestampStart() { + return timestampStart; + } + + /** + * Sets the current date as timestampIdentityLink. + */ + public void setTimestampIdentityLink() { + timestampIdentityLink = new Date(); + } + + /** + * Sets the current date as timestampStart. + */ + public void setTimestampStart() { + timestampStart = new Date(); + } + + /** + * @return template URL + */ + public String getTemplateURL() { + return templateURL; + } + + /** + * @param string the template URL + */ + public void setTemplateURL(String string) { + templateURL = string; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/data/CreateXMLSignatureResponse.java b/id.server/src/at/gv/egovernment/moa/id/auth/data/CreateXMLSignatureResponse.java new file mode 100644 index 000000000..81945f644 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/data/CreateXMLSignatureResponse.java @@ -0,0 +1,71 @@ +package at.gv.egovernment.moa.id.auth.data; + +import org.w3c.dom.Element; + +/** + * This bean saves all information of the CreateXMLSignature-Response: + * a {@link SAMLAttribute} array, the SamlAssertion-Element and the + * saml NameIdentifier + * + * @author Stefan Knirsch + * @version $Id$ + * + */ +public class CreateXMLSignatureResponse { + /** the samlNameIdentifier */ +private String samlNameIdentifier; + /** an array of saml-attributes */ +private SAMLAttribute[] samlAttributes; + /** + * the original saml:Assertion-Element + */ + private Element samlAssertion; +/** + * Returns the samlAssertion. + * @return Element + */ +public Element getSamlAssertion() { + return samlAssertion; +} + +/** + * Returns the samlAttribute. + * @return SAMLAttribute[] + */ +public SAMLAttribute[] getSamlAttributes() { + return samlAttributes; +} + +/** + * Returns the samlNameIdentifier. + * @return String + */ +public String getSamlNameIdentifier() { + return samlNameIdentifier; +} + +/** + * Sets the samlAssertion. + * @param samlAssertion The samlAssertion to set + */ +public void setSamlAssertion(Element samlAssertion) { + this.samlAssertion = samlAssertion; +} + +/** + * Sets the samlAttribute. + * @param samlAttribute The samlAttribute to set + */ +public void setSamlAttributes(SAMLAttribute[] samlAttributes) { + this.samlAttributes = samlAttributes; +} + +/** + * Sets the samlNameIdentifier. + * @param samlNameIdentifier The samlNameIdentifier to set + */ +public void setSamlNameIdentifier(String samlNameIdentifier) { + this.samlNameIdentifier = samlNameIdentifier; +} + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/data/IdentityLink.java b/id.server/src/at/gv/egovernment/moa/id/auth/data/IdentityLink.java new file mode 100644 index 000000000..e2ad2625a --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/data/IdentityLink.java @@ -0,0 +1,189 @@ +package at.gv.egovernment.moa.id.auth.data; + +import java.security.PublicKey; + +import org.w3c.dom.Element; + + +/** + * Data contained in an identity link issued by BMI, relevant to the MOA ID component. + * <br><code>"IdentityLink"</code> is the translation of <code>"Personenbindung"</code>. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class IdentityLink { + /** + * <code>"identificationValue"</code> is the translation of <code>"ZMR-Zahl"</code>. + */ + private String identificationValue; + /** + * first name + */ + private String givenName; + /** + * family name + */ + private String familyName; + /** + * date of birth + */ + private String dateOfBirth; + /** + * the original saml:Assertion-Element + */ + private Element samlAssertion; + /** + * Element /saml:Assertion/saml:AttributeStatement/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData/pr:Person + */ + private Element prPerson; + /** + * we need for each dsig:Reference Element all + * transformation elements + */ + private Element[] dsigReferenceTransforms; + + + /** + * we need all public keys stored in + * the identity link + */ + private PublicKey[] publicKey; + + /** + * Constructor for IdentityLink + */ + public IdentityLink() { + } + + /** + * Returns the dateOfBirth. + * @return Calendar + */ + public String getDateOfBirth() { + return dateOfBirth; + } + + /** + * Returns the familyName. + * @return String + */ + public String getFamilyName() { + return familyName; + } + + /** + * Returns the givenName. + * @return String + */ + public String getGivenName() { + return givenName; + } + + /** + * Returns the identificationValue. + * <code>"identificationValue"</code> is the translation of <code>"ZMR-Zahl"</code>. + * @return String + */ + public String getIdentificationValue() { + return identificationValue; + } + + /** + * Sets the dateOfBirth. + * @param dateOfBirth The dateOfBirth to set + */ + public void setDateOfBirth(String dateOfBirth) { + this.dateOfBirth = dateOfBirth; + } + + /** + * Sets the familyName. + * @param familyName The familyName to set + */ + public void setFamilyName(String familyName) { + this.familyName = familyName; + } + + /** + * Sets the givenName. + * @param givenName The givenName to set + */ + public void setGivenName(String givenName) { + this.givenName = givenName; + } + + /** + * Sets the identificationValue. + * <code>"identificationValue"</code> is the translation of <code>"ZMR-Zahl"</code>. + * @param identificationValue The identificationValue to set + */ + public void setIdentificationValue(String identificationValue) { + this.identificationValue = identificationValue; + } + + /** + * Returns the samlAssertion. + * @return Element + */ + public Element getSamlAssertion() { + return samlAssertion; + } + + /** + * Sets the samlAssertion. + * @param samlAssertion The samlAssertion to set + */ + public void setSamlAssertion(Element samlAssertion) { + this.samlAssertion = samlAssertion; + } + + /** + * Returns the dsigReferenceTransforms. + * @return Element[] + */ + public Element[] getDsigReferenceTransforms() { + return dsigReferenceTransforms; + } + + /** + * Sets the dsigReferenceTransforms. + * @param dsigReferenceTransforms The dsigReferenceTransforms to set + */ + public void setDsigReferenceTransforms(Element[] dsigReferenceTransforms) { + this.dsigReferenceTransforms = dsigReferenceTransforms; + } + + /** + * Returns the publicKey. + * @return PublicKey[] + */ + public PublicKey[] getPublicKey() { + return publicKey; + } + + /** + * Sets the publicKey. + * @param publicKey The publicKey to set + */ + public void setPublicKey(PublicKey[] publicKey) { + this.publicKey = publicKey; + } + + /** + * Returns the prPerson. + * @return Element + */ + public Element getPrPerson() { + return prPerson; + } + + /** + * Sets the prPerson. + * @param prPerson The prPerson to set + */ + public void setPrPerson(Element prPerson) { + this.prPerson = prPerson; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/data/SAMLAttribute.java b/id.server/src/at/gv/egovernment/moa/id/auth/data/SAMLAttribute.java new file mode 100644 index 000000000..c787b2a81 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/data/SAMLAttribute.java @@ -0,0 +1,78 @@ +package at.gv.egovernment.moa.id.auth.data; + +/** + * This bean saves all data of a single SAMLAttribute: + * the name, value and namespace + * + * @author Stefan Knirsch + * @version $Id$ + * + */ +public class SAMLAttribute { +/** the name to be stored */ +private String name; +/** the namespace to be stored */ +private String namespace; +/** the value to be stored */ +private String value; + + /** + * Constructor for SAMLAttribute. + */ + public SAMLAttribute(String name, String namespace, String value) { + + this.name = name; + this.namespace = namespace; + this.value = value; + + } + +/** + * Returns the name. + * @return String + */ +public String getName() { + return name; +} + +/** + * Returns the namespace. + * @return String + */ +public String getNamespace() { + return namespace; +} + +/** + * Returns the value. + * @return String + */ +public String getValue() { + return value; +} + +/** + * Sets the name. + * @param name The name to set + */ +public void setName(String name) { + this.name = name; +} + +/** + * Sets the namespace. + * @param namespace The namespace to set + */ +public void setNamespace(String namespace) { + this.namespace = namespace; +} + +/** + * Sets the value. + * @param value The value to set + */ +public void setValue(String value) { + this.value = value; +} + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/data/VerifyXMLSignatureResponse.java b/id.server/src/at/gv/egovernment/moa/id/auth/data/VerifyXMLSignatureResponse.java new file mode 100644 index 000000000..8233d1478 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/data/VerifyXMLSignatureResponse.java @@ -0,0 +1,177 @@ +package at.gv.egovernment.moa.id.auth.data; + +import iaik.x509.X509Certificate; + +/** + * This bean saves all information of the MOA-SP-Answer + * after the verification of any signature + * + * @author Stefan Knirsch + * @version $Id$ + * + */ +public class VerifyXMLSignatureResponse { + /** The xmlDsigSubjectName to be stored */ + private String xmlDsigSubjectName; + /** The signatureCheckCode to be stored */ + private int signatureCheckCode; + /** The xmlDSIGManifestCheckCode to be stored */ + private int xmlDSIGManifestCheckCode; + /** The xmlDSIGManigest to be stored */ + private boolean xmlDSIGManigest; + /** The certificateCheckCode to be stored */ + private int certificateCheckCode; + /** The publicAuthority to be stored */ + private boolean publicAuthority; + /** The publicAuthorityCode to be stored */ + private String publicAuthorityCode; + /** The qualifiedCertificate to be stored */ + private boolean qualifiedCertificate; + /** The x509certificate to be stored */ + private X509Certificate x509certificate; + + /** + * Returns the certificateCheckCode. + * @return int + */ + public int getCertificateCheckCode() { + return certificateCheckCode; + } + + /** + * Returns the signatureCheckCode. + * @return int + */ + public int getSignatureCheckCode() { + return signatureCheckCode; + } + + /** + * Returns the xmlDSIGManifestCheckCode. + * @return int + */ + public int getXmlDSIGManifestCheckCode() { + return xmlDSIGManifestCheckCode; + } + + /** + * Returns the xmlDsigSubjectName. + * @return String + */ + public String getXmlDsigSubjectName() { + return xmlDsigSubjectName; + } + + /** + * Sets the certificateCheckCode. + * @param certificateCheckCode The certificateCheckCode to set + */ + public void setCertificateCheckCode(int certificateCheckCode) { + this.certificateCheckCode = certificateCheckCode; + } + + /** + * Sets the signatureCheckCode. + * @param signatureCheckCode The signatureCheckCode to set + */ + public void setSignatureCheckCode(int signatureCheckCode) { + this.signatureCheckCode = signatureCheckCode; + } + + /** + * Sets the xmlDSIGManifestCheckCode. + * @param xmlDSIGManifestCheckCode The xmlDSIGManifestCheckCode to set + */ + public void setXmlDSIGManifestCheckCode(int xmlDSIGManifestCheckCode) { + this.xmlDSIGManifestCheckCode = xmlDSIGManifestCheckCode; + } + + /** + * Sets the xmlDsigSubjectName. + * @param xmlDsigSubjectName The xmlDsigSubjectName to set + */ + public void setXmlDsigSubjectName(String xmlDsigSubjectName) { + this.xmlDsigSubjectName = xmlDsigSubjectName; + } + + /** + * Returns the publicAuthorityCode. + * @return int + */ + public String getPublicAuthorityCode() { + return publicAuthorityCode; + } + + /** + * Sets the publicAuthorityCode. + * @param publicAuthorityCode The publicAuthorityCode to set + */ + public void setPublicAuthorityCode(String publicAuthorityCode) { + this.publicAuthorityCode = publicAuthorityCode; + } + + /** + * Returns the qualifiedCertificate. + * @return boolean + */ + public boolean isQualifiedCertificate() { + return qualifiedCertificate; + } + + /** + * Returns the x509certificate. + * @return X509Certificate + */ + public X509Certificate getX509certificate() { + return x509certificate; + } + + /** + * Sets the qualifiedCertificate. + * @param qualifiedCertificate The qualifiedCertificate to set + */ + public void setQualifiedCertificate(boolean qualifiedCertificate) { + this.qualifiedCertificate = qualifiedCertificate; + } + + /** + * Sets the x509certificate. + * @param x509certificate The x509certificate to set + */ + public void setX509certificate(X509Certificate x509certificate) { + this.x509certificate = x509certificate; + } + + /** + * Returns the xmlDSIGManigest. + * @return boolean + */ + public boolean isXmlDSIGManigest() { + return xmlDSIGManigest; + } + + /** + * Sets the xmlDSIGManigest. + * @param xmlDSIGManigest The xmlDSIGManigest to set + */ + public void setXmlDSIGManigest(boolean xmlDSIGManigest) { + this.xmlDSIGManigest = xmlDSIGManigest; + } + + /** + * Returns the publicAuthority. + * @return boolean + */ + public boolean isPublicAuthority() { + return publicAuthority; + } + + /** + * Sets the publicAuthority. + * @param publicAuthority The publicAuthority to set + */ + public void setPublicAuthority(boolean publicAuthority) { + this.publicAuthority = publicAuthority; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/invoke/SignatureVerificationInvoker.java b/id.server/src/at/gv/egovernment/moa/id/auth/invoke/SignatureVerificationInvoker.java new file mode 100644 index 000000000..8faa69260 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/invoke/SignatureVerificationInvoker.java @@ -0,0 +1,91 @@ +package at.gv.egovernment.moa.id.auth.invoke; + +import java.util.Vector; + +import javax.xml.namespace.QName; +import javax.xml.rpc.Call; +import javax.xml.rpc.Service; +import javax.xml.rpc.ServiceFactory; + +import org.apache.axis.message.SOAPBodyElement; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.ServiceException; +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.spss.api.SignatureVerificationService; +import at.gv.egovernment.moa.spss.api.xmlbind.VerifyXMLSignatureRequestParser; +import at.gv.egovernment.moa.spss.api.xmlbind.VerifyXMLSignatureResponseBuilder; +import at.gv.egovernment.moa.spss.api.xmlverify.VerifyXMLSignatureRequest; +import at.gv.egovernment.moa.spss.api.xmlverify.VerifyXMLSignatureResponse; + +/** + * Invoker of the <code>SignatureVerification</code> web service of MOA-SPSS.<br> + * Either invokes the web service, or calls the corresponding API, depending on configuration data. + * + * @author Stefan Knirsch + * @version $Id$ + */ +public class SignatureVerificationInvoker { + /** This QName Object identifies the SignatureVerification endpoint of the web service */ + private static final QName SERVICE_QNAME = new QName("SignatureVerification"); + + /** + * Method verifyXMLSignature. + * @param request to be sent + * @return Element with the answer + * @throws ServiceException if an error occurs + */ + public Element verifyXMLSignature(Element request) throws ServiceException { + return doCall(SERVICE_QNAME, request); + } + + /** + * Method doCall. + * @param serviceName the name of the service + * @param request the request to be sent + * @return Element the answer + * @throws ServiceException if an error occurs + */ + protected Element doCall(QName serviceName, Element request) throws ServiceException { + ConnectionParameter authConnParam = null; + try { + Service service = ServiceFactory.newInstance().createService(serviceName); + Call call = service.createCall(); + SOAPBodyElement body = new SOAPBodyElement(request); + SOAPBodyElement[] params = new SOAPBodyElement[] { body }; + Vector responses; + SOAPBodyElement response; + + String endPoint; + AuthConfigurationProvider authConfigProvider = AuthConfigurationProvider.getInstance(); + authConnParam = authConfigProvider.getMoaSpConnectionParameter(); + + //If the ConnectionParameter do NOT exist, we try to get the api to work.... + if (authConnParam != null) { + endPoint = authConnParam.getUrl(); + call.setTargetEndpointAddress(endPoint); + responses = (Vector) call.invoke(serviceName, params); + response = (SOAPBodyElement) responses.get(0); + return response.getAsDOM(); + } + else { + SignatureVerificationService svs = SignatureVerificationService.getInstance(); + VerifyXMLSignatureRequest vsrequest = new VerifyXMLSignatureRequestParser().parse(request); + VerifyXMLSignatureResponse vsresponse = svs.verifyXMLSignature(vsrequest); + + Document result = new VerifyXMLSignatureResponseBuilder().build(vsresponse); + Logger.setHierarchy("moa.id.auth"); + return result.getDocumentElement(); + } + } + catch (Exception ex) { + if (authConnParam != null) + throw new ServiceException("service.00", new Object[] { ex.toString()}, ex); + else + throw new ServiceException("service.03", new Object[] { ex.toString()}, ex); + } + } +}
\ No newline at end of file diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/parser/CreateXMLSignatureResponseParser.java b/id.server/src/at/gv/egovernment/moa/id/auth/parser/CreateXMLSignatureResponseParser.java new file mode 100644 index 000000000..1079a48de --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/parser/CreateXMLSignatureResponseParser.java @@ -0,0 +1,140 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.traversal.NodeIterator; + +import at.gv.egovernment.moa.id.*; +import at.gv.egovernment.moa.id.auth.data.CreateXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.data.SAMLAttribute; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Parses an <code><InfoboxReadResponse></code> returned from + * the security layer + * + * @author Stefan Knirsch + * @version $Id$ + */ + +public class CreateXMLSignatureResponseParser { + // + // XPath namespace prefix shortcuts + // + /** Xpath prefix for reaching SecurityLayer 1.0 Namespaces */ + private static final String SL10 = Constants.SL10_PREFIX + ":"; + /** Xpath prefix for reaching SecurityLayer 1.1 Namespaces */ + private static final String SL11 = Constants.SL11_PREFIX + ":"; + /** Xpath prefix for reaching SAML Namespaces */ + private static final String SAML = Constants.SAML_PREFIX + ":"; + /** Xpath prefix for reaching XML-DSIG Namespaces */ + private static final String DSIG = Constants.DSIG_PREFIX + ":"; + /** Xpath expression to the root element */ + private static final String ROOT = "/" + SL11 + "CreateXMLSignatureResponse/"; + /** Xpath expression to the SAML:Assertion element */ + private static final String SAML_ASSERTION_XPATH = ROOT + SAML + "Assertion"; + /** Xpath expression to the SAML:NameIdentifier element */ + private static final String SAML_SUBJECT_NAME_IDENTIFIER_XPATH = SAML_ASSERTION_XPATH + "/" + SAML + "AttributeStatement/" + SAML + "Subject/" + SAML + "NameIdentifier"; + /** Xpath expression to the AttributeStatement element */ + private static final String SAML_ATTRIBUTE_XPATH = SAML_ASSERTION_XPATH + "/" + SAML + "AttributeStatement/" + SAML + "Attribute"; + /** Xpath expression to the AttributeValue element */ + private static final String SAML_ATTRIBUTE_VALUE_XPATH = SAML + "AttributeValue"; + + /** This is the root element of the XML-Document provided by the Security Layer Card */ + private Element sigResponse; + + /** + * Constructor for CreateXMLSignatureResponseParser. + * A DOM-representation of the incoming String will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as String + * @throws AuthenticationException if any authentication error occurs + * @throws ParseException if an element cannot be parsed + */ + public CreateXMLSignatureResponseParser(String xmlResponse) throws AuthenticationException, ParseException { + ErrorResponseParser erp = new ErrorResponseParser(xmlResponse); + if (erp.getErrorCode() != null) { + throw new AuthenticationException("auth.08", new Object[] { erp.getErrorCode(), erp.getErrorInfo()}); + } + + try { + + InputStream s = new ByteArrayInputStream(xmlResponse.getBytes("UTF-8")); + sigResponse = DOMUtils.parseXmlValidating(s); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Constructor for CreateXMLSignatureResponseParser. + * A DOM-representation of the incoming Inputstream will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as InputStream + * @throws AuthenticationException if any Authentication error occurs + * @throws ParseException if an element cannot be parsed + */ + public CreateXMLSignatureResponseParser(InputStream is) throws AuthenticationException, ParseException { + + ErrorResponseParser erp = new ErrorResponseParser(is); + if (erp.getErrorCode() != null) { + throw new AuthenticationException("auth.08", new Object[] { erp.getErrorCode(), erp.getErrorInfo()}); + } + + try { + + sigResponse = DOMUtils.parseXmlValidating(is); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Constructor for CreateXMLSignatureResponseParser. + * The incoming Element will be used for further operations + * @param xmlResponse <code><InfoboxReadResponse></code> as InputStream + */ + public CreateXMLSignatureResponseParser(Element xmlResponse) { + sigResponse = xmlResponse; + + } + + /** + * Parses the identity link from <code><InfoboxReadResponse></code> + * @return Identity link + * @throws ParseException + */ + + public CreateXMLSignatureResponse parseResponse() throws ParseException { + CreateXMLSignatureResponse cResp; + try { + + cResp = new CreateXMLSignatureResponse(); + cResp.setSamlNameIdentifier(XPathUtils.getElementValue(sigResponse, SAML_SUBJECT_NAME_IDENTIFIER_XPATH, null)); + cResp.setSamlAssertion((Element) XPathUtils.selectSingleNode(sigResponse, SAML_ASSERTION_XPATH)); + NodeIterator attrIter = XPathUtils.selectNodeIterator(sigResponse, SAML_ATTRIBUTE_XPATH); + Element samlAttr; + List samlAttributes = new ArrayList(); + while ((samlAttr = (Element) attrIter.nextNode()) != null) { + String attrName = XPathUtils.getAttributeValue(samlAttr, "@AttributeName", ""); + String attrNamespace = XPathUtils.getAttributeValue(samlAttr, "@AttributeNamespace", ""); + String attrValue = XPathUtils.getElementValue(samlAttr, SAML_ATTRIBUTE_VALUE_XPATH, ""); + samlAttributes.add(new SAMLAttribute(attrName, attrNamespace, attrValue)); + } + SAMLAttribute[] result = new SAMLAttribute[samlAttributes.size()]; + samlAttributes.toArray(result); + cResp.setSamlAttributes(result); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + return cResp; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/parser/ECDSAKeyValueConverter.java b/id.server/src/at/gv/egovernment/moa/id/auth/parser/ECDSAKeyValueConverter.java new file mode 100644 index 000000000..c28cfac76 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/parser/ECDSAKeyValueConverter.java @@ -0,0 +1,350 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import iaik.security.ecc.ecdsa.ECDSAParameter; +import iaik.security.ecc.ecdsa.ECPublicKey; +import iaik.security.ecc.math.ecgroup.ECGroupFactory; +import iaik.security.ecc.math.ecgroup.ECPoint; +import iaik.security.ecc.math.ecgroup.EllipticCurve; +import iaik.security.ecc.math.ecgroup.ProjectiveCoordinate; +import iaik.security.ecc.math.field.Field; +import iaik.security.ecc.math.field.FieldElement; +import iaik.security.ecc.math.field.FieldFactory; +import iaik.security.ecc.math.field.Value; +import iaik.security.ecc.parameter.ECCParameterFactory; +import iaik.security.ecc.spec.ECCParameterSpec; +import java.math.BigInteger; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Vector; +import java.net.URL; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import at.gv.egovernment.moa.util.Constants; + +/** + * @author Stefan Knirsch + * @version $Id$ + * + */ +public class ECDSAKeyValueConverter +{ + /** Namespaces */ + public static final String NAMESPACE_XSI = "http://www.w3.org/2001/XMLSchema-instance"; + + /** + * Method element2ECDSAPublicKey. + * @param keyValueElem a DomElement containing an ECDSA Public Key + * @return PublicKey a java.security.publicKey - object + * @throws Exception on any error + */ + + public static PublicKey element2ECDSAPublicKey(Element keyValueElem) throws Exception + { + String ecdsaNS = Constants.ECDSA_NS_URI; + // Domain parameters + Element domainParams = getChildElement(keyValueElem, ecdsaNS, "DomainParameters", 1); + if (domainParams == null) throw new Exception("Domain parameters must not be implicit."); + Element namedCurve = getChildElement(domainParams, ecdsaNS, "NamedCurve", 1); + ECCParameterSpec eccParameterSpec; + if (namedCurve != null) + { + URL curveNameURN = new URL(namedCurve.getAttributeNS(null, "URN")); + ECCParameterFactory eccParamFactory = ECCParameterFactory.getInstance(); + eccParameterSpec = eccParamFactory.getParameterByOID(curveNameURN.getPath().substring(4)); + } + else + { + Element excplicitParams = getChildElement(domainParams, ecdsaNS, "ExplicitParams", 1); + Element fieldParams = getChildElement(excplicitParams, ecdsaNS, "FieldParams", 1); + Element curveParams = getChildElement(excplicitParams, ecdsaNS, "CurveParams", 1); + Element basePointParams = getChildElement(excplicitParams, ecdsaNS, "BasePointParams", 1); + + // Field parameters + String fieldParamsTypeStr = fieldParams.getAttributeNS(NAMESPACE_XSI, "type"); + String ecdsaNSPrefix = getECDSANSPrefix(fieldParams); + BigInteger p = null; + int fieldParamsType = 0; + final int FIELD_TYPE_PRIME = 1; + final int FIELD_TYPE_TNB = 2; + final int FIELD_TYPE_PNB = 3; + int m = -1, k = -1, k1 = -1, k2 = -1, k3 = -1; + if (fieldParamsTypeStr.equals(ecdsaNSPrefix + ":PrimeFieldParamsType")) + { + fieldParamsType = FIELD_TYPE_PRIME; + String pStr = getChildElementText(fieldParams, ecdsaNS, "P", 1); + p = new BigInteger(pStr, 10); + } + else if (fieldParamsTypeStr.equals(ecdsaNSPrefix + ":TnBFieldParamsType")) + { + fieldParamsType = FIELD_TYPE_TNB; + String mStr = getChildElementText(fieldParams, ecdsaNS, "M", 1); + m = Integer.parseInt(mStr); + String kStr = getChildElementText(fieldParams, ecdsaNS, "K", 1); + k = Integer.parseInt(kStr); + } + else if (fieldParamsTypeStr.equals(ecdsaNSPrefix + ":PnBFieldParamsType")) + { + fieldParamsType = FIELD_TYPE_PNB; + String mStr = getChildElementText(fieldParams, ecdsaNS, "M", 1); + m = Integer.parseInt(mStr); + String k1Str = getChildElementText(fieldParams, ecdsaNS, "K1", 1); + k1 = Integer.parseInt(k1Str); + String k2Str = getChildElementText(fieldParams, ecdsaNS, "K2", 1); + k2 = Integer.parseInt(k2Str); + String k3Str = getChildElementText(fieldParams, ecdsaNS, "K3", 1); + k3 = Integer.parseInt(k3Str); + } + else throw new Exception("Unknown field parameters."); + + // Curve parameters + Element aElem = getChildElement(curveParams, ecdsaNS, "A", 1); + String aStr = aElem.getAttributeNS(null, "Value"); + Element bElem = getChildElement(curveParams, ecdsaNS, "B", 1); + String bStr = bElem.getAttributeNS(null, "Value"); + String seedStr = getChildElementText(curveParams, ecdsaNS, "Seed", 1); + BigInteger seed = (seedStr != null) ? new BigInteger(seedStr, 10) : null; + + // Base point parameters + Element basePoint = getChildElement(basePointParams, ecdsaNS, "BasePoint", 1); + Element basePointXElem = getChildElement(basePoint, ecdsaNS, "X", 1); + String basePointXStr = basePointXElem.getAttributeNS(null, "Value"); + Element basePointYElem = getChildElement(basePoint, ecdsaNS, "Y", 1); + String basePointYStr = basePointYElem.getAttributeNS(null, "Value"); + String orderStr = getChildElementText(basePointParams, ecdsaNS, "Order", 1); + BigInteger order = new BigInteger(orderStr, 10); + String cofactorStr = getChildElementText(basePointParams, ecdsaNS, "Cofactor", 1); + BigInteger cofactor = (cofactorStr != null) ? new BigInteger(cofactorStr, 10) : null; + + if (fieldParamsType == FIELD_TYPE_PRIME) + { + BigInteger a = new BigInteger(aStr, 10); + BigInteger b = new BigInteger(bStr, 10); + BigInteger basePointX = new BigInteger(basePointXStr, 10); + BigInteger basePointY = new BigInteger(basePointYStr, 10); + eccParameterSpec = new ECCParameterSpec(p, cofactor, order, seed, null, a, b, basePointX, + basePointY, null); + } + else + { + int[] irreducible = new int[m/32 + ((m % 32 != 0) ? 1 : 0)]; + if (fieldParamsType == FIELD_TYPE_TNB) + { + irreducible[m/32] = 1 << m % 32; + irreducible[k/32] += 1 << k % 32; + irreducible[0] += 1; + } + else + { + irreducible[m/32] = 1 << m % 32; + irreducible[k3/32] += 1 << k3 % 32; + irreducible[k2/32] += 1 << k2 % 32; + irreducible[k1/32] += 1 << k1 % 32; + irreducible[0] += 1; + } + eccParameterSpec = new ECCParameterSpec(irreducible, cofactor, order, octetString2IntArray(aStr), + octetString2IntArray(bStr), octetString2IntArray(basePointXStr), + octetString2IntArray(basePointYStr), null); + } + } + + // Public key + Element publicKeyElem = getChildElement(keyValueElem, ecdsaNS, "PublicKey", 1); + Element publicKeyXElem = getChildElement(publicKeyElem, ecdsaNS, "X", 1); + String publicKeyXStr = publicKeyXElem.getAttributeNS(null, "Value"); + Element publicKeyYElem = getChildElement(publicKeyElem, ecdsaNS, "Y", 1); + String publicKeyYStr = publicKeyYElem.getAttributeNS(null, "Value"); + + ECDSAParameter ecdsaParams = new ECDSAParameter(eccParameterSpec, false); + ECGroupFactory ecGroupFactory = ECGroupFactory.getInstance(); + EllipticCurve eCurve = ecGroupFactory.getCurveWithProjective(eccParameterSpec.getA(), + eccParameterSpec.getB(), eccParameterSpec.getR()); + Field field = eCurve.getField(); + + // Detect type of public key field elements + String elementType = publicKeyXElem.getAttributeNS(NAMESPACE_XSI, "type"); + String elementTypeLocalName = elementType.substring(elementType.indexOf(':') + 1); + int FIELD_TYPE_PRIME = 1, FIELD_TYPE_CHAR_TWO = 2; + int fieldElemType = ("PrimeFieldElemType".equals(elementTypeLocalName)) + ? FIELD_TYPE_PRIME + : FIELD_TYPE_CHAR_TWO; + + FieldElement publicKeyPointX, publicKeyPointY; + if (fieldElemType == FIELD_TYPE_PRIME) + { + Value xValue = FieldFactory.getInstance().getPrimeFieldValue(new BigInteger(publicKeyXStr, 10)); + publicKeyPointX = field.newElement(xValue); + Value yValue = FieldFactory.getInstance().getPrimeFieldValue(new BigInteger(publicKeyYStr, 10)); + publicKeyPointY = field.newElement(yValue); + } + else + { + publicKeyPointX = field.newElement(octetString2ByteArray(publicKeyXStr)); + publicKeyPointY = field.newElement(octetString2ByteArray(publicKeyYStr)); + } + ProjectiveCoordinate publicKeyPointCoordinate = new ProjectiveCoordinate(publicKeyPointX, + publicKeyPointY, field.getONEelement()); + ECPoint publicKeyPoint = eCurve.newPoint(publicKeyPointCoordinate); + ECPublicKey publicKey = new ECPublicKey(ecdsaParams, publicKeyPoint); + + return publicKey; + } + + /** + * Method getECDSANSPrefix. + * @param element to get the prefix + * @return String the prefix + */ + private static String getECDSANSPrefix(Element element) + { + // FIXXME: Review this function (GK, 11.06.2002) - should return a list of strings, since more than + // one NS prefix can be bound to the ECDSA namespace + + HashMap inScopeNSAttrs = getInScopeNSAttrs(element); + Iterator inScopeNSAttrsIt = inScopeNSAttrs.keySet().iterator(); + while (inScopeNSAttrsIt.hasNext()) + { + Attr currentAttr = (Attr)inScopeNSAttrs.get(inScopeNSAttrsIt.next()); + if (Constants.ECDSA_NS_URI.equals(currentAttr.getValue())) + { + return ("xmlns".equals(currentAttr.getNodeName())) ? "" : currentAttr.getNodeName().substring(6); + } + } + return null; + } + + /** + * Method octetString2IntArray. + * Converts an octet string representation into an int array as needed for the IAIK ECC library + * @param octetString rightmost byte is least significant byte + * @return int[] rightmost byte is LEAST significant byte + */ + private static int[] octetString2IntArray(String octetString) + { + int byteCount = octetString.length()/2; + int[] intArray = new int[byteCount/4 + ((byteCount % 4 != 0) ? 1 : 0)]; + for (int i = 0; i < byteCount; i++) + { + int oSStartPos = octetString.length() - (i + 1) * 2; + int currentByte = Integer.parseInt(octetString.substring(oSStartPos, oSStartPos + 2), 16); + intArray[i/4] += (currentByte & 0xFF) << ((i % 4) * 8); + } + return intArray; + } + + /** + * Converts an octet string representation into a byte array as needed for the IAIK ECC library + * @param octetString rightmost byte is least significant byte + * @return byte[] rightmost byte is MOST significant byte + */ + private static byte[] octetString2ByteArray(String octetString) + { + int byteCount = octetString.length()/2; + byte[] byteArray = new byte[byteCount]; + for (int i = 0; i < byteCount; i++) + { + int oSStartPos = octetString.length() - (i + 1) * 2; + byteArray[byteCount - i - 1] = (byte) Integer.parseInt(octetString.substring( + oSStartPos, oSStartPos + 2), 16); + } + return byteArray; + } + + /** + * Method evenStringLength. + * @param hexString + * @return String + */ + + private static String evenStringLength(String hexString) + { + return (hexString.length() % 2 != 0) ? "0" + hexString : hexString; + } + + /** + * Method getChildElement. + * @param parent + * @param namespace + * @param localName + * @param instance + * @return Element + */ + + private static Element getChildElement(Element parent, String namespace, String localName, + int instance) + { + NodeList namedElements = parent.getElementsByTagNameNS(namespace, localName); + if (namedElements.getLength() < instance) return null; + return (Element)namedElements.item(instance - 1); + } + + /** + * Method getChildElementText. + * @param parent Element + * @param namespace String + * @param localName String + * @param instance int + * @return String + */ + + private static String getChildElementText(Element parent, String namespace, String localName, + int instance) + { + Element child = getChildElement(parent, namespace, localName, instance); + if (child == null) return null; + NodeList childNodes = child.getChildNodes(); + int nodeCount = 0; + while (nodeCount < childNodes.getLength()) + { + Node currentNode = childNodes.item(nodeCount); + if (currentNode.getNodeType() == Node.TEXT_NODE) return currentNode.getNodeValue(); + nodeCount++; + } + return null; + } + + /** + * Method getInScopeNSAttrs. + * @param element element + * @return HashMap + */ + public static HashMap getInScopeNSAttrs(Element element) + { + // Get all ancestors of element + Vector ancestors = new Vector(); + ancestors.add(element); + Node currentAncestor = element; + while ((currentAncestor = currentAncestor.getParentNode()) != null && + currentAncestor.getNodeType() == Node.ELEMENT_NODE) + { + ancestors.add(currentAncestor); + } + + // Scan all ancestors for NS attributes + HashMap inScopeNSAttrs = new HashMap(); + for (int i = ancestors.size() - 1; i >= 0; i--) + { + Element currentAncestorElem = (Element)ancestors.get(i); + NamedNodeMap attrs = currentAncestorElem.getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) + { + Attr currentAttr = (Attr)attrs.item(j); + String currentAttrName = currentAttr.getNodeName(); + if ("xmlns".equals(currentAttrName) || currentAttrName.startsWith("xmlns:")) + { + inScopeNSAttrs.put(currentAttrName, currentAttr); + } + } + } + + // Check if default NS attribute is in list; if value is empty remove it from list + Attr defaultNSAttr = (Attr)inScopeNSAttrs.get("xmlns"); + if (defaultNSAttr != null && "".equals(defaultNSAttr.getValue())) inScopeNSAttrs.remove("xmlns"); + + return inScopeNSAttrs; + } +}
\ No newline at end of file diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/parser/ErrorResponseParser.java b/id.server/src/at/gv/egovernment/moa/id/auth/parser/ErrorResponseParser.java new file mode 100644 index 000000000..4fbc58977 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/parser/ErrorResponseParser.java @@ -0,0 +1,89 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Parses an <code><InfoboxReadResponse></code>. + * + * @author Stefan Knirsch + * @version $Id$ + */ + +public class ErrorResponseParser { + // + // XPath namespace prefix shortcuts + // + /** Xpath prefix for reaching SecurityLayer 1.0 Namespaces */ + private static final String SL10 = Constants.SL10_PREFIX + ":"; + /** Xpath expression to the root element */ + private static final String ROOT = "/" + SL10 + "ErrorResponse/"; + /** Xpath expression to the ErrorCode element */ + private static final String ERROR_CODE_XPATH = + ROOT + SL10 + "ErrorCode"; + /** Xpath expression to the Info element */ + private static final String ERROR_INFO_XPATH = + ROOT + SL10 + "Info"; + + + /** This is the root element of the XML-Document provided by the Security Layer Card */ + private Element errorElement; + + /** + * Constructor for InfoboxReadResponseParser. + * A DOM-representation of the incoming String will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as String + * @throws ParseException on any error + */ + public ErrorResponseParser(String xmlResponse) throws ParseException { + try { + InputStream s = new ByteArrayInputStream(xmlResponse.getBytes("UTF-8")); + errorElement = DOMUtils.parseXmlValidating(s); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Constructor for InfoboxReadResponseParser. + * A DOM-representation of the incoming Inputstream will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as InputStream + * @throws ParseException on any error + */ + public ErrorResponseParser(InputStream xmlResponse) throws ParseException { + try { + errorElement = DOMUtils.parseXmlValidating(xmlResponse); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString() }, t); + } + } + + /** + * Method getErrorCode. returns the error code + * @return String + */ + public String getErrorCode() { + + return XPathUtils.getElementValue(errorElement,ERROR_CODE_XPATH,null); + } + + /** + * Method getErrorInfo: returns the information about the error + * @return String + */ + public String getErrorInfo() { + + return XPathUtils.getElementValue(errorElement,ERROR_INFO_XPATH,null); + } + + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/parser/IdentityLinkAssertionParser.java b/id.server/src/at/gv/egovernment/moa/id/auth/parser/IdentityLinkAssertionParser.java new file mode 100644 index 000000000..f9ef54884 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/parser/IdentityLinkAssertionParser.java @@ -0,0 +1,266 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.security.interfaces.RSAPublicKey; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.traversal.NodeIterator; + +import at.gv.egovernment.moa.id.*; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Parses an identity link <code><saml:Assertion></code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class IdentityLinkAssertionParser { + + // + // XPath namespace prefix shortcuts + // + + /** Xpath prefix for reaching PersonData Namespaces */ + private static final String PDATA = Constants.PD_PREFIX + ":"; + /** Xpath prefix for reaching SecurityLayer 1.0 Namespaces */ + private static final String SL10 = Constants.SL10_PREFIX + ":"; + /** Xpath prefix for reaching SAML Namespaces */ + private static final String SAML = Constants.SAML_PREFIX + ":"; + /** Xpath prefix for reaching XML-DSIG Namespaces */ + private static final String DSIG = Constants.DSIG_PREFIX + ":"; + /** Xpath prefix for reaching ECDS Namespaces */ + private static final String ECDSA = Constants.ECDSA_PREFIX + ":"; + /** Xpath expression to the root element */ + private static final String ROOT = "/" + SAML + "Assertion/"; + /** Xpath expression to the SAMLSubjectConfirmationData element */ + private static final String SAML_SUBJECT_CONFIRMATION_DATA_XPATH = + ROOT + + SAML + + "AttributeStatement/" + + SAML + + "Subject/" + + SAML + + "SubjectConfirmation/" + + SAML + + "SubjectConfirmationData"; + /** Xpath expression to the PersonData element */ + private static final String PERSON_XPATH = + SAML_SUBJECT_CONFIRMATION_DATA_XPATH + + "/" + + PDATA + + "Person"; + /** Xpath expression to the PersonData GivenName element */ + private static final String PERSON_GIVEN_NAME_XPATH = + PERSON_XPATH + + "/" + + PDATA + + "Name/" + + PDATA + + "GivenName"; + /** Xpath expression to the PersonData FamilyName element */ + private static final String PERSON_FAMILY_NAME_XPATH = + PERSON_XPATH + + "/" + + PDATA + + "Name/" + + PDATA + + "FamilyName"; + /** Xpath expression to the PersonData DateOfBirth element */ + private static final String PERSON_DATE_OF_BIRTH_XPATH = + PERSON_XPATH + + "/" + + PDATA + + "DateOfBirth"; + /** Xpath expression to the Identification element */ + private static final String PERSON_IDENT_XPATH = + PERSON_XPATH + + "/" + + PDATA + + "Identification"; + + /** Xpath expression to the Identification Value element */ + private static final String PERSON_IDENT_VALUE_XPATH = + PERSON_XPATH + + "/" + + PDATA + + "Identification/" + + PDATA + + "Value"; + /** Xpath expression to the RSAKeyValue element */ + private static final String RSA_KEY_VALUE_XPATH = + ROOT + + SAML + + "AttributeStatement/" + + SAML + + "Attribute/" + + SAML + + "AttributeValue/" + + DSIG + + "RSAKeyValue"; + /** Xpath expression to the RSA Modulus element */ + private static final String RSA_KEY_MODULUS_XPATH = DSIG + "Modulus"; + /** Xpath expression to the RSA Exponent element */ + private static final String RSA_KEY_EXPONENT_XPATH = DSIG + "Exponent"; + /** Xpath expression to the DSIG X509Certificate element */ + private static final String DSIG_CERTIFICATES_XPATH = + ROOT + + DSIG + + "Signature/" + + DSIG + + "KeyInfo/" + + DSIG + + "X509Data/" + + DSIG + + "X509Certificate"; + /** Xpath expression to the DSIG Transforms element */ + private static final String DSIG_REFERENCE_TRANSFORMATION_XPATH = + ROOT + + DSIG + + "Signature/" + + DSIG + + "SignedInfo/" + + DSIG + + "Reference/" + + DSIG + + "Transforms"; + + /**This is the root element of the XML-Document provided by the Security Layer Card*/ + private Element assertionElem; + + /** + * Constructor for <code>IdentityLinkAssertionParser</code>. + * A DOM-representation of the incoming String will be created + * @param xmlAssertion <code><saml:Assertion></code> as String + * @throws ParseException on any parsing error + */ + public IdentityLinkAssertionParser(String xmlAssertion) throws ParseException { + try { + InputStream s = new ByteArrayInputStream(xmlAssertion.getBytes("UTF-8")); + assertionElem = DOMUtils.parseXmlValidating(s); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Constructor for <code>IdentityLinkAssertionParser</code>. + * A DOM-representation of the incoming Inputstream will be created + * @param xmlAssertion <code><saml:Assertion></code> as InputStream + * @throws ParseException on any parsing error + */ + public IdentityLinkAssertionParser(InputStream xmlAssertion) throws Exception { + try { + assertionElem = DOMUtils.parseXmlValidating(xmlAssertion); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString() }, t); + } + } + + /** + * Parses the identity link from the <code><saml:Assertion></code> + * @return Identity link + * @throws ParseException on any parsing error + */ + + public IdentityLink parseIdentityLink() throws ParseException { + IdentityLink identityLink; + try { + identityLink = new IdentityLink(); + //ÄNDERN: NUR der Identification-Teil + identityLink.setSamlAssertion(assertionElem); + identityLink.setPrPerson((Element) + XPathUtils.selectSingleNode(assertionElem, PERSON_XPATH)); + identityLink.setIdentificationValue( + XPathUtils.getElementValue(assertionElem, PERSON_IDENT_VALUE_XPATH, "")); + identityLink.setGivenName( + XPathUtils.getElementValue(assertionElem, PERSON_GIVEN_NAME_XPATH, "")); + identityLink.setFamilyName( + XPathUtils.getElementValue(assertionElem, PERSON_FAMILY_NAME_XPATH, "")); + identityLink.setDateOfBirth( + XPathUtils.getElementValue(assertionElem, PERSON_DATE_OF_BIRTH_XPATH, "")); + NodeIterator dsigRefTransforms = + XPathUtils.selectNodeIterator(assertionElem, DSIG_REFERENCE_TRANSFORMATION_XPATH); + List transElems = new ArrayList(); + Element transformsElem; + while ((transformsElem = (Element) dsigRefTransforms.nextNode()) != null) { + transElems.add(transformsElem); + } + Element[] result = new Element[transElems.size()]; + transElems.toArray(result); + identityLink.setDsigReferenceTransforms(result); + + identityLink.setPublicKey(getPublicKeys()); + + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString() }, t); + } + + return identityLink; + } + + /** + * Parses an array of Public Keys from the <code><InfoboxReadResponse></code> + * @return RSAPublicKey[] + * @throws IOException can occur when decoding the base64 values of the modulus and exponent + */ + public PublicKey[] getPublicKeys() throws IOException{ + + + List pubKeys = new ArrayList(); + //Try to get RSA-Keys + NodeIterator rsaIter = + XPathUtils.selectNodeIterator(assertionElem, RSA_KEY_VALUE_XPATH); + Element rsaElem; + while ((rsaElem = (Element) rsaIter.nextNode()) != null) { + String modulus = + XPathUtils.getElementValue(rsaElem, RSA_KEY_MODULUS_XPATH, ""); + String exponent = + XPathUtils.getElementValue(rsaElem, RSA_KEY_EXPONENT_XPATH, ""); + + RSAPublicKey resPub = + new iaik.security.rsa.RSAPublicKey( + new BigInteger(1, Base64Utils.decode(modulus, true)), + new BigInteger(1, Base64Utils.decode(exponent, true))); + pubKeys.add(resPub);} + + PublicKey[] result = new PublicKey[pubKeys.size()]; + + pubKeys.toArray(result); + return result; + + } + /** + * Parses a string array of decoded base64 certificates from + * the <code><InfoboxReadResponse></code> found in the dsig-signature + * @return String[] with raw-certificates from the dsig-signature keyinfo + * @throws Exception + */ + public String[] getCertificates() throws Exception { + List certs = new ArrayList(); + NodeIterator rsaIter = + XPathUtils.selectNodeIterator(assertionElem, DSIG_CERTIFICATES_XPATH); + Element certElem; + while ((certElem = (Element) rsaIter.nextNode()) != null) { + String content = DOMUtils.getText(certElem); + certs.add(new String(Base64Utils.decode(content, true))); + } + String[] result = new String[certs.size()]; + certs.toArray(result); + return result; + + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/parser/InfoboxReadResponseParser.java b/id.server/src/at/gv/egovernment/moa/id/auth/parser/InfoboxReadResponseParser.java new file mode 100644 index 000000000..c1146218e --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/parser/InfoboxReadResponseParser.java @@ -0,0 +1,110 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.AuthenticationException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Parses an <code><InfoboxReadResponse></code>. + * + * @author Stefan Knirsch + * @version $Id$ + */ + +public class InfoboxReadResponseParser { + // + // XPath namespace prefix shortcuts + // + /** Xpath prefix for reaching SecurityLayer 1.0 Namespaces */ + private static final String SL10 = Constants.SL10_PREFIX + ":"; + /** Xpath prefix for reaching SAML Namespaces */ + private static final String SAML = Constants.SAML_PREFIX + ":"; + /** Xpath expression to the root element */ + private static final String ROOT = "/" + SL10 + "InfoboxReadResponse/"; + /** Xpath expression to the SAML:Assertion element */ + private static final String SAML_ASSERTION_XPATH = ROOT + SL10 + "BinaryFileData/" + SL10 + "XMLContent/" + SAML + "Assertion"; + + /** This is the root element of the XML-Document provided by the Security Layer Card*/ + private Element infoBoxElem; + + /** + * Constructor for InfoboxReadResponseParser. + * A DOM-representation of the incoming String will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as String + * @throws ParseException on any parsing error + */ + public InfoboxReadResponseParser(String xmlResponse) throws ParseException, AuthenticationException { + + ErrorResponseParser erp = new ErrorResponseParser(xmlResponse); + if (erp.getErrorCode() != null) { + throw new AuthenticationException("auth.08", new Object[] { erp.getErrorCode(), erp.getErrorInfo()}); + } + + try { + + InputStream s = new ByteArrayInputStream(xmlResponse.getBytes("UTF-8")); + infoBoxElem = DOMUtils.parseXmlValidating(s); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Constructor for InfoboxReadResponseParser. + * A DOM-representation of the incoming Inputstream will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as InputStream + * @throws ParseException on any parsing error + */ + public InfoboxReadResponseParser(InputStream is) throws ParseException, AuthenticationException { + + ErrorResponseParser erp = new ErrorResponseParser(is); + if (erp.getErrorCode() != null) { + throw new AuthenticationException("auth.08", new Object[] { erp.getErrorCode(), erp.getErrorInfo()}); + } + + try { + + infoBoxElem = DOMUtils.parseXmlValidating(is); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Parses the embedded <code><saml:Assertion></code> element from <code><InfoboxReadResponse></code> + * @return <code><saml:Assertion></code> as String + * @throws ParseException on any parsing error + */ + public String parseSAMLAssertion() throws ParseException { + try { + Element samlAssertion = (Element) XPathUtils.selectSingleNode(infoBoxElem, SAML_ASSERTION_XPATH); + return DOMUtils.serializeNode(samlAssertion); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Parses the identity link from the <code><saml:Assertion></code> + * @return Identity link + * @throws ParseException on any parsing error + */ + + public IdentityLink parseIdentityLink() throws ParseException { + String samlAssertionString = parseSAMLAssertion(); + IdentityLinkAssertionParser ilParser = new IdentityLinkAssertionParser(samlAssertionString); + return ilParser.parseIdentityLink(); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/parser/SAMLArtifactParser.java b/id.server/src/at/gv/egovernment/moa/id/auth/parser/SAMLArtifactParser.java new file mode 100644 index 000000000..7c4c01abe --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/parser/SAMLArtifactParser.java @@ -0,0 +1,58 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.io.IOException; + +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.util.Base64Utils; + +/** + * Parser for a SAML artifact. + * @author Paul Ivancsics + * @version $Id$ + */ +public class SAMLArtifactParser { + /** byte array containing the SamlArtifact bytes */ + private byte[] samlArtifactBytes; + + /** + * Constructor + * @param samlArtifact as String + * @throws ParseException on any parsing error + */ + public SAMLArtifactParser(String samlArtifact) throws ParseException { + try { + samlArtifactBytes = Base64Utils.decode(samlArtifact, false); + } + catch (IOException ex) { + throw new ParseException("parser.02", new Object[] {ex.toString()}, ex); + } + } + /** + * Parses the type code. + * @return type code + * @throws ParseException when SAML artifact is invalid + */ + public byte[] parseTypeCode() throws ParseException { + try { + byte[] typeCode = new byte[] {samlArtifactBytes[0], samlArtifactBytes[1]}; + return typeCode; + } + catch (Throwable ex) { + throw new ParseException("parser.02", new Object[] {ex.toString()}, ex); + } + } + /** + * Parses the assertion handle. + * @return assertion handle + * @throws ParseException when SAML artifact is invalid + */ + public String parseAssertionHandle() throws ParseException { + try { + return new String(samlArtifactBytes, 22, 20); + } + catch (Throwable ex) { + throw new ParseException("parser.02", new Object[] {ex.toString()}, ex); + } + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/parser/VerifyXMLSignatureResponseParser.java b/id.server/src/at/gv/egovernment/moa/id/auth/parser/VerifyXMLSignatureResponseParser.java new file mode 100644 index 000000000..c74dc64e8 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/parser/VerifyXMLSignatureResponseParser.java @@ -0,0 +1,159 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import iaik.utils.Base64InputStream; +import iaik.x509.X509Certificate; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.*; +import at.gv.egovernment.moa.id.auth.data.VerifyXMLSignatureResponse; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Parses a <code><VerifyXMLSignatureResponse></code> returned by + * MOA-SPSS. + * This class implements the Singleton pattern + * + * @author Stefan Knirsch + * @version $Id$ + */ + + +public class VerifyXMLSignatureResponseParser { + // + // XPath namespace prefix shortcuts + // + /** Xpath prefix for reaching MOA Namespaces */ + private static final String MOA = Constants.MOA_PREFIX + ":"; + /** Xpath prefix for reaching DSIG Namespaces */ + private static final String DSIG = Constants.DSIG_PREFIX + ":"; + /** Xpath prefix for reaching SecurityLayer 1.1 Namespaces */ + private static final String SL11 = Constants.SL11_PREFIX + ":"; + /** Xpath expression to the root element */ + private static final String ROOT = "/" + MOA + "VerifyXMLSignatureResponse/"; + + /** Xpath expression to the X509SubjectName element */ + private static final String DSIG_SUBJECT_NAME_XPATH = + ROOT + MOA + "SignerInfo/" + DSIG + "X509Data/" + + DSIG + "X509SubjectName"; + /** Xpath expression to the X509Certificate element */ + private static final String DSIG_X509_CERTIFICATE_XPATH = + ROOT + MOA + "SignerInfo/" + DSIG + "X509Data/" + + DSIG + "X509Certificate"; + /** Xpath expression to the PublicAuthority element */ + private static final String PUBLIC_AUTHORITY_XPATH = + ROOT + MOA + "SignerInfo/" + DSIG + "X509Data/" + + MOA + "PublicAuthority"; + /** Xpath expression to the PublicAuthorityCode element */ + private static final String PUBLIC_AUTHORITY_CODE_XPATH = + PUBLIC_AUTHORITY_XPATH + "/" + MOA + "Code"; + /** Xpath expression to the QualifiedCertificate element */ + private static final String QUALIFIED_CERTIFICATE_XPATH = + ROOT + MOA + "SignerInfo/" + DSIG + "X509Data/" + + SL11 + "QualifiedCertificate"; + + /** Xpath expression to the SignatureCheckCode element */ + private static final String SIGNATURE_CHECK_CODE_XPATH = + ROOT + MOA + "SignatureCheck/" + MOA + "Code"; + /** Xpath expression to the XMLDSIGManifestCheckCode element */ + private static final String XMLDSIG_MANIFEST_CHECK_CODE_XPATH = + ROOT + MOA + "XMLDSIGManifestCheck/" + MOA + "Code"; + /** Xpath expression to the CertificateCheckCode element */ + private static final String CERTIFICATE_CHECK_CODE_XPATH = + ROOT + MOA + "CertificateCheck/" + MOA + "Code"; + + + /** This is the root element of the XML-Document provided by the Security Layer Card*/ + private Element verifyXMLSignatureResponse; + + /** + * Constructor for VerifyXMLSignatureResponseParser. + * A DOM-representation of the incoming String will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as String + * @throws ParseException on any parsing error + */ + public VerifyXMLSignatureResponseParser(String xmlResponse) throws ParseException{ + try { + InputStream s = new ByteArrayInputStream(xmlResponse.getBytes("UTF-8")); + + verifyXMLSignatureResponse = DOMUtils.parseXmlValidating(s); + } + catch (Throwable t) { + throw new ParseException("parser.01", null, t); + } + } + + /** + * Constructor for VerifyXMLSignatureResponseParser. + * A DOM-representation of the incoming Inputstream will be created + * @param xmlResponse <code><InfoboxReadResponse></code> as InputStream + * @throws Exception on any parsing error + */ + public VerifyXMLSignatureResponseParser(InputStream xmlResponse) throws Exception + { + try { + verifyXMLSignatureResponse = DOMUtils.parseXmlValidating(xmlResponse); + } + catch (Throwable t) { + throw new ParseException("parser.01", null, t); + } + } + + /** + * Constructor for VerifyXMLSignatureResponseParser. + * The incoming Element will be used for further operations + * @param xmlResponse <code><InfoboxReadResponse></code> as Element + */ + public VerifyXMLSignatureResponseParser(Element xmlResponse) + { + verifyXMLSignatureResponse =xmlResponse; + + } + + /** + * Parse identity link from <code><InfoboxReadResponse></code> + * @return Identity link + * @throws ParseException on any parsing error + */ + + public VerifyXMLSignatureResponse parseData() throws ParseException { + VerifyXMLSignatureResponse respData=new VerifyXMLSignatureResponse(); + + try { + + respData.setXmlDsigSubjectName(XPathUtils.getElementValue(verifyXMLSignatureResponse,DSIG_SUBJECT_NAME_XPATH,"")); + Element e = (Element)XPathUtils.selectSingleNode(verifyXMLSignatureResponse,QUALIFIED_CERTIFICATE_XPATH); + respData.setQualifiedCertificate(e!=null); + + Base64InputStream in = new Base64InputStream(new ByteArrayInputStream(XPathUtils.getElementValue( + verifyXMLSignatureResponse,DSIG_X509_CERTIFICATE_XPATH,"").getBytes("UTF-8")),true); + + respData.setX509certificate(new X509Certificate(in)); + + Element publicAuthority = (Element)XPathUtils.selectSingleNode(verifyXMLSignatureResponse,PUBLIC_AUTHORITY_CODE_XPATH); + respData.setPublicAuthority(publicAuthority != null); + respData.setPublicAuthorityCode(XPathUtils.getElementValue(verifyXMLSignatureResponse,PUBLIC_AUTHORITY_CODE_XPATH,"")); + respData.setSignatureCheckCode(new Integer(XPathUtils.getElementValue(verifyXMLSignatureResponse,SIGNATURE_CHECK_CODE_XPATH,"")).intValue()); + + String xmlDsigCheckCode = XPathUtils.getElementValue(verifyXMLSignatureResponse,XMLDSIG_MANIFEST_CHECK_CODE_XPATH,null); + if (xmlDsigCheckCode!=null) + { + respData.setXmlDSIGManigest(true); + respData.setXmlDSIGManifestCheckCode(new Integer(xmlDsigCheckCode).intValue()); + } + else + respData.setXmlDSIGManigest(false); + respData.setCertificateCheckCode(new Integer(XPathUtils.getElementValue(verifyXMLSignatureResponse,CERTIFICATE_CHECK_CODE_XPATH,"")).intValue()); + } + catch (Throwable t) { + throw new ParseException("parser.01", null, t); + } + return respData; + } + + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/servlet/AuthServlet.java b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/AuthServlet.java new file mode 100644 index 000000000..3a1cab4be --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/AuthServlet.java @@ -0,0 +1,117 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; +import at.gv.egovernment.moa.id.auth.WrongParametersException; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.URLDecoder; + +/** + * Base class for MOA-ID Auth Servlets, providing standard error handling + * and constant names. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthServlet extends HttpServlet implements MOAIDAuthConstants { + + + /** + * Handles an error. <br> + * <ul> + * <li>Logs the error</li> + * <li>Places error message and exception thrown into the request + * as request attributes (to be used by <code>"/errorpage.jsp"</code>)</li> + * <li>Sets HTTP status 500 (internal server error)</li> + * </ul> + * + * @param errorMessage error message + * @param exceptionThrown exception thrown + * @param req servlet request + * @param resp servlet response + */ + protected void handleError( + String errorMessage, Throwable exceptionThrown, HttpServletRequest req, HttpServletResponse resp) { + + if (exceptionThrown != null) + Logger.error(errorMessage, exceptionThrown); + else + Logger.error(errorMessage); + req.setAttribute("ErrorMessage", errorMessage); + req.setAttribute("ExceptionThrown", exceptionThrown); + resp.setStatus(500); + } + /** + * Handles a <code>WrongParametersException</code>. + * @param req servlet request + * @param resp servlet response + */ + protected void handleWrongParameters(WrongParametersException ex, HttpServletRequest req, HttpServletResponse resp) { + Logger.error(ex.toString()); + req.setAttribute("WrongParameters", "true"); + resp.setStatus(500); + } + + /** + * Logs all servlet parameters for debugging purposes. + */ + protected void logParameters(HttpServletRequest req) { + for (Enumeration enum = req.getParameterNames(); enum.hasMoreElements(); ) { + String parname = (String)enum.nextElement(); + Logger.debug("Parameter " + parname + req.getParameter(parname)); + } + } + /** + * Parses the request input stream for parameters, + * assuming parameters are encoded UTF-8. + * @param req servlet request + * @return mapping parameter name -> value + */ + protected Map getParameters(HttpServletRequest req) throws IOException { + Map parameters = new HashMap(); + InputStream in = req.getInputStream(); + String paramName; + String paramValueURLEncoded; + do { + paramName = new String(readBytesUpTo(in, '=')); + if (paramName.length() > 0) { + paramValueURLEncoded = readBytesUpTo(in, '&'); + String paramValue = URLDecoder.decode(paramValueURLEncoded, "UTF-8"); + parameters.put(paramName, paramValue); + } + } + while (paramName.length() > 0); + in.close(); + + return parameters; + } + /** + * Reads bytes up to a delimiter, consuming the delimiter. + * @param in input stream + * @param delimiter delimiter character + * @return String constructed from the read bytes + * @throws IOException + */ + protected String readBytesUpTo(InputStream in, char delimiter) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + boolean done = false; + int b; + while (! done && (b = in.read()) >= 0) { + if (b == delimiter) + done = true; + else + bout.write(b); + } + return bout.toString(); + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/servlet/ConfigurationServlet.java b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/ConfigurationServlet.java new file mode 100644 index 000000000..554819f73 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/ConfigurationServlet.java @@ -0,0 +1,74 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.auth.MOAIDAuthInitializer; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; + +/** + * Servlet requested for updating the MOA-ID Auth configuration from configuration file + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class ConfigurationServlet extends HttpServlet { + /** Constant for the DTD-Doc type */ + private static final String DOC_TYPE = + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"; + + /** + * Handle a HTTP GET request, used to indicated that the MOA + * configuration needs to be updated (reloaded). + * + * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest, HttpServletResponse) + */ + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + MOAIDMessageProvider msg = MOAIDMessageProvider.getInstance(); + PrintWriter out; + + response.setContentType("text/html"); + out = response.getWriter(); + out.println(DOC_TYPE); + out.println("<head><title>MOA configuration update</title></head>"); + out.println("<body bgcolor=\"#FFFFFF\">"); + try { + MOAIDAuthInitializer.initialized=false; + MOAIDAuthInitializer.initialize(); + String message = msg.getMessage("config.00", null); + Logger.info(message); + out.println("<p><b>"); + out.println(message); + out.println("</b></p>"); + } catch (Throwable t) { + String errorMessage = msg.getMessage("config.04", null); + Logger.error(errorMessage, t); + out.println("<p><b>"); + out.println(errorMessage); + out.println("</b></p>"); + } + out.println("</body>"); + + out.flush(); + out.close(); + } + + /** + * Do the same as <code>doGet</code>. + * + * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest, HttpServletResponse) + */ + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/servlet/GetAuthenticationDataService.java b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/GetAuthenticationDataService.java new file mode 100644 index 000000000..c41b514c8 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/GetAuthenticationDataService.java @@ -0,0 +1,135 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.util.Calendar; + +import org.apache.axis.AxisFault; +import org.w3c.dom.Element; + +import org.w3c.dom.NodeList; + +import at.gv.egovernment.moa.id.AuthenticationException; +import at.gv.egovernment.moa.id.MOAIDException; +import at.gv.egovernment.moa.id.auth.AuthenticationServer; +import at.gv.egovernment.moa.id.auth.builder.SAMLResponseBuilder; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.id.util.Random; +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.XPathUtils; + +/** + * Web service for picking up authentication data created in the MOA-ID Auth component. + * + * @author Paul Ivancsics + * @version $Id$ + * @see at.gv.egovernment.moa.id.auth.AuthenticationServer#getAuthenticationData + */ +public class GetAuthenticationDataService implements Constants { + + /** + * Constructor for GetAuthenticationDataService. + */ + public GetAuthenticationDataService() { + super(); + } + + /** + * Takes a <code>lt;samlp:Request></code> containing a + * <code>SAML artifact</code> and returns the corresponding + * authentication data <code>lt;saml:Assertion></code> + * (obtained from the <code>AuthenticationServer</code>), + * enclosed in a <code>lt;samlp:Response></code>. + * <br/>Bad requests are mapped into various <code>lt;samlp:StatusCode></code>s, + * possibly containing enclosed sub-<code>lt;samlp:StatusCode></code>s. + * The status codes are defined in the SAML specification. + * + * @param requests request elements of type <code>lt;samlp:Request></code>; + * only 1 request element is allowed + * @return response element of type <code>lt;samlp:Response></code>, + * packed into an <code>Element[]</code> + * @throws AxisFault thrown when an error occurs in assembling the + * <code>lt;samlp:Response></code> + */ + public Element[] Request(Element[] requests) + throws AxisFault { + + Element request = requests[0]; + Element[] responses = new Element[1]; + String requestID = ""; + String statusCode = ""; + String subStatusCode = null; + String statusMessageCode = null; + String statusMessage = null; + String samlAssertion = ""; + if (requests.length > 1) { + // more than 1 request given as parameter + statusCode = "samlp:Requester"; + subStatusCode = "samlp:TooManyResponses"; + statusMessageCode = "1201"; + } + else { + try { + DOMUtils.validateElement(request, ALL_SCHEMA_LOCATIONS, null); + NodeList samlArtifactList = XPathUtils.selectNodeList(request, "samlp:AssertionArtifact"); + if (samlArtifactList.getLength() == 0) { + // no SAML artifact given in request + statusCode = "samlp:Requester"; + statusMessageCode = "1202"; + } + else if (samlArtifactList.getLength() > 1) { + // too many SAML artifacts given in request + statusCode = "samlp:Requester"; + subStatusCode = "samlp:TooManyResponses"; + statusMessageCode = "1203"; + } + else { + Element samlArtifactElem = (Element)samlArtifactList.item(0); + requestID = samlArtifactElem.getAttribute("RequestID"); + String samlArtifact = DOMUtils.getText(samlArtifactElem); + try { + AuthenticationData authData = AuthenticationServer.getInstance(). + getAuthenticationData(samlArtifact); + // success + samlAssertion = authData.getSamlAssertion(); + statusCode = "samlp:Success"; + statusMessageCode = "1200"; + } + catch (AuthenticationException ex) { + // no authentication data for given SAML artifact + statusCode = "samlp:Requester"; + subStatusCode = "samlp:ResourceNotRecognized"; + statusMessage = ex.toString(); + } + } + } + catch (Throwable t) { + // invalid request format + statusCode = "samlp:Requester"; + statusMessageCode = "1204"; + } + } + try { + String responseID = Random.nextRandom(); + String issueInstant = DateTimeUtils.buildDateTime(Calendar.getInstance()); + if (statusMessage == null) + statusMessage = MOAIDMessageProvider.getInstance().getMessage(statusMessageCode, null); + responses[0] = new SAMLResponseBuilder().build( + responseID, requestID, issueInstant, statusCode, subStatusCode, statusMessage, samlAssertion); + } + catch (MOAIDException e) { + AxisFault fault = AxisFault.makeFault(e); + fault.setFaultDetail(new Element[] { e.toErrorResponse()}); + throw fault; + } + catch (Throwable t) { + MOAIDException e = new MOAIDException("1299", null, t); + AxisFault fault = AxisFault.makeFault(e); + fault.setFaultDetail(new Element[] { e.toErrorResponse()}); + throw fault; + } + return responses; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/servlet/SelectBKUServlet.java b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/SelectBKUServlet.java new file mode 100644 index 000000000..50ca21c69 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/SelectBKUServlet.java @@ -0,0 +1,95 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.auth.AuthenticationServer; +import at.gv.egovernment.moa.id.auth.MOAIDAuthInitializer; +import at.gv.egovernment.moa.id.auth.WrongParametersException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; + +/** + * Servlet requested for selecting a BKU. + * <br>In case of {@link AuthConfigurationProvider#getBKUSelectionType}==HTMLComplete, + * the browser is redirected to the configured "BKU-Auswahl-URL". + * <br>In case of {@link AuthConfigurationProvider#getBKUSelectionType}==HTMLSelect, + * the list of available BKU's is fetched from a BKU-Auswahl server, and presented + * to the user in an HTML form. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class SelectBKUServlet extends AuthServlet { + + /** + * Calls the web application initializer. + * + * @see javax.servlet.Servlet#init(ServletConfig) + */ + public void init(ServletConfig servletConfig) throws ServletException { + try { + MOAIDAuthInitializer.initialize(); + Logger.info(MOAIDMessageProvider.getInstance().getMessage("init.00", null)); + } + catch (Exception ex) { + Logger.fatal(MOAIDMessageProvider.getInstance().getMessage("init.02", null), ex); + throw new ServletException(ex); + } + } + + /** + * Responds with an HTML form which requests the user to choose a BKU. + */ + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + Logger.debug("GET SelectBKU"); + String authURL = + req.getScheme() + "://" + + req.getServerName() + ":" + + req.getServerPort() + + req.getContextPath() + "/"; + String target = req.getParameter(PARAM_TARGET); + String oaURL = req.getParameter(PARAM_OA); + String bkuSelectionTemplateURL = req.getParameter(PARAM_BKUTEMPLATE); + String templateURL = req.getParameter(PARAM_TEMPLATE); + resp.setHeader(HEADER_EXPIRES,HEADER_VALUE_EXPIRES); + resp.setHeader(HEADER_PRAGMA,HEADER_VALUE_PRAGMA); + resp.setHeader(HEADER_CACHE_CONTROL,HEADER_VALUE_CACHE_CONTROL); + resp.addHeader(HEADER_CACHE_CONTROL,HEADER_VALUE_CACHE_CONTROL_IE); + + try { + String returnValue = AuthenticationServer.getInstance().selectBKU( + authURL, target, oaURL, bkuSelectionTemplateURL, templateURL); + String bkuSelectionType = AuthConfigurationProvider.getInstance().getBKUSelectionType(); + if (bkuSelectionType.equals(AuthConfigurationProvider.BKU_SELECTION_TYPE_HTMLCOMPLETE)) { + // bkuSelectionType==HTMLComplete + String redirectURL = returnValue; + resp.sendRedirect(redirectURL); + Logger.debug("REDIRECT TO: " + redirectURL); + } + else { + // bkuSelectionType==HTMLSelect + String htmlForm = returnValue; + resp.setContentType("text/html"); + PrintWriter out = new PrintWriter(resp.getOutputStream()); + out.print(htmlForm); + out.flush(); + Logger.debug("Finished GET SelectBKU"); + } + } + catch (WrongParametersException ex) { + handleWrongParameters(ex, req, resp); + } + catch (Throwable ex) { + handleError(null, ex, req, resp); + } + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/servlet/StartAuthenticationServlet.java b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/StartAuthenticationServlet.java new file mode 100644 index 000000000..2ea43935b --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/StartAuthenticationServlet.java @@ -0,0 +1,102 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.MOAIDException; +import at.gv.egovernment.moa.id.auth.AuthenticationServer; +import at.gv.egovernment.moa.id.auth.MOAIDAuthInitializer; +import at.gv.egovernment.moa.id.auth.WrongParametersException; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; + +/** + * Servlet requested for starting a MOA ID authentication session. + * Utilizes the {@link AuthenticationServer}. + * + * @author Paul Ivancsics + * @version $Id$ + * @see AuthenticationServer#startAuthentication + */ +public class StartAuthenticationServlet extends AuthServlet { + + /** + * Responds with an HTML form which upon submit requests the identity link + * from the security layer implementation. + * <br> + * Response: + * <ul> + * <li>Content type: <code>"text/html"</code></li> + * <li>Content: see return value of {@link AuthenticationServer#startAuthentication}</li> + * <li>Error status: <code>500</code> + * </ul> + * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest, HttpServletResponse) + */ + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + Logger.debug("GET StartAuthentication"); + String authURL = + req.getScheme() + "://" + + req.getServerName() + ":" + + req.getServerPort() + + req.getContextPath() + "/"; + String target = req.getParameter(PARAM_TARGET); + String oaURL = req.getParameter(PARAM_OA); + String bkuURL = req.getParameter(PARAM_BKU); + String templateURL = req.getParameter(PARAM_TEMPLATE); + String sessionID = req.getParameter(PARAM_SESSIONID); + resp.setHeader(HEADER_EXPIRES,HEADER_VALUE_EXPIRES); + resp.setHeader(HEADER_PRAGMA,HEADER_VALUE_PRAGMA); + resp.setHeader(HEADER_CACHE_CONTROL,HEADER_VALUE_CACHE_CONTROL); + resp.addHeader(HEADER_CACHE_CONTROL,HEADER_VALUE_CACHE_CONTROL_IE); + try { + String getIdentityLinkForm = + AuthenticationServer.getInstance().startAuthentication(authURL, target, oaURL, templateURL, bkuURL, sessionID); + resp.setContentType("text/html"); + PrintWriter out = new PrintWriter(resp.getOutputStream()); + out.print(getIdentityLinkForm); + out.flush(); + Logger.debug("Finished GET StartAuthentication"); + } + catch (WrongParametersException ex) { + handleWrongParameters(ex, req, resp); + } + catch (MOAIDException ex) { + handleError(null, ex, req, resp); + } + } + + + /** + * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + doGet(req, resp); + } + + + /** + * Calls the web application initializer. + * + * @see javax.servlet.Servlet#init(ServletConfig) + */ + public void init(ServletConfig servletConfig) throws ServletException { + try { + MOAIDAuthInitializer.initialize(); + Logger.info(MOAIDMessageProvider.getInstance().getMessage("init.00", null)); + } + catch (Exception ex) { + Logger.fatal(MOAIDMessageProvider.getInstance().getMessage("init.02", null), ex); + throw new ServletException(ex); + } + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/servlet/VerifyAuthenticationBlockServlet.java b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/VerifyAuthenticationBlockServlet.java new file mode 100644 index 000000000..8d16f73dd --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/VerifyAuthenticationBlockServlet.java @@ -0,0 +1,110 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.MOAIDException; +import at.gv.egovernment.moa.id.auth.AuthenticationServer; +import at.gv.egovernment.moa.id.auth.WrongParametersException; +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.logging.Logger; + +/** + * Servlet requested for verifying the signed authentication block + * provided by the security layer implementation. + * Utilizes the {@link AuthenticationServer}. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class VerifyAuthenticationBlockServlet extends AuthServlet { + + + /** + * Constructor for VerifyAuthenticationBlockServlet. + */ + public VerifyAuthenticationBlockServlet() { + super(); + } + + /** + * GET requested by security layer implementation to verify + * that data URL resource is available. + * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest, HttpServletResponse) + */ + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + Logger.debug("GET VerifyAuthenticationBlock"); + } + + /** + * Verifies the signed authentication block and redirects the browser + * to the online application requested, adding a parameter needed for + * retrieving the authentication data. + * <br> + * Request parameters: + * <ul> + * <li>MOASessionID: ID of associated authentication session</li> + * <li>XMLResponse: <code><CreateXMLSignatureResponse></code></li> + * </ul> + * Response: + * <ul> + * <li>Status: <code>302</code></li> + * <li>Header <code>"Location"</code>: URL of the online application requested, with + * parameters <code>"Target"</code> and <code>"SAMLArtifact"</code> added</li> + * <li>Error status: <code>500</code> + * </ul> + * @see AuthenticationServer#verifyAuthenticationBlock + * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest, HttpServletResponse) + */ + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + Logger.debug("POST VerifyAuthenticationBlock"); + Map parameters = getParameters(req); + String sessionID = req.getParameter(PARAM_SESSIONID); + String createXMLSignatureResponse = (String)parameters.get(PARAM_XMLRESPONSE); + // debug output + AuthenticationServer.debugOutputXMLFile("CreateXMLSignatureResponse.xml", createXMLSignatureResponse); + try { + AuthenticationSession session = AuthenticationServer.getSession(sessionID); + String samlArtifactBase64 = + AuthenticationServer.getInstance().verifyAuthenticationBlock(sessionID, createXMLSignatureResponse); + String redirectURL = session.getOAURLRequested(); + redirectURL = addURLParameter(redirectURL, PARAM_TARGET, session.getTarget()); + redirectURL = addURLParameter(redirectURL, PARAM_SAMLARTIFACT, URLEncoder.encode(samlArtifactBase64)); + redirectURL = resp.encodeRedirectURL(redirectURL); + resp.setStatus(302); + resp.addHeader("Location", redirectURL); + Logger.debug("REDIRECT TO: " + redirectURL); + } + catch (WrongParametersException ex) { + handleWrongParameters(ex, req, resp); + } + catch (MOAIDException ex) { + handleError(null, ex, req, resp); + } + + } + /** + * Adds a parameter to a URL. + * @param url the URL + * @param paramname parameter name + * @param paramvalue parameter value + * @return the URL with parameter added + */ + private static String addURLParameter(String url, String paramname, String paramvalue) { + String param = paramname + "=" + paramvalue; + if (url.indexOf("?") < 0) + return url + "?" + param; + else + return url + "&" + param; + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/servlet/VerifyIdentityLinkServlet.java b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/VerifyIdentityLinkServlet.java new file mode 100644 index 000000000..d3a28c7d4 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/servlet/VerifyIdentityLinkServlet.java @@ -0,0 +1,97 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.MOAIDException; +import at.gv.egovernment.moa.id.auth.AuthenticationServer; +import at.gv.egovernment.moa.id.auth.WrongParametersException; +import at.gv.egovernment.moa.id.auth.builder.DataURLBuilder; +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.logging.Logger; + +/** + * Servlet requested for verifying the identity link + * provided by the security layer implementation. + * Utilizes the {@link AuthenticationServer}. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class VerifyIdentityLinkServlet extends AuthServlet { + + /** + * Constructor for VerifyIdentityLinkServlet. + */ + public VerifyIdentityLinkServlet() { + super(); + } + + /** + * GET requested by security layer implementation to verify + * that data URL resource is available. + * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest, HttpServletResponse) + */ + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + Logger.debug("GET VerifyIdentityLink"); + } + + /** + * Verifies the identity link and responds with a new + * <code>CreateXMLSignatureRequest</code>. + * <br> + * Request parameters: + * <ul> + * <li>MOASessionID: ID of associated authentication session</li> + * <li>XMLResponse: <code><InfoboxReadResponse></code></li> + * </ul> + * Response: + * <ul> + * <li>Content type: <code>"text/xml"</code></li> + * <li>Content: see return value of {@link AuthenticationServer#verifyIdentityLink}</li> + * <li>Error status: <code>500</code> + * </ul> + * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest, HttpServletResponse) + */ + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + Logger.debug("POST VerifyIdentityLink"); + Map parameters = getParameters(req); + String sessionID = req.getParameter(PARAM_SESSIONID); + String infoboxReadResponse = (String)parameters.get(PARAM_XMLRESPONSE); + // debug output + AuthenticationServer.debugOutputXMLFile("InfoboxReadResponse.xml", infoboxReadResponse); + try { + String createXMLSignatureRequest = + AuthenticationServer.getInstance().verifyIdentityLink(sessionID, infoboxReadResponse); + AuthenticationSession session = AuthenticationServer.getSession(sessionID); + resp.setStatus(307); + String dataURL = new DataURLBuilder().buildDataURL( + session.getAuthURL(), AuthenticationServer.REQ_VERIFY_AUTH_BLOCK, sessionID); + resp.addHeader("Location", dataURL); + resp.setContentType("text/xml"); + // debug output + AuthenticationServer.debugOutputXMLFile("CreateXMLSignatureRequest.xml", createXMLSignatureRequest); + OutputStream out = resp.getOutputStream(); + out.write(createXMLSignatureRequest.getBytes("UTF-8")); + out.flush(); + out.close(); + Logger.debug("Finished POST VerifyIdentityLink"); + } + catch (WrongParametersException ex) { + handleWrongParameters(ex, req, resp); + } + catch (MOAIDException ex) { + handleError(null, ex, req, resp); + } + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/validator/CreateXMLSignatureResponseValidator.java b/id.server/src/at/gv/egovernment/moa/id/auth/validator/CreateXMLSignatureResponseValidator.java new file mode 100644 index 000000000..e596e79a4 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/validator/CreateXMLSignatureResponseValidator.java @@ -0,0 +1,106 @@ +package at.gv.egovernment.moa.id.auth.validator; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.auth.data.CreateXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.data.SAMLAttribute; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * + * This class is used to validate an {@link CreateXMLSignatureResponse} + * returned by the security layer. + * This class implements the Singleton pattern. + * @author Stefan Knirsch + * @version $Id$ + */ +public class CreateXMLSignatureResponseValidator { + + /** Xpath prefix for reaching SecurityLayer 1.0 Namespaces */ + private static final String SAML = Constants.SAML_PREFIX + ":"; + /** Xpath prefix for reaching XML-DSIG Namespaces */ + private static final String DSIG = Constants.DSIG_PREFIX + ":"; + /** Xpath expression to the SAML:Assertion element */ + private static final String ROOT = SAML + "Assertion"; + /** Xpath expression to the SAML:NameIdentifier element */ + private static final String SAML_SUBJECT_NAME_IDENTIFIER_XPATH = + SAML + "AttributeStatement/" + SAML + "Subject/" + + SAML + "NameIdentifier"; + /** Xpath expression to the SAML:Attribute element */ + private static final String SAML_ATTRIBUTE_XPATH = + ROOT + "/" + SAML + "AttributeStatement/" + SAML + "Attribute"; + /** Xpath expression to the SAML:AttributeValue element */ + private static final String SAML_ATTRIBUTE_VALUE_XPATH = + SAML + "AttributeValue"; + + + /** Singleton instance. <code>null</code>, if none has been created. */ + private static CreateXMLSignatureResponseValidator instance; + + /** + * Constructor for a singleton CreateXMLSignatureResponseValidator. + * @return an instance of CreateXMLSignatureResponseValidator + * @throws ValidateException if no instance can be created + */ + public static synchronized CreateXMLSignatureResponseValidator getInstance() + throws ValidateException { + if (instance == null) { + instance = new CreateXMLSignatureResponseValidator(); + } + return instance; + } + + + /** + * The Method validate is used for validating an explicit {@link CreateXMLSignatureResponse} + * @param createXMLSignatureResponse + * @param gbTarget + * @param oaURL + * @throws ValidateException + */ + public void validate(CreateXMLSignatureResponse createXMLSignatureResponse, String gbTarget, String oaURL) + throws ValidateException { + + // A3.056: more then one /saml:Assertion/saml:AttributeStatement/saml:Subject/saml:NameIdentifier + + XPathUtils.selectNodeList(createXMLSignatureResponse.getSamlAssertion(),SAML_SUBJECT_NAME_IDENTIFIER_XPATH); + + SAMLAttribute[] samlattributes = createXMLSignatureResponse.getSamlAttributes(); + boolean foundOA = false; + boolean foundGB = false; + for (int i = 0; i < samlattributes.length; i++) + { + if (samlattributes[i].getName().equals("Geschäftsbereich")) + if (samlattributes[i].getNamespace().equals("http://reference.e-government.gv.at/namespace/moa/20020822#")) + + { + foundGB = true; + if (!gbTarget.equals(samlattributes[i].getValue())) + { + throw new ValidateException("validator.13", null); + } + } + else throw new ValidateException("validator.12", null); + if (samlattributes[i].getName().equals("OA")) + if (samlattributes[i].getNamespace().equals("http://reference.e-government.gv.at/namespace/moa/20020822#")) + { + foundOA = true; + if (!oaURL.equals(samlattributes[i].getValue())) // CHECKS für die AttributeVALUES fehlen noch + { + throw new ValidateException("validator.16", new Object[] {":gefunden wurde '" + oaURL + "', erwartet wurde '" + samlattributes[i].getValue()}); + } + + } + else throw new ValidateException("validator.15", null); + } + if (!foundOA) throw new ValidateException("validator.14", null); + if (!foundGB) throw new ValidateException("validator.11", null); + + //Check if dsig:Signature exists + Element dsigSignature = (Element) XPathUtils.selectSingleNode(createXMLSignatureResponse.getSamlAssertion(),DSIG + "Signature"); + if (dsigSignature==null) throw new ValidateException("validator.05", null); + + + } +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/validator/IdentityLinkValidator.java b/id.server/src/at/gv/egovernment/moa/id/auth/validator/IdentityLinkValidator.java new file mode 100644 index 000000000..42e3e946f --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/validator/IdentityLinkValidator.java @@ -0,0 +1,156 @@ +package at.gv.egovernment.moa.id.auth.validator; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * + * This class is used to validate an {@link IdentityLink} + * returned by the security layer + * + * @author Stefan Knirsch + * @version $Id$ + */ +public class IdentityLinkValidator implements Constants { + + // + // XPath namespace prefix shortcuts + // + /** Xpath prefix for reaching PersonData Namespaces */ + private static final String PDATA = PD_PREFIX + ":"; + /** Xpath prefix for reaching SAML Namespaces */ + private static final String SAML = SAML_PREFIX + ":"; + /** Xpath prefix for reaching XML-DSIG Namespaces */ + private static final String DSIG = DSIG_PREFIX + ":"; + /** Xpath prefix for reaching ECDSA Namespaces */ + private static final String ECDSA = ECDSA_PREFIX + ":"; + /** Xpath expression to the root element */ + private static final String ROOT = "/" + SAML + "Assertion/"; + /** Xpath expression to the SAML:SubjectConfirmationData element */ + private static final String SAML_SUBJECT_CONFIRMATION_DATA_XPATH = + ROOT + + SAML + + "AttributeStatement/" + + SAML + + "Subject/" + + SAML + + "SubjectConfirmation/" + + SAML + + "SubjectConfirmationData"; +/** Xpath expression to the PersonData:Person element */ + private static final String PERSON_XPATH = + SAML_SUBJECT_CONFIRMATION_DATA_XPATH + "/" + PDATA + "Person"; + /** Xpath expression to the SAML:Attribute element */ + private static final String ATTRIBUTE_XPATH = + ROOT + SAML + "AttributeStatement/" + SAML + "Attribute"; + /** Xpath expression to the SAML:AttributeName attribute */ + private static final String ATTRIBUTE_NAME_XPATH = + ROOT + SAML + "AttributeStatement/" + SAML + "Attribute/@AttributeName"; + /** Xpath expression to the SAML:AttributeNamespace attribute */ + private static final String ATTRIBUTE_NAMESPACE_XPATH = + ROOT + + SAML + + "AttributeStatement/" + + SAML + + "Attribute/@AttributeNamespace"; + /** Xpath expression to the SAML:AttributeValue element */ + private static final String ATTRIBUTE_VALUE_XPATH = + ROOT + + SAML + + "AttributeStatement/" + + SAML + + "Attribute/" + + SAML + + "AttributeValue"; + + /** Singleton instance. <code>null</code>, if none has been created. */ + private static IdentityLinkValidator instance; + + /** + * Constructor for a singleton IdentityLinkValidator. + * @return a new IdentityLinkValidator instance + * @throws ValidateException if no instance can be created + */ + public static synchronized IdentityLinkValidator getInstance() + throws ValidateException { + if (instance == null) { + instance = new IdentityLinkValidator(); + } + return instance; + } + + /** + * Method validate. Validates the {@link IdentityLink} + * @param identityLink The identityLink to validate + * @throws ValidateException on any validation error + */ + public void validate(IdentityLink identityLink) throws ValidateException { + + //Search the SAML:ASSERTION Object (A2.054) + if (identityLink.getSamlAssertion() == null) + throw new ValidateException("validator.00", null); + + // Check how many saml:Assertion/saml:AttributeStatement/ + // saml:Subject/ saml:SubjectConfirmation/ + // saml:SubjectConfirmationData/pr:Person of type + // PhysicalPersonType exist (A2.056) + NodeList nl = + XPathUtils.selectNodeList(identityLink.getSamlAssertion(), PERSON_XPATH); + // If we have just one Person-Element we don't need to check the attributes + int counterPhysicalPersonType = 0; + if (nl.getLength() > 1) + for (int i = 0; i < nl.getLength(); i++) { + String xsiType = + ((Element) nl.item(i)) + .getAttributeNodeNS( + "http://www.w3.org/2001/XMLSchema-instance", + "type") + .getNodeValue(); + // We have to check if xsiType contains "PhysicalPersonType" + // An equal-check will fail because of the Namespace-prefix of the attribute value + if (xsiType.indexOf("PhysicalPersonType") > -1) + counterPhysicalPersonType++; + } + if (counterPhysicalPersonType > 1) + throw new ValidateException("validator.01", null); + + //Check the SAML:ATTRIBUTES + nl = XPathUtils.selectNodeList(identityLink.getSamlAssertion(), ATTRIBUTE_XPATH); + for (int i = 0; i < nl.getLength(); i++) { + String attributeName = + XPathUtils.getAttributeValue( + (Element) nl.item(i), + "@AttributeName", + null); + String attributeNS = + XPathUtils.getAttributeValue( + (Element) nl.item(i), + "@AttributeNamespace", + null); + if (attributeName.equals("CitizenPublicKey")) { + if (attributeNS.equals("http://www.buergerkarte.at/namespaces/personenbindung/20020506#")) { + Element attributeValue = + (Element) XPathUtils.selectSingleNode((Element) nl.item(i),SAML + "AttributeValue/" + DSIG + "RSAKeyValue"); + if (attributeValue==null) + attributeValue = + (Element) XPathUtils.selectSingleNode((Element)nl.item(i), SAML + "AttributeValue/" + ECDSA + "ECDSAKeyValue"); + if (attributeValue == null) + throw new ValidateException("validator.02", null); + } + else + throw new ValidateException("validator.03", null); + } + else + throw new ValidateException("validator.04", null); + } + + //Check if dsig:Signature exists + Element dsigSignature = (Element) XPathUtils.selectSingleNode(identityLink.getSamlAssertion(),ROOT + DSIG + "Signature"); + if (dsigSignature==null) throw new ValidateException("validator.05", null); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/validator/ValidateException.java b/id.server/src/at/gv/egovernment/moa/id/auth/validator/ValidateException.java new file mode 100644 index 000000000..a6685fca8 --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/validator/ValidateException.java @@ -0,0 +1,35 @@ +package at.gv.egovernment.moa.id.auth.validator; + +import at.gv.egovernment.moa.id.MOAIDException; + +/** + * Exception thrown while validating an incoming XML structure + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class ValidateException extends MOAIDException { + + /** + * Constructor for ValidateException. + * @param messageId + * @param parameters + */ + public ValidateException(String messageId, Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for ValidateException. + * @param messageId + * @param parameters + * @param wrapped + */ + public ValidateException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id.server/src/at/gv/egovernment/moa/id/auth/validator/VerifyXMLSignatureResponseValidator.java b/id.server/src/at/gv/egovernment/moa/id/auth/validator/VerifyXMLSignatureResponseValidator.java new file mode 100644 index 000000000..a238d28cb --- /dev/null +++ b/id.server/src/at/gv/egovernment/moa/id/auth/validator/VerifyXMLSignatureResponseValidator.java @@ -0,0 +1,124 @@ +package at.gv.egovernment.moa.id.auth.validator; + +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; + +import iaik.asn1.structures.Name; +import iaik.utils.RFC2253NameParserException; +import iaik.x509.X509Certificate; + +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.id.auth.data.VerifyXMLSignatureResponse; + +/** + * This class is used to validate an {@link VerifyXMLSignatureResponse} + * returned by MOA-SPSS + * + * @author Stefan Knirsch + * @version $Id$ + */ +public class VerifyXMLSignatureResponseValidator { + + /** Identification string for checking identity link */ + public static final String CHECK_IDENTITY_LINK = "IdentityLink"; + /** Identification string for checking authentication block */ + public static final String CHECK_AUTH_BLOCK = "AuthBlock"; + + /** Singleton instance. <code>null</code>, if none has been created. */ + private static VerifyXMLSignatureResponseValidator instance; + + /** + * Constructor for a singleton VerifyXMLSignatureResponseValidator. + */ + public static synchronized VerifyXMLSignatureResponseValidator getInstance() + throws ValidateException { + if (instance == null) { + instance = new VerifyXMLSignatureResponseValidator(); + } + return instance; + } + + /** + * Validates a {@link VerifyXMLSignatureResponse} returned by MOA-SPSS. + * + * @param verifyXMLSignatureResponse the <code><VerifyXMLSignatureResponse></code> + * @param identityLinkSignersSubjectDNNames subject names configured + * @param whatToCheck is used to identify whether the identityLink or the Auth-Block is validated + * @throws ValidateException on any validation error + */ + public void validate( + VerifyXMLSignatureResponse verifyXMLSignatureResponse, + String[] identityLinkSignersSubjectDNNames, String whatToCheck) + throws ValidateException { + + if (verifyXMLSignatureResponse.getSignatureCheckCode() != 0) + throw new ValidateException("validator.06", null); + if (verifyXMLSignatureResponse.getCertificateCheckCode() != 0) + if (whatToCheck.equals(CHECK_IDENTITY_LINK)) + throw new ValidateException("validator.07", null); + else + throw new ValidateException("validator.19", null); + if (verifyXMLSignatureResponse.isXmlDSIGManigest()) + if (verifyXMLSignatureResponse.getXmlDSIGManifestCheckCode() != 0) + throw new ValidateException("validator.08", null); + //Check whether the returned X509 SubjectName is in the MOA-ID configuration or not + if (identityLinkSignersSubjectDNNames != null) { + String subjectDN = ""; + X509Certificate x509Cert = verifyXMLSignatureResponse.getX509certificate(); + try { + subjectDN = ((Name) x509Cert.getSubjectDN()).getRFC2253String(); + } + catch (RFC2253NameParserException e) { + throw new ValidateException("validator.17", null); + } + boolean found = false; + for (int i = 0; i < identityLinkSignersSubjectDNNames.length; i++) { + if (identityLinkSignersSubjectDNNames[i].equals(subjectDN)) + found = true; + } + if (!found) + throw new ValidateException( + "validator.18", + new Object[] { subjectDN }); + } + } + + /** + * Method validateCertificate. + * @param vsr is the VerifyXMLSignatureResponse + * @param idl + * @throws ValidateException + */ + public void validateCertificate( + VerifyXMLSignatureResponse verifyXMLSignatureResponse, + IdentityLink idl) + throws ValidateException { + + X509Certificate x509Response = verifyXMLSignatureResponse.getX509certificate(); + PublicKey[] pubKeysIdentityLink = (PublicKey[]) idl.getPublicKey(); + + RSAPublicKey pubKeyResponse = (RSAPublicKey) x509Response.getPublicKey(); + + boolean found = false; + for (int i = 0; i < pubKeysIdentityLink.length; i++) { + if (idl.getPublicKey()[i] + instanceof java.security.interfaces.RSAPublicKey) { + /* for (int j = 0; + j < idl.getPublicKey()[i].getClass().getInterfaces().length; + j++) { + if (idl.getPublicKey()[i].getClass().getInterfaces()[j].getName() + .equals("java.security.interfaces.RSAPublicKey")) {*/ + RSAPublicKey rsakey = (RSAPublicKey) pubKeysIdentityLink[i]; + if (rsakey.getModulus().equals(pubKeyResponse.getModulus()) + && rsakey.getPublicExponent().equals( + pubKeyResponse.getPublicExponent())) + found = true; + } + + } + + if (!found) + throw new ValidateException("validator.09", null); + } + +} |