diff options
Diffstat (limited to 'id/server/idserverlib/src/main/java')
118 files changed, 18184 insertions, 0 deletions
diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/AuthenticationException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/AuthenticationException.java new file mode 100644 index 000000000..96a5e0673 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/AuthenticationException.java @@ -0,0 +1,31 @@ +package at.gv.egovernment.moa.id; + + +/** + * Exception thrown during handling of AuthenticationSession + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationException extends MOAIDException { + + /** + * Constructor for AuthenticationException. + * @param messageId + */ + public AuthenticationException(String messageId, Object[] parameters) { + super(messageId, parameters, null); + } + /** + * Constructor for AuthenticationException. + * @param messageId + * @param parameters + * @param wrapped + */ + public AuthenticationException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/BuildException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/BuildException.java new file mode 100644 index 000000000..785dce7a3 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/BuildException.java @@ -0,0 +1,34 @@ +package at.gv.egovernment.moa.id; + + +/** + * Exception thrown while building an XML or HTML structure. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class BuildException extends MOAIDException { + + /** + * Constructor for BuildException. + * @param messageId + * @param parameters + */ + public BuildException(String messageId, Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for BuildException. + * @param messageId + * @param parameters + * @param wrapped + */ + public BuildException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ECDSAConverterException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ECDSAConverterException.java new file mode 100644 index 000000000..682395a83 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ECDSAConverterException.java @@ -0,0 +1,34 @@ +package at.gv.egovernment.moa.id; + + +/** + * Exception thrown while converting ECDSAKeys from/to an XML structure. + * + * @author Rudolf Schamberger + * @version $Id$ + */ +public class ECDSAConverterException extends MOAIDException { + + /** + * Constructor for ECDSAConverterException. + * @param messageId + * @param parameters + */ + public ECDSAConverterException(String messageId, Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for ECDSAConverterException. + * @param messageId + * @param parameters + * @param wrapped + */ + public ECDSAConverterException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/MOAIDException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/MOAIDException.java new file mode 100644 index 000000000..bce2c4778 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/MOAIDException.java @@ -0,0 +1,159 @@ +package at.gv.egovernment.moa.id; + +import java.io.PrintStream; +import java.io.PrintWriter; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.util.Constants; + +/** + * Base class of technical MOA exceptions. + * + * Technical exceptions are exceptions that originate from system failure (e.g., + * a database connection fails, a component is not available, etc.) + * + * @author Patrick Peck, Ivancsics Paul + * @version $Id$ + */ +public class MOAIDException extends Exception { + /** message ID */ + private String messageId; + /** wrapped exception */ + private Throwable wrapped; + + /** + * Create a new <code>MOAIDException</code>. + * + * @param messageId The identifier of the message associated with this + * exception. + * @param parameters Additional message parameters. + */ + public MOAIDException(String messageId, Object[] parameters) { + super(MOAIDMessageProvider.getInstance().getMessage(messageId, parameters)); + this.messageId = messageId; + } + + /** + * Create a new <code>MOAIDException</code>. + * + * @param messageId The identifier of the message associated with this + * <code>MOAIDException</code>. + * @param parameters Additional message parameters. + * @param wrapped The exception wrapped by this + * <code>MOAIDException</code>. + */ + public MOAIDException( + String messageId, + Object[] parameters, + Throwable wrapped) { + + super(MOAIDMessageProvider.getInstance().getMessage(messageId, parameters)); + this.messageId = messageId; + this.wrapped = wrapped; + } + + /** + * Print a stack trace of this exception to <code>System.err</code>. + * + * @see java.lang.Throwable#printStackTrace() + */ + public void printStackTrace() { + printStackTrace(System.err); + } + + /** + * Print a stack trace of this exception, including the wrapped exception. + * + * @param s The stream to write the stack trace to. + * @see java.lang.Throwable#printStackTrace(java.io.PrintStream) + */ + public void printStackTrace(PrintStream s) { + if (getWrapped() == null) + super.printStackTrace(s); + else { + s.print("Root exception: "); + getWrapped().printStackTrace(s); + } + } + + /** + * Print a stack trace of this exception, including the wrapped exception. + * + * @param s The stream to write the stacktrace to. + * @see java.lang.Throwable#printStackTrace(java.io.PrintWriter) + */ + public void printStackTrace(PrintWriter s) { + if (getWrapped() == null) + super.printStackTrace(s); + else { + s.print("Root exception: "); + getWrapped().printStackTrace(s); + } + } + + /** + * @return message ID + */ + public String getMessageId() { + return messageId; + } + + /** + * @return wrapped exception + */ + public Throwable getWrapped() { + return wrapped; + } + + /** + * Convert this <code>MOAIDException</code> to an <code>ErrorResponse</code> + * element from the MOA namespace. + * + * @return An <code>ErrorResponse</code> element, containing the subelements + * <code>ErrorCode</code> and <code>Info</code> required by the MOA schema. + */ + public Element toErrorResponse() { + DocumentBuilder builder; + DOMImplementation impl; + Document doc; + Element errorResponse; + Element errorCode; + Element info; + + // create a new document + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + impl = builder.getDOMImplementation(); + } catch (ParserConfigurationException e) { + return null; + } + + // build the ErrorResponse element + doc = impl.createDocument(Constants.MOA_NS_URI, "ErrorResponse", null); + errorResponse = doc.getDocumentElement(); + + // add MOA namespace declaration + errorResponse.setAttributeNS( + Constants.XMLNS_NS_URI, + "xmlns", + Constants.MOA_NS_URI); + + // build the child elements + errorCode = doc.createElementNS(Constants.MOA_NS_URI, "ErrorCode"); + errorCode.appendChild(doc.createTextNode(messageId)); + info = doc.createElementNS(Constants.MOA_NS_URI, "Info"); + info.appendChild(doc.createTextNode(toString())); + errorResponse.appendChild(errorCode); + errorResponse.appendChild(info); + return errorResponse; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ParseException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ParseException.java new file mode 100644 index 000000000..a5e0088d9 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ParseException.java @@ -0,0 +1,34 @@ +package at.gv.egovernment.moa.id; + + +/** + * Exception thrown while parsing an XML structure. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class ParseException extends MOAIDException { + + /** + * Constructor for ParseException. + * @param messageId + * @param parameters + */ + public ParseException(String messageId, Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for ParseException. + * @param messageId + * @param parameters + * @param wrapped + */ + public ParseException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ServiceException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ServiceException.java new file mode 100644 index 000000000..9e6ab2361 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/ServiceException.java @@ -0,0 +1,34 @@ +package at.gv.egovernment.moa.id; + + +/** + * Exception thrown while calling the MOA-SPSS web service. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class ServiceException extends MOAIDException { + + /** + * Constructor for ServiceException. + * @param messageId + * @param parameters + */ + public ServiceException(String messageId, Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for ServiceException. + * @param messageId + * @param parameters + * @param wrapped + */ + public ServiceException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java new file mode 100644 index 000000000..5f4ec2d29 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationServer.java @@ -0,0 +1,1019 @@ +package at.gv.egovernment.moa.id.auth; + +import iaik.pki.PKIException; +import iaik.x509.X509Certificate; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import javax.xml.transform.TransformerException; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.AuthenticationException; +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.ServiceException; +import at.gv.egovernment.moa.id.auth.builder.AuthenticationBlockAssertionBuilder; +import at.gv.egovernment.moa.id.auth.builder.AuthenticationDataAssertionBuilder; +import at.gv.egovernment.moa.id.auth.builder.BPKBuilder; +import at.gv.egovernment.moa.id.auth.builder.CertInfoVerifyXMLSignatureRequestBuilder; +import at.gv.egovernment.moa.id.auth.builder.CreateXMLSignatureRequestBuilder; +import at.gv.egovernment.moa.id.auth.builder.DataURLBuilder; +import at.gv.egovernment.moa.id.auth.builder.GetIdentityLinkFormBuilder; +import at.gv.egovernment.moa.id.auth.builder.InfoboxReadRequestBuilder; +import at.gv.egovernment.moa.id.auth.builder.InfoboxValidatorParamsBuilder; +import at.gv.egovernment.moa.id.auth.builder.PersonDataBuilder; +import at.gv.egovernment.moa.id.auth.builder.SAMLArtifactBuilder; +import at.gv.egovernment.moa.id.auth.builder.SelectBKUFormBuilder; +import at.gv.egovernment.moa.id.auth.builder.VerifyXMLSignatureRequestBuilder; +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.id.auth.data.CreateXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.id.auth.data.InfoboxValidationResult; +import at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams; +import at.gv.egovernment.moa.id.auth.data.VerifyXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.invoke.SignatureVerificationInvoker; +import at.gv.egovernment.moa.id.auth.parser.CreateXMLSignatureResponseParser; +import at.gv.egovernment.moa.id.auth.parser.ExtendedInfoboxReadResponseParser; +import at.gv.egovernment.moa.id.auth.parser.InfoboxReadResponseParser; +import at.gv.egovernment.moa.id.auth.parser.SAMLArtifactParser; +import at.gv.egovernment.moa.id.auth.parser.VerifyXMLSignatureResponseParser; +import at.gv.egovernment.moa.id.auth.servlet.AuthServlet; +import at.gv.egovernment.moa.id.auth.validator.CreateXMLSignatureResponseValidator; +import at.gv.egovernment.moa.id.auth.validator.IdentityLinkValidator; +import at.gv.egovernment.moa.id.auth.validator.InfoboxValidator; +import at.gv.egovernment.moa.id.auth.validator.ValidateException; +import at.gv.egovernment.moa.id.auth.validator.VerifyXMLSignatureResponseValidator; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConfigurationProvider; +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; +import at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameter; +import at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameters; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.id.util.HTTPUtils; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.id.util.Random; +import at.gv.egovernment.moa.id.util.SSLUtils; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.BoolUtils; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.DateTimeUtils; +import at.gv.egovernment.moa.util.FileUtils; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * API for MOA ID Authentication Service.<br> + * {@link AuthenticationSession} is stored in a session store and retrieved + * by giving the session ID. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationServer implements MOAIDAuthConstants { + + /** single instance */ + private static AuthenticationServer instance; + /** session data store (session ID -> AuthenticationSession) */ + private static Map sessionStore = new HashMap(); + /** authentication data store (assertion handle -> AuthenticationData) */ + private static Map authenticationDataStore = new HashMap(); + /** + * time out in milliseconds used by {@link cleanup} for session store + */ + private long sessionTimeOut = 10 * 60 * 1000; // default 10 minutes + /** + * time out in milliseconds used by {@link cleanup} for authentication data store + */ + private long authDataTimeOut = 2 * 60 * 1000; // default 2 minutes + + /** + * Returns the single instance of <code>AuthenticationServer</code>. + * + * @return the single instance of <code>AuthenticationServer</code> + */ + public static AuthenticationServer getInstance() { + if (instance == null) + instance = new AuthenticationServer(); + return instance; + } + /** + * Constructor for AuthenticationServer. + */ + public AuthenticationServer() { + super(); + } + /** + * Processes request to select a BKU. + * <br/>Processing depends on value of {@link AuthConfigurationProvider#getBKUSelectionType}. + * <br/>For <code>bkuSelectionType==HTMLComplete</code>, a <code>returnURI</code> for the + * "BKU Auswahl" service is returned. + * <br/>For <code>bkuSelectionType==HTMLSelect</code>, an HTML form for BKU selection is returned. + * @param authURL base URL of MOA-ID Auth component + * @param target "Geschäftsbereich" + * @param oaURL online application URL requested + * @param bkuSelectionTemplateURL template for BKU selection form to be used + * in case of <code>HTMLSelect</code>; may be null + * @param templateURL URL providing an HTML template for the HTML form to be used + * for call <code>startAuthentication</code> + * @return for <code>bkuSelectionType==HTMLComplete</code>, the <code>returnURI</code> for the + * "BKU Auswahl" service; + * for <code>bkuSelectionType==HTMLSelect</code>, an HTML form for BKU selection + * @throws WrongParametersException upon missing parameters + * @throws AuthenticationException when the configured BKU selection service cannot be reached, + * and when the given bkuSelectionTemplateURL cannot be reached + * @throws ConfigurationException on missing configuration data + * @throws BuildException while building the HTML form + */ + public String selectBKU( + String authURL, + String target, + String oaURL, + String bkuSelectionTemplateURL, + String templateURL) + throws WrongParametersException, AuthenticationException, ConfigurationException, BuildException { + + //check if HTTP Connection may be allowed (through FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY) + String boolStr = AuthConfigurationProvider.getInstance().getGenericConfigurationParameter( + AuthConfigurationProvider.FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY); + if ((!authURL.startsWith("https:")) && (false == BoolUtils.valueOf(boolStr))) + throw new AuthenticationException("auth.07", new Object[] { authURL + "*" }); + if (isEmpty(authURL)) + throw new WrongParametersException("StartAuthentication", "AuthURL"); + if (isEmpty(oaURL)) + throw new WrongParametersException("StartAuthentication", PARAM_OA); + + ConnectionParameter bkuConnParam = + AuthConfigurationProvider.getInstance().getBKUConnectionParameter(); + if (bkuConnParam == null) + throw new ConfigurationException( + "config.08", + new Object[] { "BKUSelection/ConnectionParameter" }); + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(oaURL); + if (oaParam == null) + throw new AuthenticationException("auth.00", new Object[] { oaURL }); + + if (!oaParam.getBusinessService()) { + if (isEmpty(target)) + throw new WrongParametersException("StartAuthentication", PARAM_TARGET); + } else { + if (!isEmpty(target)) { + Logger.info("Ignoring target parameter thus application type is \"businessService\""); + } + target = null; + } + + AuthenticationSession session = newSession(); + Logger.info("MOASession " + session.getSessionID() + " angelegt"); + session.setTarget(target); + session.setOAURLRequested(oaURL); + session.setPublicOAURLPrefix(oaParam.getPublicURLPrefix()); + session.setAuthURL(authURL); + session.setTemplateURL(templateURL); + session.setBusinessService(oaParam.getBusinessService()); + String returnURL = + new DataURLBuilder().buildDataURL(authURL, REQ_START_AUTHENTICATION, session.getSessionID()); + String bkuSelectionType = AuthConfigurationProvider.getInstance().getBKUSelectionType(); + if (bkuSelectionType.equals(AuthConfigurationProvider.BKU_SELECTION_TYPE_HTMLCOMPLETE)) { + // bkuSelectionType==HTMLComplete + String redirectURL = bkuConnParam.getUrl() + "?" + AuthServlet.PARAM_RETURN + "=" + returnURL; + return redirectURL; + } else { + // bkuSelectionType==HTMLSelect + String bkuSelectTag; + try { + bkuSelectTag = readBKUSelectTag(AuthConfigurationProvider.getInstance(), bkuConnParam); + } catch (Throwable ex) { + throw new AuthenticationException( + "auth.11", + new Object[] { bkuConnParam.getUrl(), ex.toString()}, + ex); + } + String bkuSelectionTemplate = null; + // override template url by url from configuration file + if (oaParam.getBkuSelectionTemplateURL() != null) { + bkuSelectionTemplateURL = oaParam.getBkuSelectionTemplateURL(); + } + if (bkuSelectionTemplateURL != null) { + try { + bkuSelectionTemplate = new String(FileUtils.readURL(bkuSelectionTemplateURL)); + } catch (IOException ex) { + throw new AuthenticationException( + "auth.03", + new Object[] { bkuSelectionTemplateURL, ex.toString()}, + ex); + } + } + String htmlForm = + new SelectBKUFormBuilder().build(bkuSelectionTemplate, returnURL, bkuSelectTag); + return htmlForm; + } + } + /** + * Method readBKUSelectTag. + * @param conf the ConfigurationProvider + * @param connParam the ConnectionParameter for that connection + * @return String + * @throws ConfigurationException on config-errors + * @throws PKIException on PKI errors + * @throws IOException on any data error + * @throws GeneralSecurityException on security errors + */ + private String readBKUSelectTag(ConfigurationProvider conf, ConnectionParameter connParam) + throws ConfigurationException, PKIException, IOException, GeneralSecurityException { + + if (connParam.isHTTPSURL()) + return SSLUtils.readHttpsURL(conf, connParam); + else + return HTTPUtils.readHttpURL(connParam.getUrl()); + } + /** + * Processes the beginning of an authentication session. + * <ul> + * <li>Starts an authentication session</li> + * <li>Creates an <code><InfoboxReadRequest></code></li> + * <li>Creates an HTML form for querying the identity link from the + * security layer implementation. + * <br>Form parameters include + * <ul> + * <li>the <code><InfoboxReadRequest></code></li> + * <li>the data URL where the security layer implementation sends it response to</li> + * </ul> + * </ul> + * @param authURL URL of the servlet to be used as data URL + * @param target "Geschäftsbereich" of the online application requested + * @param oaURL online application URL requested + * @param bkuURL URL of the "Bürgerkartenumgebung" to be used; + * may be <code>null</code>; in this case, the default location will be used + * @param templateURL URL providing an HTML template for the HTML form generated + * @return HTML form + * @throws AuthenticationException + * @see GetIdentityLinkFormBuilder + * @see InfoboxReadRequestBuilder + */ + public String startAuthentication( + String authURL, + String target, + String oaURL, + String templateURL, + String bkuURL, + String sessionID) + throws WrongParametersException, AuthenticationException, ConfigurationException, BuildException { + + if (isEmpty(sessionID)) { + if (isEmpty(authURL)) + throw new WrongParametersException("StartAuthentication", "AuthURL"); + + //check if HTTP Connection may be allowed (through FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY) + String boolStr = + AuthConfigurationProvider.getInstance().getGenericConfigurationParameter( + AuthConfigurationProvider.FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY); + if ((!authURL.startsWith("https:")) && (false == BoolUtils.valueOf(boolStr))) + throw new AuthenticationException("auth.07", new Object[] { authURL + "*" }); + if (isEmpty(oaURL)) + throw new WrongParametersException("StartAuthentication", PARAM_OA); + } + AuthenticationSession session; + OAAuthParameter oaParam; + if (sessionID != null) { + session = getSession(sessionID); + oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter( + session.getPublicOAURLPrefix()); + } else { + oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(oaURL); + if (oaParam == null) + throw new AuthenticationException("auth.00", new Object[] { oaURL }); + if (!oaParam.getBusinessService()) { + if (isEmpty(target)) + throw new WrongParametersException("StartAuthentication", PARAM_TARGET); + } else { + target = null; + } + session = newSession(); + Logger.info("MOASession " + session.getSessionID() + " angelegt"); + session.setTarget(target); + session.setOAURLRequested(oaURL); + session.setPublicOAURLPrefix(oaParam.getPublicURLPrefix()); + session.setAuthURL(authURL); + session.setTemplateURL(templateURL); + session.setBusinessService(oaParam.getBusinessService()); + } + // BKU URL has not been set yet, even if session already exists + if (bkuURL == null) { + bkuURL = DEFAULT_BKU; + } + session.setBkuURL(bkuURL); + String infoboxReadRequest = + new InfoboxReadRequestBuilder().build(oaParam.getSlVersion12(), + oaParam.getBusinessService(), + oaParam.getIdentityLinkDomainIdentifier()); + String dataURL = + new DataURLBuilder().buildDataURL( + session.getAuthURL(), + REQ_VERIFY_IDENTITY_LINK, + session.getSessionID()); + String template = null; + // override template url by url from configuration file + if (oaParam.getTemplateURL() != null) { + templateURL = oaParam.getTemplateURL(); + } else { + templateURL = session.getTemplateURL(); + } + if (templateURL != null) { + try { + template = new String(FileUtils.readURL(templateURL)); + } catch (IOException ex) { + throw new AuthenticationException( + "auth.03", + new Object[] { templateURL, ex.toString()}, + ex); + } + } + String pushInfobox = ""; + VerifyInfoboxParameters verifyInfoboxParameters = oaParam.getVerifyInfoboxParameters(); + if (verifyInfoboxParameters != null) { + pushInfobox = verifyInfoboxParameters.getPushInfobox(); + } + String certInfoRequest = new CertInfoVerifyXMLSignatureRequestBuilder().build(oaParam.getSlVersion12()); + String certInfoDataURL = + new DataURLBuilder().buildDataURL( + session.getAuthURL(), + REQ_START_AUTHENTICATION, + session.getSessionID()); + String htmlForm = + new GetIdentityLinkFormBuilder().build( + template, + bkuURL, + infoboxReadRequest, + dataURL, + certInfoRequest, + certInfoDataURL, + pushInfobox); + return htmlForm; + } + /** + * Processes an <code><InfoboxReadResponse></code> sent by the + * security layer implementation.<br> + * <ul> + * <li>Validates given <code><InfoboxReadResponse></code></li> + * <li>Parses identity link enclosed in <code><InfoboxReadResponse></code></li> + * <li>Verifies identity link by calling the MOA SP component</li> + * <li>Checks certificate authority of identity link</li> + * <li>Stores identity link in the session</li> + * <li>Verifies all additional infoboxes returned from the BKU</li> + * <li>Creates an authentication block to be signed by the user</li> + * <li>Creates and returns a <code><CreateXMLSignatureRequest></code> + * containg the authentication block, meant to be returned to the + * security layer implementation</li> + * </ul> + * + * @param sessionID ID of associated authentication session data + * @param infoboxReadResponseParameters The parameters from the response returned from + * the BKU including the <code><InfoboxReadResponse></code> + * @return String representation of the <code><CreateXMLSignatureRequest></code> + */ + public String verifyIdentityLink(String sessionID, Map infoboxReadResponseParameters) + throws + AuthenticationException, + BuildException, + ParseException, + ConfigurationException, + ValidateException, + ServiceException { + + if (isEmpty(sessionID)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_IDENTITY_LINK, PARAM_SESSIONID}); + + String xmlInfoboxReadResponse = (String)infoboxReadResponseParameters.get(PARAM_XMLRESPONSE); + if (isEmpty(xmlInfoboxReadResponse)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_IDENTITY_LINK, PARAM_XMLRESPONSE}); + + AuthenticationSession session = getSession(sessionID); + if (session.getTimestampIdentityLink() != null) + throw new AuthenticationException("auth.01", new Object[] { sessionID }); + session.setTimestampIdentityLink(); + AuthConfigurationProvider authConf = AuthConfigurationProvider.getInstance(); + // parses the <InfoboxReadResponse> + IdentityLink identityLink = + new InfoboxReadResponseParser(xmlInfoboxReadResponse).parseIdentityLink(); + // validates the identity link + IdentityLinkValidator.getInstance().validate(identityLink); + // builds a <VerifyXMLSignatureRequest> for a call of MOA-SP + Element domVerifyXMLSignatureRequest = + new VerifyXMLSignatureRequestBuilder().build( + identityLink, + authConf.getMoaSpIdentityLinkTrustProfileID()); + + // invokes the call + Element domVerifyXMLSignatureResponse = + new SignatureVerificationInvoker().verifyXMLSignature(domVerifyXMLSignatureRequest); + // parses the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponse verifyXMLSignatureResponse = + new VerifyXMLSignatureResponseParser(domVerifyXMLSignatureResponse).parseData(); + + if (identityLink.getIdentificationType().equalsIgnoreCase(Constants.URN_PREFIX_BASEID)) { + } + + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter( + session.getPublicOAURLPrefix()); + + // if OA is type is business service the manifest validation result has to be ignored + boolean ignoreManifestValidationResult = oaParam.getBusinessService() ? true : false; + + // validates the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponseValidator.getInstance().validate( + verifyXMLSignatureResponse, + authConf.getIdentityLinkX509SubjectNames(), + VerifyXMLSignatureResponseValidator.CHECK_IDENTITY_LINK, + ignoreManifestValidationResult); + + session.setIdentityLink(identityLink); + // now validate the extended infoboxes + verifyInfoboxes(session, infoboxReadResponseParameters, !oaParam.getProvideStammzahl()); + // builds the AUTH-block + String authBlock = buildAuthenticationBlock(session); +// session.setAuthBlock(authBlock); + // builds the <CreateXMLSignatureRequest> + String[] transformsInfos = oaParam.getTransformsInfos(); + if ((transformsInfos == null) || (transformsInfos.length == 0)) { + // no OA specific transforms specified, use default ones + transformsInfos = authConf.getTransformsInfos(); + } + String createXMLSignatureRequest = + new CreateXMLSignatureRequestBuilder().build(authBlock, + oaParam.getKeyBoxIdentifier(), + transformsInfos, + oaParam.getSlVersion12()); + return createXMLSignatureRequest; + } + /** + * Builds an authentication block <code><saml:Assertion></code> from given session data. + * @param session authentication session + * + * @return <code><saml:Assertion></code> as a String + * + * @throws BuildException If an error occurs on serializing an extended SAML attribute + * to be appended to the AUTH-Block. + */ + private String buildAuthenticationBlock(AuthenticationSession session) throws BuildException { + IdentityLink identityLink = session.getIdentityLink(); + String issuer = identityLink.getName(); + String gebDat = identityLink.getDateOfBirth(); + String identificationValue = identityLink.getIdentificationValue(); + String identificationType = identityLink.getIdentificationType(); + String issueInstant = DateTimeUtils.buildDateTime(Calendar.getInstance()); + session.setIssueInstant(issueInstant); + String authURL = session.getAuthURL(); + String target = session.getTarget(); + String oaURL = session.getPublicOAURLPrefix(); + List extendedSAMLAttributes = session.getExtendedSAMLAttributesAUTH(); + String authBlock = new AuthenticationBlockAssertionBuilder().buildAuthBlock( + issuer, + issueInstant, + authURL, + target, + identificationValue, + identificationType, + oaURL, + gebDat, + extendedSAMLAttributes, + session); + + return authBlock; + } + + /** + * Verifies the infoboxes (except of the identity link infobox) returned by the BKU by + * calling appropriate validator classes. + * + * @param session The actual authentication session. + * @param infoboxReadResponseParams The parameters returned from the BKU as response + * to an infobox read request (including the infobox + * tokens to be verified). + * @param hideStammzahl Indicates whether source pins (<code>Stammzahl</code>en) + * should be hidden in any SAML attribute that may be + * returned by a validator. + * + * @throws AuthenticationException If the verification of at least one infobox fails. + * @throws ConfigurationException If the OAuthParameter cannot be extracted. + */ + private void verifyInfoboxes( + AuthenticationSession session, Map infoboxReadResponseParams, boolean hideStammzahl) + throws ValidateException, ConfigurationException + { + + AuthConfigurationProvider authConfigurationProvider = AuthConfigurationProvider.getInstance(); + // get the default VerifyInfobox parameters + Map defaultInfoboxParameters = null; + VerifyInfoboxParameters defaultVerifyInfoboxParameters = + authConfigurationProvider.getDefaultVerifyInfoboxParameters(); + if (defaultVerifyInfoboxParameters != null) { + defaultInfoboxParameters = defaultVerifyInfoboxParameters.getInfoboxParameters(); + } + // get the OA specific VerifyInfobox parameters + Map infoboxParameters = null; + OAAuthParameter oaParam = + authConfigurationProvider.getOnlineApplicationParameter(session.getPublicOAURLPrefix()); + VerifyInfoboxParameters verifyInfoboxParameters = oaParam.getVerifyInfoboxParameters(); + if (verifyInfoboxParameters != null) { + Vector authAttributes = new Vector(); + Vector oaAttributes = new Vector(); + infoboxParameters = verifyInfoboxParameters.getInfoboxParameters(); + // get the list of infobox identifiers + List identifiers = verifyInfoboxParameters.getIdentifiers(); + if (identifiers != null) { + // step through the identifiers and verify the infoboxes + Iterator it = identifiers.iterator(); + while (it.hasNext()) { + String identifier = (String)it.next(); + // get the infobox read response from the map of parameters + String infoboxReadResponse = (String)infoboxReadResponseParams.get(identifier); + // get the configuration parameters + VerifyInfoboxParameter verifyInfoboxParameter = null; + Object object = infoboxParameters.get(identifier); + // if not present, use default + if ((object == null) && (defaultInfoboxParameters != null)) { + object = defaultInfoboxParameters.get(identifier); + } + if (object != null) { + verifyInfoboxParameter = (VerifyInfoboxParameter)object; + } + if (infoboxReadResponse != null) { + if (verifyInfoboxParameter == null) { + // should not happen because of the pushinfobox mechanism; check it anyway + Logger.error("No validator for verifying \"" + identifier + "\"-infobox configured."); + throw new ValidateException("validator.41", new Object[] {identifier}); + } else { + String friendlyName = verifyInfoboxParameter.getFriendlyName(); + // get the class for validating the infobox + InfoboxValidator infoboxValidator = null; + try { + Class validatorClass = Class.forName(verifyInfoboxParameter.getValidatorClassName()); + infoboxValidator = (InfoboxValidator) validatorClass.newInstance(); + } catch (Exception e) { + Logger.error("Could not load validator class \"" + verifyInfoboxParameter.getValidatorClassName() + + "\" for \"" + identifier + "\"-infobox: " + e.getMessage()); + throw new ValidateException("validator.42", new Object[] {friendlyName}); + } + Logger.debug("Successfully loaded validator class \"" + verifyInfoboxParameter.getValidatorClassName() + + "\" for \"" + identifier + "\"-infobox."); + // parse the infobox read reponse + List infoboxTokenList = null; + try { + infoboxTokenList = + ExtendedInfoboxReadResponseParser.parseInfoboxReadResponse(infoboxReadResponse, friendlyName); + } catch (ParseException e) { + Logger.error("InfoboxReadResponse for \"" + identifier + + "\"-infobox could not be parsed successfully: " + e.getMessage()); + throw new ValidateException("validator.43", new Object[] {friendlyName}); + } + // build the parameters for validating the infobox + InfoboxValidatorParams infoboxValidatorParams = + InfoboxValidatorParamsBuilder.buildInfoboxValidatorParams( + session, verifyInfoboxParameter, infoboxTokenList, hideStammzahl); + // now validate the infobox + InfoboxValidationResult infoboxValidationResult = null; + try { + infoboxValidationResult = infoboxValidator.validate(infoboxValidatorParams); + } catch (ValidateException e) { + Logger.error("Error validating " + identifier + " infobox:" + e.getMessage()); + throw new ValidateException( + "validator.44", new Object[] {friendlyName}); + } + if (!infoboxValidationResult.isValid()) { + Logger.info("Validation of " + identifier + " infobox failed."); + throw new ValidateException( + "validator.40", new Object[] {friendlyName, infoboxValidationResult.getErrorMessage()}); + } + + Logger.info(identifier + " infobox successfully validated."); + + // get the SAML attributes to be appended to the AUTHBlock or to the final + // SAML Assertion + ExtendedSAMLAttribute[] extendedSAMLAttributes = infoboxValidationResult.getExtendedSamlAttributes(); + if (extendedSAMLAttributes != null) { + int length = extendedSAMLAttributes.length; + for (int i=0; i<length; i++) { + ExtendedSAMLAttribute samlAttribute = extendedSAMLAttributes[i]; + String name = samlAttribute.getName(); + if (name == null) { + Logger.info("The name of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is null."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName, "Name", String.valueOf((i+1)), "null"}); + } + if (name == "") { + Logger.info("The name of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is empty."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName, "Name", String.valueOf((i+1)), "leer"}); + } + if (samlAttribute.getNameSpace() == null) { + Logger.info("The namespace of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is null."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName, "Namespace", String.valueOf((i+1)), "null"}); + } + Object value = samlAttribute.getValue(); + if (value == null) { + Logger.info("The value of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is null."); + throw new ValidateException( + "validator.45", new Object[] {friendlyName ,"Wert", String.valueOf((i+1)), "null"}); + } + if ((value instanceof String) || (value instanceof Element)) { + + switch (samlAttribute.getAddToAUTHBlock()) { + case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK: + authAttributes.add(samlAttribute); + oaAttributes.add(samlAttribute); + break; + case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY: + authAttributes.add(samlAttribute); + break; + case ExtendedSAMLAttribute.NOT_ADD_TO_AUTHBLOCK: + oaAttributes.add(samlAttribute); + break; + default: + Logger.info("Invalid return value from method \"getAddToAUTHBlock()\" (" + + samlAttribute.getAddToAUTHBlock() + ") in SAML attribute number " + + (i+1) + " for infobox " + identifier); + throw new ValidateException( + "validator.47", new Object[] {friendlyName, String.valueOf((i+1))}); + } + } else { + Logger.info("The type of SAML-Attribute number " + (i+1) + " returned from " + + identifier + "-infobox validator is not valid. Must be either \"java.Lang.String\"" + + " or \"org.w3c.dom.Element\""); + throw new ValidateException( + "validator.46", new Object[] {identifier, String.valueOf((i+1))}); + + } + } + + } + } + } else { + if ((verifyInfoboxParameter !=null) && (verifyInfoboxParameter.isRequired())) { + Logger.info("Infobox \"" + identifier + "\" is required, but not returned from the BKU"); + throw new ValidateException( + "validator.48", new Object[] {verifyInfoboxParameter.getFriendlyName()}); + + } + Logger.debug("Infobox \"" + identifier + "\" not returned from BKU."); + } + } + session.setExtendedSAMLAttributesAUTH(authAttributes); + session.setExtendedSAMLAttributesOA(oaAttributes); + } + } + } + + /** + * Processes a <code><CreateXMLSignatureResponse></code> sent by the + * security layer implementation.<br> + * <ul> + * <li>Validates given <code><CreateXMLSignatureResponse></code></li> + * <li>Parses <code><CreateXMLSignatureResponse></code> for error codes</li> + * <li>Parses authentication block enclosed in + * <code><CreateXMLSignatureResponse></code></li> + * <li>Verifies authentication block by calling the MOA SP component</li> + * <li>Creates authentication data</li> + * <li>Creates a corresponding SAML artifact</li> + * <li>Stores authentication data in the authentication data store + * indexed by the SAML artifact</li> + * <li>Deletes authentication session</li> + * <li>Returns the SAML artifact, encoded BASE64</li> + * </ul> + * + * @param sessionID session ID of the running authentication session + * @param xmlCreateXMLSignatureReadResponse String representation of the + * <code><CreateXMLSignatureResponse></code> + * @return SAML artifact needed for retrieving authentication data, encoded BASE64 + */ + public String verifyAuthenticationBlock( + String sessionID, + String xmlCreateXMLSignatureReadResponse) + throws + AuthenticationException, + BuildException, + ParseException, + ConfigurationException, + ServiceException, + ValidateException { + + if (isEmpty(sessionID)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_SESSIONID}); + if (isEmpty(xmlCreateXMLSignatureReadResponse)) + throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_XMLRESPONSE}); + AuthenticationSession session = getSession(sessionID); + AuthConfigurationProvider authConf = AuthConfigurationProvider.getInstance(); + // parses <CreateXMLSignatureResponse> + CreateXMLSignatureResponse csresp = + new CreateXMLSignatureResponseParser(xmlCreateXMLSignatureReadResponse).parseResponse(); + try { + String serializedAssertion = DOMUtils.serializeNode(csresp.getSamlAssertion()); + session.setAuthBlock(serializedAssertion); + } catch (TransformerException e) { + throw new ParseException("parser.04", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_XMLRESPONSE}); + } catch (IOException e) { + throw new ParseException("parser.04", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_XMLRESPONSE}); + } + // validates <CreateXMLSignatureResponse> + new CreateXMLSignatureResponseValidator().validate(csresp, session); + // builds a <VerifyXMLSignatureRequest> for a MOA-SPSS call + String[] vtids = authConf.getMoaSpAuthBlockVerifyTransformsInfoIDs(); + String tpid = authConf.getMoaSpAuthBlockTrustProfileID(); + Element domVsreq = new VerifyXMLSignatureRequestBuilder().build(csresp, vtids, tpid); + // debug output + + // invokes the call + Element domVsresp = new SignatureVerificationInvoker().verifyXMLSignature(domVsreq); + // debug output + + // parses the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponse vsresp = new VerifyXMLSignatureResponseParser(domVsresp).parseData(); + // validates the <VerifyXMLSignatureResponse> + VerifyXMLSignatureResponseValidator.getInstance().validate( + vsresp, + null, + VerifyXMLSignatureResponseValidator.CHECK_AUTH_BLOCK, + false); + + + // TODO See Bug #144 + // Compare AuthBlock Data with information stored in session, especially date and time + + + // compares the public keys from the identityLink with the AuthBlock + VerifyXMLSignatureResponseValidator.getInstance().validateCertificate( + vsresp, + session.getIdentityLink()); + + // builds authentication data and stores it together with a SAML artifact + AuthenticationData authData = buildAuthenticationData(session, vsresp); + String samlArtifact = + new SAMLArtifactBuilder().build(session.getAuthURL(), session.getSessionID()); + storeAuthenticationData(samlArtifact, authData); + // invalidates the authentication session + sessionStore.remove(sessionID); + Logger.info( + "Anmeldedaten zu MOASession " + sessionID + " angelegt, SAML Artifakt " + samlArtifact); + return samlArtifact; + } + /** + * Builds the AuthenticationData object together with the + * corresponding <code><saml:Assertion></code> + * @param session authentication session + * @param verifyXMLSigResp VerifyXMLSignatureResponse from MOA-SP + * @return AuthenticationData object + * @throws ConfigurationException while accessing configuration data + * @throws BuildException while building the <code><saml:Assertion></code> + */ + private AuthenticationData buildAuthenticationData( + AuthenticationSession session, + VerifyXMLSignatureResponse verifyXMLSigResp) + throws ConfigurationException, BuildException { + + IdentityLink identityLink = session.getIdentityLink(); + AuthenticationData authData = new AuthenticationData(); + OAAuthParameter oaParam = + AuthConfigurationProvider.getInstance().getOnlineApplicationParameter( + session.getPublicOAURLPrefix()); + boolean businessService = oaParam.getBusinessService(); + authData.setMajorVersion(1); + authData.setMinorVersion(0); + authData.setAssertionID(Random.nextRandom()); + authData.setIssuer(session.getAuthURL()); + authData.setIssueInstant(DateTimeUtils.buildDateTime(Calendar.getInstance())); + + authData.setIdentificationType(identityLink.getIdentificationType()); + authData.setGivenName(identityLink.getGivenName()); + authData.setFamilyName(identityLink.getFamilyName()); + authData.setDateOfBirth(identityLink.getDateOfBirth()); + authData.setQualifiedCertificate(verifyXMLSigResp.isQualifiedCertificate()); + authData.setPublicAuthority(verifyXMLSigResp.isPublicAuthority()); + authData.setPublicAuthorityCode(verifyXMLSigResp.getPublicAuthorityCode()); + authData.setBkuURL(session.getBkuURL()); + boolean provideStammzahl = oaParam.getProvideStammzahl(); + if (provideStammzahl) { + authData.setIdentificationValue(identityLink.getIdentificationValue()); + } + String prPerson = new PersonDataBuilder().build(identityLink, provideStammzahl); + try { + String signerCertificateBase64 = ""; + if (oaParam.getProvideCertifcate()) { + X509Certificate signerCertificate = verifyXMLSigResp.getX509certificate(); + if (signerCertificate != null) { + signerCertificateBase64 = Base64Utils.encode(signerCertificate.getEncoded()); + } else { + Logger.info("\"provideCertificate\" is \"true\", but no signer certificate available"); + } + } + authData.setSignerCertificate(signerCertificateBase64); + if (businessService) { + authData.setWBPK(identityLink.getIdentificationValue()); + } else { + // only compute bPK if online applcation is a public service + String bpkBase64 = + new BPKBuilder().buildBPK( + identityLink.getIdentificationValue(), + session.getTarget()); + authData.setBPK(bpkBase64); + } + String ilAssertion = + oaParam.getProvideIdentityLink() + ? identityLink.getSerializedSamlAssertion() + : ""; + if (!oaParam.getProvideStammzahl()) { + ilAssertion = StringUtils.replaceAll(ilAssertion, identityLink.getIdentificationValue(), ""); + } + String authBlock = oaParam.getProvideAuthBlock() ? session.getAuthBlock() : ""; + String samlAssertion = + new AuthenticationDataAssertionBuilder().build( + authData, + prPerson, + authBlock, + ilAssertion, + session.getBkuURL(), + signerCertificateBase64, + businessService, + session.getExtendedSAMLAttributesOA()); + authData.setSamlAssertion(samlAssertion); + return authData; + } catch (Throwable ex) { + throw new BuildException( + "builder.00", + new Object[] { "AuthenticationData", ex.toString()}, + ex); + } + } + /** + * Retrieves <code>AuthenticationData</code> indexed by the SAML artifact. + * The <code>AuthenticationData</code> is deleted from the store upon end of this call. + * + * @return <code>AuthenticationData</code> + */ + public AuthenticationData getAuthenticationData(String samlArtifact) + throws AuthenticationException { + String assertionHandle; + try { + assertionHandle = new SAMLArtifactParser(samlArtifact).parseAssertionHandle(); + } catch (ParseException ex) { + throw new AuthenticationException("1205", new Object[] { samlArtifact, ex.toString()}); + } + AuthenticationData authData = null; + synchronized (authenticationDataStore) { + authData = (AuthenticationData) authenticationDataStore.get(assertionHandle); + if (authData == null) { + Logger.error("Assertion not found for SAML Artifact: " + samlArtifact); + throw new AuthenticationException("1206", new Object[] { samlArtifact }); + } + authenticationDataStore.remove(assertionHandle); + } + long now = new Date().getTime(); + if (now - authData.getTimestamp().getTime() > authDataTimeOut) + throw new AuthenticationException("1207", new Object[] { samlArtifact }); + Logger.debug("Assertion delivered for SAML Artifact: " + samlArtifact); + return authData; + } + /** + * Stores authentication data indexed by the assertion handle contained in the + * given saml artifact. + * @param samlArtifact SAML artifact + * @param authData authentication data + * @throws AuthenticationException when SAML artifact is invalid + */ + private void storeAuthenticationData(String samlArtifact, AuthenticationData authData) + throws AuthenticationException { + + try { + SAMLArtifactParser parser = new SAMLArtifactParser(samlArtifact); + // check type code 0x0001 + byte[] typeCode = parser.parseTypeCode(); + if (typeCode[0] != 0 || typeCode[1] != 1) + throw new AuthenticationException("auth.06", new Object[] { samlArtifact }); + String assertionHandle = parser.parseAssertionHandle(); + synchronized (authenticationDataStore) { + Logger.debug("Assertion stored for SAML Artifact: " + samlArtifact); + authenticationDataStore.put(assertionHandle, authData); + } + } catch (AuthenticationException ex) { + throw ex; + } catch (Throwable ex) { + throw new AuthenticationException("auth.06", new Object[] { samlArtifact }); + } + } + /** + * Creates a new session and puts it into the session store. + * + * @param id Session ID + * @return AuthenticationSession created + * @exception AuthenticationException + * thrown when an <code>AuthenticationSession</code> is running + * already for the given session ID + */ + private static AuthenticationSession newSession() throws AuthenticationException { + String sessionID = Random.nextRandom(); + AuthenticationSession newSession = new AuthenticationSession(sessionID); + synchronized (sessionStore) { + AuthenticationSession session = (AuthenticationSession) sessionStore.get(sessionID); + if (session != null) + throw new AuthenticationException("auth.01", new Object[] { sessionID }); + sessionStore.put(sessionID, newSession); + } + return newSession; + } + /** + * Retrieves a session from the session store. + * + * @param id session ID + * @return <code>AuthenticationSession</code> stored with given session ID, + * <code>null</code> if session ID unknown + */ + public static AuthenticationSession getSession(String id) throws AuthenticationException { + AuthenticationSession session = (AuthenticationSession) sessionStore.get(id); + if (session == null) + throw new AuthenticationException("auth.02", new Object[] { id }); + return session; + } + /** + * Cleans up expired session and authentication data stores. + */ + public void cleanup() { + long now = new Date().getTime(); + synchronized (sessionStore) { + Set keys = new HashSet(sessionStore.keySet()); + for (Iterator iter = keys.iterator(); iter.hasNext();) { + String sessionID = (String) iter.next(); + AuthenticationSession session = (AuthenticationSession) sessionStore.get(sessionID); + if (now - session.getTimestampStart().getTime() > sessionTimeOut) { + Logger.info( + MOAIDMessageProvider.getInstance().getMessage( + "cleaner.02", + new Object[] { sessionID })); + sessionStore.remove(sessionID); + } + } + } + synchronized (authenticationDataStore) { + Set keys = new HashSet(authenticationDataStore.keySet()); + for (Iterator iter = keys.iterator(); iter.hasNext();) { + String samlArtifact = (String) iter.next(); + AuthenticationData authData = + (AuthenticationData) authenticationDataStore.get(samlArtifact); + if (now - authData.getTimestamp().getTime() > authDataTimeOut) { + Logger.info( + MOAIDMessageProvider.getInstance().getMessage( + "cleaner.03", + new Object[] { samlArtifact })); + authenticationDataStore.remove(samlArtifact); + } + } + } + } + + /** + * Sets the sessionTimeOut. + * @param seconds Time out of the session in seconds + */ + public void setSecondsSessionTimeOut(long seconds) { + sessionTimeOut = 1000 * seconds; + } + /** + * Sets the authDataTimeOut. + * @param seconds Time out for signing AuthData in seconds + */ + public void setSecondsAuthDataTimeOut(long seconds) { + authDataTimeOut = 1000 * seconds; + } + + /** + * Checks a parameter. + * @param param parameter + * @return true if the parameter is null or empty + */ + private boolean isEmpty(String param) { + return param == null || param.length() == 0; + } + + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationSessionCleaner.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/AuthenticationSessionCleaner.java new file mode 100644 index 000000000..7e5ed6ec7 --- /dev/null +++ b/id/server/idserverlib/src/main/java/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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/MOAIDAuthConstants.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/MOAIDAuthConstants.java new file mode 100644 index 000000000..43e88e7b5 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/MOAIDAuthConstants.java @@ -0,0 +1,75 @@ +package at.gv.egovernment.moa.id.auth; + +import iaik.asn1.ObjectID; + + +/** + * 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"; + /** default BKU URL */ + public static final String DEFAULT_BKU = "http://localhost:3495/http-security-layer-request"; + /** 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"; + /** + * the identity link signer X509Subject names of those identity link signer certificates + * not including the identity link signer OID. The authorisation for signing the identity + * link must be checked by using their issuer names. After february 19th 2007 the OID of + * the certificate will be used fo checking the authorisation for signing identity links. + */ + public static final String[] IDENTITY_LINK_SIGNERS_WITHOUT_OID = + new String[] {"T=Dr.,CN=Nikolaus Schwab,O=BM f. Inneres i.A. des gf. Mitgieds der Datenschutzkommission", + "T=Dr.,CN=Nikolaus Schwab,O=BM f. Inneres i.A. des gf. Mitglieds der Datenschutzkommission"}; + /** + * the number of the certifcate extension "Eigenschaft zur Ausstellung von Personenbindungen" + */ + public static final String IDENTITY_LINK_SIGNER_OID_NUMBER = "1.2.40.0.10.1.7.1"; + /** + * the OID of the identity link signer certificate (Eigenschaft zur Ausstellung von Personenbindungen); + * used for checking the authorisation for signing the identity link for identity links signed after february 19th 2007 + */ + public static final ObjectID IDENTITY_LINK_SIGNER_OID = new ObjectID(IDENTITY_LINK_SIGNER_OID_NUMBER); + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/MOAIDAuthInitializer.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/MOAIDAuthInitializer.java new file mode 100644 index 000000000..7964e2fb6 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/MOAIDAuthInitializer.java @@ -0,0 +1,163 @@ +package at.gv.egovernment.moa.id.auth; + +import iaik.pki.PKIException; +import iaik.pki.jsse.IAIKX509TrustManager; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Properties; + +import javax.activation.CommandMap; +import javax.activation.MailcapCommandMap; +import javax.mail.Session; +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; +import at.gv.egovernment.moa.util.Constants; + +/** + * 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"); + Logger.info("Default java file.encoding: " + + System.getProperty("file.encoding")); + + //JDK bug workaround according to: + // http://jce.iaik.tugraz.at/products/03_cms/faq/index.php#JarVerifier + // register content data handlers for S/MIME types + MailcapCommandMap mc = new MailcapCommandMap(); + CommandMap.setDefaultCommandMap(mc); + + // create some properties and get the default Session + Properties props = new Properties(); + props.put("mail.smtp.host", "localhost"); + Session session = Session.getDefaultInstance(props, null); + + // 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(); + + // Initializes Namespace Map + Constants.nSMap.put(Constants.SAML_PREFIX, Constants.SAML_NS_URI); + Constants.nSMap.put(Constants.ECDSA_PREFIX, + "http://www.w3.org/2001/04/xmldsig-more#"); + Constants.nSMap.put(Constants.DSIG_PREFIX, Constants.DSIG_NS_URI); + + // 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); + } + + } + +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/WrongParametersException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/WrongParametersException.java new file mode 100644 index 000000000..3ce2798ea --- /dev/null +++ b/id/server/idserverlib/src/main/java/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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationAssertionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationAssertionBuilder.java new file mode 100644 index 000000000..241cf0afc --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationAssertionBuilder.java @@ -0,0 +1,88 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Iterator; +import java.util.List; + +import javax.xml.transform.TransformerException; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * Base class for building authentication the AUTHBlock and final OA data SAML assertions. + * Encapsulates methods used by the two specific builders + * {@link at.gv.egovernment.moa.id.auth.builder.AuthenticationBlockAssertionBuilder AuthenticationBlockAssertionBuilder} + * and + * {@link at.gv.egovernment.moa.id.auth.builder.AuthenticationDataAssertionBuilder AuthenticationDataAssertionBuilder} + * + * @author Harald Bratko + */ +public class AuthenticationAssertionBuilder { + + /** the NewLine representation in Java*/ + protected static String NL = "\n"; + + protected static String SAML_ATTRIBUTE = + " <saml:Attribute AttributeName=''{0}'' AttributeNamespace=''{1}''>" + NL + + " <saml:AttributeValue>{2}</saml:AttributeValue>" + NL + + " </saml:Attribute>"+ NL; + + /** + * Empty constructor + */ + public AuthenticationAssertionBuilder() { + } + + /** + * Builds the SAML attributes to be appended to the AUTHBlock or to the SAML assertion + * delivered to the online application. + * The method traverses through the list of given SAML attribute objects and builds an + * XML structure (String representation) for each of the attributes. + * + * @param extendedSAMLAttributes The SAML attributes to be appended to the AUTHBlock or + * to the SAML assertion delivered to the online application. + * @return A string representation including the XML structures of + * the SAML attributes. + * + * @throws ParseException If an error occurs on serializing an SAML attribute. + */ + protected String buildExtendedSAMLAttributes(List extendedSAMLAttributes) throws ParseException + { + StringBuffer sb = new StringBuffer(); + if (extendedSAMLAttributes!=null) { + Iterator it = extendedSAMLAttributes.iterator(); + while (it.hasNext()) { + ExtendedSAMLAttribute extendedSAMLAttribute = (ExtendedSAMLAttribute)it.next(); + Object value = extendedSAMLAttribute.getValue(); + String name = extendedSAMLAttribute.getName(); + String namespace = extendedSAMLAttribute.getNameSpace(); + if (value instanceof String) { + sb.append(MessageFormat.format( SAML_ATTRIBUTE, new Object[] {name, namespace, value})); + } else if (value instanceof Element) { + try { + String serializedValue = DOMUtils.serializeNode((Element)(value)); + serializedValue = StringUtils.removeXMLDeclaration(serializedValue); + sb.append(MessageFormat.format( SAML_ATTRIBUTE, new Object[] {name, namespace, serializedValue})); + } catch (TransformerException e) { + Logger.error("Error on serializing SAML attribute \"" + name + + " (namespace: \"" + namespace + "\"."); + throw new ParseException("parser.05", new Object[] { name, namespace}); + } catch (IOException e) { + Logger.error("Error on serializing SAML attribute \"" + name + + " (namespace: \"" + namespace + "\"."); + throw new ParseException("parser.05", new Object[] { name, namespace}); + } + } + } + } + return sb.toString(); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationBlockAssertionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationBlockAssertionBuilder.java new file mode 100644 index 000000000..60cd11ed6 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationBlockAssertionBuilder.java @@ -0,0 +1,146 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; +import java.util.List; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.logging.Logger; +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 extends AuthenticationAssertionBuilder implements Constants { + + /** template for the Auth-Block */ + private static String AUTH_BLOCK = + "<saml:Assertion xmlns:saml=''" + SAML_NS_URI + "''{0} MajorVersion=''1'' MinorVersion=''0'' AssertionID=''any'' Issuer=''{1}'' IssueInstant=''{2}''>" + NL + + " <saml:AttributeStatement>" + NL + + " <saml:Subject>" + NL + + " <saml:NameIdentifier>{3}</saml:NameIdentifier>" + NL + + " </saml:Subject>" + NL + + "{4}" + + " <saml:Attribute AttributeName=''OA'' AttributeNamespace=''" + MOA_NS_URI + "''>" + NL + + " <saml:AttributeValue>{5}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL + + " <saml:Attribute AttributeName=''Geburtsdatum'' AttributeNamespace=''" + MOA_NS_URI + "''>" + NL + + " <saml:AttributeValue>{6}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL + + "{7}" + + " </saml:AttributeStatement>" + NL + + "</saml:Assertion>"; + + private static String GESCHAEFTS_BEREICH_ATTRIBUTE = + " <saml:Attribute AttributeName=''Geschaeftsbereich'' AttributeNamespace=''" + MOA_NS_URI + "''>" + NL + + " <saml:AttributeValue>{0}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL; + + private static String WBPK_ATTRIBUTE = + " <saml:Attribute AttributeName=''wbPK'' AttributeNamespace=''" + MOA_NS_URI + "''>" + NL + + " <saml:AttributeValue>" + NL + + " <pr:Identification>" + NL + + " <pr:Value>{0}</pr:Value>" + NL + + " <pr:Type>{1}</pr:Type>" + NL + + " </pr:Identification>" + NL + + " </saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL; + + /** + * The number of SAML attributes included in this AUTH-Block (without the extended SAML attributes). + */ + public static final int NUM_OF_SAML_ATTRIBUTES = 3; + + /** + * 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"; maybe <code>null</code> if the application + * is a business application + * @param identityLinkValue the content of the <code><pr:Value></code> + * child element of the <code><pr:Identification></code> + * element derived from the Identitylink; this is the + * value of the <code>wbPK</code>; + * maybe <code>null</code> if the application is a public service + * @param identityLinkType the content of the <code><pr:Type></code> + * child element of the <code><pr:Identification></code> + * element derived from the Identitylink; this includes the + * URN prefix and the identification number of the business + * application used as input for wbPK computation; + * maybe <code>null</code> if the application is a public service + * @param oaURL public URL of online application requested + * @param gebDat The date of birth from the identity link. + * @param extendedSAMLAttributes The SAML attributes to be appended to the AUTHBlock. + * + * @return String representation of authentication block + * <code><saml:Assertion></code> built + * + * @throws BuildException If an error occurs on serializing an extended SAML attribute + * to be appended to the AUTH-Block. + */ + public String buildAuthBlock( + String issuer, + String issueInstant, + String authURL, + String target, + String identityLinkValue, + String identityLinkType, + String oaURL, + String gebDat, + List extendedSAMLAttributes, + AuthenticationSession session) + throws BuildException + { + session.setSAMLAttributeGebeORwbpk(true); + String gebeORwbpk = ""; + String wbpkNSDeclaration = ""; + if (target == null) { + // OA is a business application + if (!Constants.URN_PREFIX_HPI.equals(identityLinkType)) { + // Only add wbPKs to AUTH-Block. HPIs can be added to the AUTH-Block by the corresponding Validator + gebeORwbpk = MessageFormat.format(WBPK_ATTRIBUTE, new Object[] { identityLinkValue, identityLinkType }); + wbpkNSDeclaration = " xmlns:pr=\"" + PD_NS_URI + "\""; + } else { + // We do not have a wbPK, therefore no SAML-Attribute is provided + session.setSAMLAttributeGebeORwbpk(false); + } + } else { + gebeORwbpk = MessageFormat.format(GESCHAEFTS_BEREICH_ATTRIBUTE, new Object[] { target }); + } + + String assertion; + try { + assertion = MessageFormat.format( + AUTH_BLOCK, new Object[] { + wbpkNSDeclaration, + issuer, + issueInstant, + authURL, + gebeORwbpk, + oaURL, + gebDat, + buildExtendedSAMLAttributes(extendedSAMLAttributes)}); + } catch (ParseException e) { + Logger.error("Error on building AUTH-Block: " + e.getMessage()); + throw new BuildException("builder.00", new Object[] { "AUTH-Block", e.toString()}); + } + + return assertion; + + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataAssertionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataAssertionBuilder.java new file mode 100644 index 000000000..53520c846 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataAssertionBuilder.java @@ -0,0 +1,156 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; +import java.util.List; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * 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 extends AuthenticationAssertionBuilder 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 + "''" + + " xmlns:si=''" + XSI_NS_URI + "''" + + " MajorVersion=''1'' MinorVersion=''0'' AssertionID=''{0}'' Issuer=''{1}'' IssueInstant=''{2}''>" + NL + + " <saml:AttributeStatement>" + NL + + " <saml:Subject>" + NL + + " <saml:NameIdentifier NameQualifier=''{3}''>{4}</saml:NameIdentifier>" + NL + + " <saml:SubjectConfirmation>" + NL + + " <saml:ConfirmationMethod>" + MOA_NS_URI + "cm</saml:ConfirmationMethod>" + NL + + " <saml:SubjectConfirmationData>{5}{6}</saml:SubjectConfirmationData>" + NL + + " </saml:SubjectConfirmation>" + NL + + " </saml:Subject>" + NL + + " <saml:Attribute AttributeName=''PersonData'' AttributeNamespace=''" + PD_NS_URI + "''>" + NL + + " <saml:AttributeValue>{7}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL + + " <saml:Attribute AttributeName=''isQualifiedCertificate'' AttributeNamespace=''" + MOA_NS_URI + "''>" + NL + + " <saml:AttributeValue>{8}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL + + " <saml:Attribute AttributeName=''bkuURL'' AttributeNamespace=''" + MOA_NS_URI + "''>" + NL + + " <saml:AttributeValue>{9}</saml:AttributeValue>" + NL + + " </saml:Attribute>" + NL + + "{10}" + + "{11}" + + "{12}" + + " </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; + + private static final String SIGNER_CERTIFICATE_ATT = + " <saml:Attribute AttributeName=''SignerCertificate'' AttributeNamespace=''" + MOA_NS_URI + "''>" + 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>"Stammzahl"</code> or not; may be empty + * @param xmlIdentityLink the IdentityLink + * @param signerCertificateBase64 Base64 encoded certificate of the signer. Maybe + * an empty string if the signer certificate should not be provided. + * Will be ignored if the <code>businessService</code> parameter is + * set to <code>false</code>. + * @param businessService <code>true</code> if the online application is a + * business service, otherwise <code>false</code> + * @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, + String bkuURL, + String signerCertificateBase64, + boolean businessService, + List extendedSAMLAttributes) + 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 signerCertificateAttribute = ""; + if (signerCertificateBase64 != "") { + signerCertificateAttribute = MessageFormat.format( + SIGNER_CERTIFICATE_ATT, new Object[] { signerCertificateBase64 }); + } + + String pkType; + String pkValue; + if (businessService) { + pkType = authData.getIdentificationType(); + pkValue = authData.getWBPK(); + + } else { + pkType = URN_PREFIX_BPK; + pkValue = authData.getBPK(); + } + + String assertion; + try { + assertion = MessageFormat.format(AUTH_DATA, new Object[] { + authData.getAssertionID(), + authData.getIssuer(), + authData.getIssueInstant(), + pkType, + pkValue, + StringUtils.removeXMLDeclaration(xmlAuthBlock), + StringUtils.removeXMLDeclaration(xmlIdentityLink), + StringUtils.removeXMLDeclaration(xmlPersonData), + isQualifiedCertificate, + bkuURL, + publicAuthorityAttribute, + signerCertificateAttribute, + buildExtendedSAMLAttributes(extendedSAMLAttributes)}); + } catch (ParseException e) { + Logger.error("Error on building Authentication Data Assertion: " + e.getMessage()); + throw new BuildException("builder.00", new Object[] { "Authentication Data Assertion", e.toString()}); + } + return assertion; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/BPKBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/BPKBuilder.java new file mode 100644 index 000000000..6cc8c1be8 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/BPKBuilder.java @@ -0,0 +1,49 @@ +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; +import at.gv.egovernment.moa.util.Constants; + +/** + * Builder for the bPK, as defined in + * <code>"Ableitung f¨r die bereichsspezifische Personenkennzeichnung"</code> + * version <code>1.0.1</code> from <code>"reference.e-government.gv.at"</code>. + * + * @author Paul Schamberger + * @version $Id$ + */ +public class BPKBuilder { + + /** + * Builds the bPK from the given parameters. + * @param identificationValue Base64 encoded "Stammzahl" + * @param target "Bereich lt. Verordnung des BKA" + * @return bPK in a BASE64 encoding + * @throws BuildException if an error occurs on building the bPK + */ + public String buildBPK(String identificationValue, String target) + throws BuildException { + + if ((identificationValue == null || + identificationValue.length() == 0 || + target == null || + target.length() == 0)) + { + throw new BuildException("builder.00", + new Object[] {"BPK", "Unvollständige Parameterangaben: identificationValue=" + + identificationValue + ",target=" + target}); + } + String basisbegriff = identificationValue + "+" + Constants.URN_PREFIX_CDID + "+" + target; + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] hash = md.digest(basisbegriff.getBytes("ISO-8859-1")); + String hashBase64 = Base64Utils.encode(hash); + return hashBase64; + } catch (Exception ex) { + throw new BuildException("builder.00", new Object[] {"BPK", ex.toString()}, ex); + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/Builder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/Builder.java new file mode 100644 index 000000000..3a2ee07de --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/Builder.java @@ -0,0 +1,59 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * Base class for HTML/XML builders providing commonly useful functions. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class Builder { + + /** + * Replaces a given number of occurences of a special tag in an XML or HTML template by a value. + * @param template html template + * @param tag special tag + * @param value value replacing the tag + * @param expected specifies if the tag is expected to present; if <code>true</code> and the tag + * is not present, an exception is thrown; if <code>false</code> and the tag is + * not present, the original string is returned + * @param maxreplacements Set -1 to replace each occurence of tag, or limit replacements by a given positive number + * @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, + boolean expected, + int maxreplacements) + throws BuildException + { + String result = template; + int index = result.indexOf(tag); + if (index < 0) { + if (expected) { + // Substring not found but should + throw new BuildException( + "builder.01", + new Object[] {"<" + tag.substring(1, tag.length() - 1) + ">"}); + } + } else { + // replace each occurence + if (maxreplacements == -1) { + return StringUtils.replaceAll(template, tag, value); + } else { + int found = 1; + while (index > -1 && (found <= maxreplacements)) { + result = result.substring(0, index) + value + result.substring(index + tag.length()); + index = result.indexOf(tag); + if (index > -1) found += 1; + } + } + } + return result; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/CertInfoVerifyXMLSignatureRequestBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/CertInfoVerifyXMLSignatureRequestBuilder.java new file mode 100644 index 000000000..06c81f49e --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/CertInfoVerifyXMLSignatureRequestBuilder.java @@ -0,0 +1,84 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.io.IOException; +import java.text.MessageFormat; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.util.Constants; +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 implements Constants { + + /** special tag in the VerifyXMLRequest template to be substituted for a <code><dsig:Signature></code> */ + private static final String SIGNATURE_TAG = "<dsig:Signature/>"; + + /** private static String nl contains the NewLine representation in Java*/ + private static final String nl = "\n"; + + /** + * XML template for the CertInfoVerifyXMLSignatureRequest to be built + */ + static final String CERTINFO_REQUEST = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + nl + + "<{0}:VerifyXMLSignatureRequest {2} xmlns:dsig=\"" + DSIG_NS_URI + "\">" + nl + + " <{0}:SignatureInfo>" + nl + + " <{0}:SignatureEnvironment>" + nl + + " <{1}:XMLContent xml:space=\"preserve\"><dsig:Signature/></{1}:XMLContent>" + nl + + " </{0}:SignatureEnvironment>" + nl + + " <{0}:SignatureLocation>//dsig:Signature</{0}:SignatureLocation>" + nl + + " </{0}:SignatureInfo>" + nl + + "</{0}:VerifyXMLSignatureRequest>"; + + /** + * Constructor + */ + public CertInfoVerifyXMLSignatureRequestBuilder() { + super(); + } + /** + * Builds the <code><VerifyXMLSignatureRequest></code> structure. + * @return the XML structure + * @throws BuildException + */ + public String build(boolean slVersion12) throws BuildException { + + String sl10Prefix; + String sl11Prefix; + String slNsDeclaration; + + if (slVersion12) { + + sl10Prefix = SL12_PREFIX; + sl11Prefix = SL12_PREFIX; + slNsDeclaration = "xmlns:" + SL12_PREFIX + "=\"" + SL12_NS_URI + "\""; + + } else { + + sl10Prefix = SL10_PREFIX; + sl11Prefix = SL11_PREFIX; + slNsDeclaration = "xmlns:" + sl11Prefix + "=\"" + SL11_NS_URI + "\" xmlns:" + sl10Prefix + "=\"" + SL10_NS_URI + "\""; + + } + + String certInfoRequest = MessageFormat.format(CERTINFO_REQUEST, new Object[] {sl11Prefix, sl10Prefix, slNsDeclaration}); + String resDsigSignature = "resources/xmldata/CertInfoDsigSignature.xml"; + + + try { + String dsigSignature = FileUtils.readResource(resDsigSignature, "UTF-8"); + certInfoRequest = replaceTag(certInfoRequest, SIGNATURE_TAG, dsigSignature, true, 1); + return certInfoRequest; + } + catch (IOException ex) { + throw new BuildException("auth.04", new Object[] {resDsigSignature, ex.toString()}); + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/CreateXMLSignatureRequestBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/CreateXMLSignatureRequestBuilder.java new file mode 100644 index 000000000..e9a9f308d --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/CreateXMLSignatureRequestBuilder.java @@ -0,0 +1,93 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; + +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * 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 + + "<{3}:CreateXMLSignatureRequest xmlns:dsig=''" + DSIG_NS_URI + "'' {5}>" + nl + + " <{3}:KeyboxIdentifier>{1}</{3}:KeyboxIdentifier>" + nl + + " <{3}:DataObjectInfo Structure=''detached''>" + nl + + " <{4}:DataObject Reference=''''/>" + nl + + "{2}" + + " </{3}:DataObjectInfo>" + nl + + " <{3}:SignatureInfo>" + nl + + " <{3}:SignatureEnvironment>" + nl + + " <{4}:XMLContent>{0}</{4}:XMLContent>" + nl + + " </{3}:SignatureEnvironment>" + nl + + " <{3}:SignatureLocation Index=''2''>/saml:Assertion</{3}:SignatureLocation>" + nl + + " </{3}:SignatureInfo>" + nl + + "</{3}:CreateXMLSignatureRequest>"; + + + /** + * Constructor for CreateXMLSignatureRequestBuilder. + */ + public CreateXMLSignatureRequestBuilder() { + super(); + } + + /** + * Builds the <code><CreateXMLSignatureRequest></code>. + * + * @param authBlock String representation of XML authentication block + * @param keyBoxIdentifier the key box identifier which will be used (e.g. CertifiedKeypair) + * @param slVersion12 specifies whether the Security Layer version number is 1.2 or not + * @return String representation of <code><CreateXMLSignatureRequest></code> + */ + public String build(String authBlock, String keyBoxIdentifier, String[] dsigTransformInfos, boolean slVersion12) { + + String sl10Prefix; + String sl11Prefix; + String slNsDeclaration; + + String dsigTransformInfosString = ""; + for (int i = 0; i < dsigTransformInfos.length; i++) { + dsigTransformInfosString += dsigTransformInfos[i]; + } + + if (slVersion12) { + + // replace the SecurityLayer namespace prefixes and URIs within the transforms + dsigTransformInfosString = StringUtils.changeSLVersion(dsigTransformInfosString, + SL10_PREFIX, SL12_PREFIX, + SL10_NS_URI, SL12_NS_URI); + sl10Prefix = SL12_PREFIX; + sl11Prefix = SL12_PREFIX; + slNsDeclaration = "xmlns:" + SL12_PREFIX + "='" + SL12_NS_URI + "'"; + + } else { + + sl10Prefix = SL10_PREFIX; + sl11Prefix = SL11_PREFIX; + slNsDeclaration = "xmlns:" + sl10Prefix + "='" + SL10_NS_URI + "' xmlns:" + sl11Prefix + "='" + SL11_NS_URI + "'"; + + } + + String request = MessageFormat.format( + CREATE_XML_SIGNATURE_REQUEST, new Object[] { authBlock, + keyBoxIdentifier, + dsigTransformInfosString, + sl11Prefix, + sl10Prefix, + slNsDeclaration }); + + return request; + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/DataURLBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/DataURLBuilder.java new file mode 100644 index 000000000..30cc1df5a --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/DataURLBuilder.java @@ -0,0 +1,83 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import at.gv.egovernment.moa.id.auth.servlet.AuthServlet; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; + +/** + * 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 individualDataURLPrefix = null; + String dataURL; + try { + //check if an individual prefix is configured + individualDataURLPrefix = AuthConfigurationProvider.getInstance(). + getGenericConfigurationParameter(AuthConfigurationProvider.INDIVIDUAL_DATA_URL_PREFIX); + + if (null != individualDataURLPrefix) { + + //check individualDataURLPrefix + if(!individualDataURLPrefix.startsWith("http")) + throw(new ConfigurationException("config.13", new Object[] { individualDataURLPrefix})); + + //when ok then use it + dataURL = individualDataURLPrefix + authServletName; + } else + dataURL = authBaseURL + authServletName; + + } catch (ConfigurationException e) { + Logger.warn(e); + Logger.warn(MOAIDMessageProvider.getInstance().getMessage("config.12", new Object[] { authBaseURL } )); + 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/GetIdentityLinkFormBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/GetIdentityLinkFormBuilder.java new file mode 100644 index 000000000..0d0595b69 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/GetIdentityLinkFormBuilder.java @@ -0,0 +1,150 @@ +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>"; + /** special tag in the HTML template to be substituted for the infoboxes to be pushed from the BKU */ + private static final String PUSHINFOBOX_TAG = "<PushInfobox>"; + /** private static int all contains the representation to replace all tags*/ + private static final int ALL = -1; + + /** default HTML template */ + private static final String DEFAULT_HTML_TEMPLATE = + "<html>" + nl + + "<head>" + nl + + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">" + nl + + "<title>Anmeldung mit Bürgerkarte</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=\"hidden\" " + nl + + " name=\"PushInfobox\"" + nl + + " value=\"" + PUSHINFOBOX_TAG + "\"/>" + nl + + " <input type=\"submit\" value=\"Anmeldung mit Bürgerkarte\"/>" + 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 + + " <input type=\"hidden\" 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, + String pushInfobox) + throws BuildException + { + String htmlForm = htmlTemplate == null ? DEFAULT_HTML_TEMPLATE : htmlTemplate; +// String bku = bkuURL == null ? DEFAULT_BKU : bkuURL; + htmlForm = replaceTag(htmlForm, BKU_TAG, bkuURL, true, ALL); + htmlForm = replaceTag(htmlForm, XMLREQUEST_TAG, encodeParameter(xmlRequest), true, ALL); + htmlForm = replaceTag(htmlForm, DATAURL_TAG, dataURL, true, ALL); + htmlForm = replaceTag(htmlForm, PUSHINFOBOX_TAG, pushInfobox, false, ALL); +//new:wird oben mitreplaced htmlForm = replaceTag(htmlForm, BKU_TAG, bkuURL); + htmlForm = replaceTag(htmlForm, CERTINFO_XMLREQUEST_TAG, encodeParameter(certInfoXMLRequest), true, ALL); + htmlForm = replaceTag(htmlForm, CERTINFO_DATAURL_TAG, certInfoDataURL, true, ALL); + 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/InfoboxReadRequestBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/InfoboxReadRequestBuilder.java new file mode 100644 index 000000000..c2bafe43b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/InfoboxReadRequestBuilder.java @@ -0,0 +1,86 @@ +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 { + + + /** + * Constructor for InfoboxReadRequestBuilder. + */ + public InfoboxReadRequestBuilder() { + } + + + /** + * Builds an <code><InfoboxReadRequest></code>. + * + * @param slVersion12 specifies whether the Security Layer version is + * version 1.2 or not + * @param businessService specifies whether the online application is a + * business service or not + * @param identityLinkDomainIdentifier the identification number of the business + * company; maybe <code>null</code> if the OA + * is a public service; must not be <code>null</code> + * if the OA is a business service + * + * @return <code><InfoboxReadRequest></code> as String + */ + public String build(boolean slVersion12, boolean businessService, String identityLinkDomainIdentifier) { + + String slPrefix; + String slNsDeclaration; + + if (slVersion12) { + slPrefix = SL12_PREFIX; + slNsDeclaration = SL12_NS_URI; + } else { + slPrefix = SL10_PREFIX; + slNsDeclaration = SL10_NS_URI; + } + + StringBuffer sb = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); + sb.append("<"); + sb.append(slPrefix); + sb.append(":InfoboxReadRequest xmlns:"); + sb.append(slPrefix); + sb.append("=\""); + sb.append(slNsDeclaration); + sb.append("\">"); + sb.append("<"); + sb.append(slPrefix); + sb.append(":InfoboxIdentifier>IdentityLink</"); + sb.append(slPrefix); + sb.append(":InfoboxIdentifier>"); + sb.append("<"); + sb.append(slPrefix); + sb.append(":BinaryFileParameters ContentIsXMLEntity=\"true\"/>"); + if (businessService) { + sb.append("<"); + sb.append(slPrefix); + sb.append(":BoxSpecificParameters>"); + sb.append("<"); + sb.append(slPrefix); + sb.append(":IdentityLinkDomainIdentifier>"); + sb.append(identityLinkDomainIdentifier); + sb.append("</sl:IdentityLinkDomainIdentifier>"); + sb.append("</"); + sb.append(slPrefix); + sb.append(":BoxSpecificParameters>"); + } + sb.append("</"); + sb.append(slPrefix); + sb.append(":InfoboxReadRequest>"); + + return sb.toString(); + + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/InfoboxValidatorParamsBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/InfoboxValidatorParamsBuilder.java new file mode 100644 index 000000000..038e549be --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/InfoboxValidatorParamsBuilder.java @@ -0,0 +1,82 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.util.List; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams; +import at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParamsImpl; +import at.gv.egovernment.moa.id.auth.parser.IdentityLinkAssertionParser; +import at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameter; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * This class provides one method for building parameters needed for + * validating an infobox token. + * + * @author Harald Bratko + */ +public class InfoboxValidatorParamsBuilder { + + // hide the default constructor + private InfoboxValidatorParamsBuilder() { + } + + /** + * Builds the parameters passed to the validator class for validating an infobox token. + * + * @param session The actual Authentication session. + * @param verifyInfoboxParameter The configuration parameters for the infobox. + * @param infoboxTokenList Contains the infobox token to be validated. + * @param hideStammzahl Indicates whether source pins (<code>Stammzahl</code>en) + * should be hidden in any SAML attributes returned by + * an infobox validator. + * + * @return Parameters for validating an infobox token. + */ + public static InfoboxValidatorParams buildInfoboxValidatorParams( + AuthenticationSession session, + VerifyInfoboxParameter verifyInfoboxParameter, + List infoboxTokenList, + boolean hideStammzahl) + { + InfoboxValidatorParamsImpl infoboxValidatorParams = new InfoboxValidatorParamsImpl(); + IdentityLink identityLink = session.getIdentityLink(); + + // the infobox token to validate + infoboxValidatorParams.setInfoboxTokenList(infoboxTokenList); + // configuration parameters + infoboxValidatorParams.setTrustProfileID(verifyInfoboxParameter.getTrustProfileID()); + infoboxValidatorParams.setSchemaLocations(verifyInfoboxParameter.getSchemaLocations()); + infoboxValidatorParams.setApplicationSpecificParams(verifyInfoboxParameter.getApplicationSpecificParams()); + // authentication session parameters + infoboxValidatorParams.setBkuURL(session.getBkuURL()); + infoboxValidatorParams.setTarget(session.getTarget()); + infoboxValidatorParams.setBusinessApplication(session.getBusinessService()); + // parameters from the identity link + infoboxValidatorParams.setFamilyName(identityLink.getFamilyName()); + infoboxValidatorParams.setGivenName(identityLink.getGivenName()); + infoboxValidatorParams.setDateOfBirth(identityLink.getDateOfBirth()); + if (verifyInfoboxParameter.getProvideStammzahl()) { + infoboxValidatorParams.setIdentificationValue(identityLink.getIdentificationValue()); + } + infoboxValidatorParams.setIdentificationType(identityLink.getIdentificationType()); + infoboxValidatorParams.setPublicKeys(identityLink.getPublicKey()); + if (verifyInfoboxParameter.getProvideIdentityLink()) { + Element identityLinkElem = (Element)identityLink.getSamlAssertion().cloneNode(true); + if (!verifyInfoboxParameter.getProvideStammzahl()) { + Element identificationValueElem = + (Element)XPathUtils.selectSingleNode(identityLinkElem, IdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); + if (identificationValueElem != null) { + identificationValueElem.getFirstChild().setNodeValue(""); + } + } + infoboxValidatorParams.setIdentityLink(identityLinkElem); + } + infoboxValidatorParams.setHideStammzahl(hideStammzahl); + return infoboxValidatorParams; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/PersonDataBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/PersonDataBuilder.java new file mode 100644 index 000000000..819ed79bb --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/PersonDataBuilder.java @@ -0,0 +1,59 @@ +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>"Stammzahl"</code> in the + * <code><pr:Person></code> data. + * + * @param identityLink <code>IdentityLink</code> containing the + * attribute <code>prPerson</code> + * @param provideStammzahl true if <code>"Stammzahl"</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 provideStammzahl) + throws BuildException { + + try { + Element prPerson = (Element)identityLink.getPrPerson().cloneNode(true); + if (! provideStammzahl) { + Node prIdentification = XPathUtils.selectSingleNode(prPerson, "pr:Identification/pr:Value"); + //remove IdentificationValue + prIdentification.getFirstChild().setNodeValue(""); + } + 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/SAMLArtifactBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/SAMLArtifactBuilder.java new file mode 100644 index 000000000..27e19e830 --- /dev/null +++ b/id/server/idserverlib/src/main/java/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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/SAMLResponseBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/SAMLResponseBuilder.java new file mode 100644 index 000000000..64cb16181 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/SAMLResponseBuilder.java @@ -0,0 +1,91 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import java.text.MessageFormat; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * 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, + StringUtils.removeXMLDeclaration(samlAssertion) }); + Element domResponse = DOMUtils.parseDocument(xmlResponse, false, ALL_SCHEMA_LOCATIONS, null).getDocumentElement(); + return domResponse; + } + catch (Throwable ex) { + throw new BuildException( + "builder.00", + new Object[] { "samlp:Response", ex.toString() }, + ex); + } + } + + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/SelectBKUFormBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/SelectBKUFormBuilder.java new file mode 100644 index 000000000..312179e73 --- /dev/null +++ b/id/server/idserverlib/src/main/java/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 = + "<html>" + nl + + "<head>" + nl + + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">" + 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, true, 1); + htmlForm = replaceTag(htmlForm, SELECT_TAG, bkuSelectTag, true, 1); + return htmlForm; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/VerifyXMLSignatureRequestBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/VerifyXMLSignatureRequestBuilder.java new file mode 100644 index 000000000..758f28150 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/VerifyXMLSignatureRequestBuilder.java @@ -0,0 +1,206 @@ +package at.gv.egovernment.moa.id.auth.builder; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.auth.data.CreateXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.Constants; + +/** + * 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 { + + /** shortcut for XMLNS namespace URI */ + private static final String XMLNS_NS_URI = Constants.XMLNS_NS_URI; + /** shortcut for MOA namespace URI */ + private static final String MOA_NS_URI = Constants.MOA_NS_URI; + /** The DSIG-Prefix */ + private static final String DSIG = Constants.DSIG_PREFIX + ":"; + + /** The document containing the <code>VerifyXMLsignatureRequest</code> */ + private Document requestDoc_; + /** the <code>VerifyXMLsignatureRequest</code> root element */ + private Element requestElem_; + + + /** + * Builds the body for a <code>VerifyXMLsignatureRequest</code> including the root + * element and namespace declarations. + * + * @throws BuildException If an error occurs on building the document. + */ + public VerifyXMLSignatureRequestBuilder() throws BuildException { + try { + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + requestDoc_ = docBuilder.newDocument(); + requestElem_ = requestDoc_.createElementNS(MOA_NS_URI, "VerifyXMLSignatureRequest"); + requestElem_.setAttributeNS(XMLNS_NS_URI, "xmlns", MOA_NS_URI); + requestElem_.setAttributeNS(XMLNS_NS_URI, "xmlns:" + Constants.DSIG_PREFIX, Constants.DSIG_NS_URI); + requestDoc_.appendChild(requestElem_); + } catch (Throwable t) { + throw new BuildException( + "builder.00", + new Object[] {"VerifyXMLSignatureRequest", t.toString()}, + t); + } + } + + + /** + * Builds a <code><VerifyXMLSignatureRequest></code> + * from an IdentityLink with a known trustProfileID which + * has to exist in MOA-SP + * @param identityLink - The IdentityLink + * @param trustProfileID - a preconfigured TrustProfile at MOA-SP + * + * @return Element - The complete request as Dom-Element + * + * @throws ParseException + */ + public Element build(IdentityLink identityLink, String trustProfileID) + throws ParseException + { + try { + // build the request + Element dateTimeElem = requestDoc_.createElementNS(MOA_NS_URI, "DateTime"); + requestElem_.appendChild(dateTimeElem); + Node dateTime = requestDoc_.createTextNode(identityLink.getIssueInstant()); + dateTimeElem.appendChild(dateTime); + Element verifiySignatureInfoElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifySignatureInfo"); + requestElem_.appendChild(verifiySignatureInfoElem); + Element verifySignatureEnvironmentElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifySignatureEnvironment"); + verifiySignatureInfoElem.appendChild(verifySignatureEnvironmentElem); + Element base64ContentElem = requestDoc_.createElementNS(MOA_NS_URI, "Base64Content"); + verifySignatureEnvironmentElem.appendChild(base64ContentElem); + // insert the base64 encoded identity link SAML assertion + String serializedAssertion = identityLink.getSerializedSamlAssertion(); + String base64EncodedAssertion = Base64Utils.encode(serializedAssertion.getBytes("UTF-8")); + //replace all '\r' characters by no char. + StringBuffer replaced = new StringBuffer(); + for (int i = 0; i < base64EncodedAssertion.length(); i ++) { + char c = base64EncodedAssertion.charAt(i); + if (c != '\r') { + replaced.append(c); + } + } + base64EncodedAssertion = replaced.toString(); + Node base64Content = requestDoc_.createTextNode(base64EncodedAssertion); + base64ContentElem.appendChild(base64Content); + // specify the signature location + Element verifySignatureLocationElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifySignatureLocation"); + verifiySignatureInfoElem.appendChild(verifySignatureLocationElem); + Node signatureLocation = requestDoc_.createTextNode(DSIG + "Signature"); + verifySignatureLocationElem.appendChild(signatureLocation); + // signature manifest params + Element signatureManifestCheckParamsElem = + requestDoc_.createElementNS(MOA_NS_URI, "SignatureManifestCheckParams"); + requestElem_.appendChild(signatureManifestCheckParamsElem); + signatureManifestCheckParamsElem.setAttribute("ReturnReferenceInputData", "false"); + // add the transforms + Element referenceInfoElem = requestDoc_.createElementNS(MOA_NS_URI, "ReferenceInfo"); + signatureManifestCheckParamsElem.appendChild(referenceInfoElem); + Element[] dsigTransforms = identityLink.getDsigReferenceTransforms(); + for (int i = 0; i < dsigTransforms.length; i++) { + Element verifyTransformsInfoProfileElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifyTransformsInfoProfile"); + referenceInfoElem.appendChild(verifyTransformsInfoProfileElem); + verifyTransformsInfoProfileElem.appendChild(requestDoc_.importNode(dsigTransforms[i], true)); + } + Element returnHashInputDataElem = + requestDoc_.createElementNS(MOA_NS_URI, "ReturnHashInputData"); + requestElem_.appendChild(returnHashInputDataElem); + Element trustProfileIDElem = requestDoc_.createElementNS(MOA_NS_URI, "TrustProfileID"); + trustProfileIDElem.appendChild(requestDoc_.createTextNode(trustProfileID)); + requestElem_.appendChild(trustProfileIDElem); + } catch (Throwable t) { + throw new ParseException("builder.00", + new Object[] { "VerifyXMLSignatureRequest (IdentityLink)" }, t); + } + + return requestElem_; + } + + + /** + * Builds a <code><VerifyXMLSignatureRequest></code> + * from the signed AUTH-Block with a known trustProfileID which + * has to exist in MOA-SP + * @param csr - signed AUTH-Block + * @param verifyTransformsInfoProfileID - allowed verifyTransformsInfoProfileID + * @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 BuildException { //samlAssertionObject + + try { + // build the request +// requestElem_.setAttributeNS(Constants.XMLNS_NS_URI, "xmlns:" +// + Constants.XML_PREFIX, Constants.XMLNS_NS_URI); + Element verifiySignatureInfoElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifySignatureInfo"); + requestElem_.appendChild(verifiySignatureInfoElem); + Element verifySignatureEnvironmentElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifySignatureEnvironment"); + verifiySignatureInfoElem.appendChild(verifySignatureEnvironmentElem); + Element xmlContentElem = requestDoc_.createElementNS(MOA_NS_URI, "XMLContent"); + verifySignatureEnvironmentElem.appendChild(xmlContentElem); + xmlContentElem.setAttribute(Constants.XML_PREFIX + ":space", "preserve"); + // insert the SAML assertion + xmlContentElem.appendChild(requestDoc_.importNode(csr.getSamlAssertion(), true)); + // specify the signature location + Element verifySignatureLocationElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifySignatureLocation"); + verifiySignatureInfoElem.appendChild(verifySignatureLocationElem); + Node signatureLocation = requestDoc_.createTextNode(DSIG + "Signature"); + verifySignatureLocationElem.appendChild(signatureLocation); + // signature manifest params + Element signatureManifestCheckParamsElem = + requestDoc_.createElementNS(MOA_NS_URI, "SignatureManifestCheckParams"); + requestElem_.appendChild(signatureManifestCheckParamsElem); + signatureManifestCheckParamsElem.setAttribute("ReturnReferenceInputData", "true"); + // add the transform profile IDs + Element referenceInfoElem = requestDoc_.createElementNS(MOA_NS_URI, "ReferenceInfo"); + signatureManifestCheckParamsElem.appendChild(referenceInfoElem); + for (int i = 0; i < verifyTransformsInfoProfileID.length; i++) { + Element verifyTransformsInfoProfileIDElem = + requestDoc_.createElementNS(MOA_NS_URI, "VerifyTransformsInfoProfileID"); + referenceInfoElem.appendChild(verifyTransformsInfoProfileIDElem); + verifyTransformsInfoProfileIDElem.appendChild( + requestDoc_.createTextNode(verifyTransformsInfoProfileID[i])); + } + Element returnHashInputDataElem = + requestDoc_.createElementNS(MOA_NS_URI, "ReturnHashInputData"); + requestElem_.appendChild(returnHashInputDataElem); + Element trustProfileIDElem = requestDoc_.createElementNS(MOA_NS_URI, "TrustProfileID"); + trustProfileIDElem.appendChild(requestDoc_.createTextNode(trustProfileID)); + requestElem_.appendChild(trustProfileIDElem); + + } catch (Throwable t) { + throw new BuildException("builder.00", new Object[] { "VerifyXMLSignatureRequest" }, t); + } + + return requestElem_; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/AuthenticationSession.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/AuthenticationSession.java new file mode 100644 index 000000000..90d79a46d --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/AuthenticationSession.java @@ -0,0 +1,383 @@ +package at.gv.egovernment.moa.id.auth.data; + +import java.util.Date; +import java.util.List; + +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Constants; + +/** + * Session data to be stored between <code>AuthenticationServer</code> API calls. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationSession { + + private static String TARGET_PREFIX_ = Constants.URN_PREFIX_CDID + "+"; + + /** + * session ID + */ + private String sessionID; + /** + * "Geschäftsbereich" the online application belongs to; maybe <code>null</code> + * if the online application is a business application + */ + 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; + /** + * URL of the BKU + */ + private String bkuURL; + /** + * 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; + /** + * Indicates whether the corresponding online application is a business + * service or not + */ + private boolean businessService; + + /** + * SAML attributes from an extended infobox validation to be appended + * to the SAML assertion delivered to the final online application. + */ + private List extendedSAMLAttributesOA; + + /** + * The boolean value for either a target or a wbPK is provided as + * SAML Attribute in the SAML Assertion or not. + */ + private boolean samlAttributeGebeORwbpk; + + /** + * SAML attributes from an extended infobox validation to be appended + * to the SAML assertion of the AUTHBlock. + */ + private List extendedSAMLAttributesAUTH; + + /** + * The issuing time of the AUTH-Block SAML assertion. + */ + private String issueInstant; + + /** + * 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 BKU URL. + * @return String + */ + public String getBkuURL() { + return bkuURL; + } + + /** + * Returns the target. + * @return String + */ + public String getTarget() { + return target; + } + + /** + * Sets the oaURLRequested. + * @param oaURLRequested The oaURLRequested to set + */ + public void setOAURLRequested(String oaURLRequested) { + this.oaURLRequested = oaURLRequested; + } + + /** + * Sets the oaPublicURLPrefix + * @param oaPublicURLPrefix The oaPublicURLPrefix to set + */ + public void setPublicOAURLPrefix(String oaPublicURLPrefix) { + this.oaPublicURLPrefix = oaPublicURLPrefix; + } + + /** + * Sets the bkuURL + * @param bkuURL The BKU URL to set + */ + public void setBkuURL(String bkuURL) { + this.bkuURL = bkuURL; + } + + /** + * Sets the target. If the target includes the target prefix, the prefix will be stripped off. + * @param target The target to set + */ + public void setTarget(String target) { + if (target != null && target.startsWith(TARGET_PREFIX_)) + { + // If target starts with prefix "urn:publicid:gv.at:cdid+"; remove prefix + this.target = target.substring(TARGET_PREFIX_.length()); + Logger.debug("Target prefix stripped off; resulting target: " + this.target); + } + else + { + 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 businessService. + * @return <code>true</code> if the corresponding online application is + * a business application, otherwise <code>false</code> + */ + public boolean getBusinessService() { + return businessService; + } + + /** + * Sets the businessService variable. + * @param businessService the value for setting the businessService variable. + */ + public void setBusinessService(boolean businessService) { + this.businessService = businessService; + } + + /** + * 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; + } + + /** + * Returns the SAML Attributes to be appended to the AUTHBlock. Maybe <code>null</code>. + * + * @return The SAML Attributes to be appended to the AUTHBlock. Maybe <code>null</code>. + */ + public List getExtendedSAMLAttributesAUTH() { + return extendedSAMLAttributesAUTH; + } + + /** + * Sets the SAML Attributes to be appended to the AUTHBlock. + * + * @param extendedSAMLAttributesAUTH The SAML Attributes to be appended to the AUTHBlock. + */ + public void setExtendedSAMLAttributesAUTH( + List extendedSAMLAttributesAUTH) { + this.extendedSAMLAttributesAUTH = extendedSAMLAttributesAUTH; + } + + /** + * Returns the SAML Attributes to be appended to the SAML assertion + * delivered to the online application. Maybe <code>null</code>. + * + * @return The SAML Attributes to be appended to the SAML assertion + * delivered to the online application + */ + public List getExtendedSAMLAttributesOA() { + return extendedSAMLAttributesOA; + } + + /** + * Sets the SAML Attributes to be appended to the SAML assertion + * delivered to the online application. + * + * @param extendedSAMLAttributesOA The SAML Attributes to be appended to the SAML + * assertion delivered to the online application. + */ + public void setExtendedSAMLAttributesOA( + List extendedSAMLAttributesOA) { + this.extendedSAMLAttributesOA = extendedSAMLAttributesOA; + } + + /** + * Returns the boolean value for either a target or a wbPK is + * provided as SAML Attribute in the SAML Assertion or not. + * + * @return true either a target or a wbPK is provided as SAML Attribute + * in the SAML Assertion or false if not. + */ + public boolean getSAMLAttributeGebeORwbpk() { + return this.samlAttributeGebeORwbpk; + } + + /** + * Sets the boolean value for either a target or a wbPK is + * provided as SAML Attribute in the SAML Assertion or not. + * + * @param samlAttributeGebeORwbpk The boolean for value either a target or + * wbPK is provided as SAML Attribute in the SAML Assertion or not. + */ + public void setSAMLAttributeGebeORwbpk(boolean samlAttributeGebeORwbpk) { + this.samlAttributeGebeORwbpk = samlAttributeGebeORwbpk; + } + + /** + * Returns the issuing time of the AUTH-Block SAML assertion. + * + * @return The issuing time of the AUTH-Block SAML assertion. + */ + public String getIssueInstant() { + return issueInstant; + } + + /** + * Sets the issuing time of the AUTH-Block SAML assertion. + * + * @param issueInstant The issueInstant to set. + */ + public void setIssueInstant(String issueInstant) { + this.issueInstant = issueInstant; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/CreateXMLSignatureResponse.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/CreateXMLSignatureResponse.java new file mode 100644 index 000000000..d121f2c55 --- /dev/null +++ b/id/server/idserverlib/src/main/java/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 samlAttributes The samlAttributes 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/ExtendedSAMLAttribute.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/ExtendedSAMLAttribute.java new file mode 100644 index 000000000..795079227 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/ExtendedSAMLAttribute.java @@ -0,0 +1,73 @@ +package at.gv.egovernment.moa.id.auth.data; + +/** + * A SAML-Attribute to be appended to the final SAML-Assertion + * that will be passed to the online application. + */ +public interface ExtendedSAMLAttribute { + /** + * Add this attribute only to the SAML-Assertion + * passed to the online application, but not to + * the AUTH-Block. + */ + public final static int NOT_ADD_TO_AUTHBLOCK = 0; + /** + * Add this attribute to both, the AUTH-Block and the + * final SAML-Assertion passed to the online application. + */ + public final static int ADD_TO_AUTHBLOCK = 1; + /** + * Add this attribute to only the AUTH-Block, but not + * to the final SAML-Assertion passed to the online application. + */ + public final static int ADD_TO_AUTHBLOCK_ONLY = 2; + + /** + * The value of the SAML-Attribute. This must be either a + * <code>org.w3c.Element</code> or a <code>java.lang.String</code> + * object. Each other type will be ignored. <br> + * If, for example, the type of the actual SAML-Attribute is a + * <code><xsd:boolean></code> the value must be either the String + * <code>"true"</code> or <code>"false"</code>. + * Or the <code><xsd:integer></code> number <code>273</code> + * has to be the String <code>"273"</code>. + * + * @return The value of the SAML-Attribute. Must not be <code>null</code>. + */ + public Object getValue(); + + /** + * The name of the SAML-Attribute. + * + * @return The name of the SAML-Attribute. Must not be <code>null</code>. + */ + public String getName(); + + /** + * The namespace of the SAML-Attribute. + * An application will use the context specific namespace URI for the attribute it returns. + * However, if the application cannot explicitely assign a namespace URI, the + * {@link at.gv.egovernment.moa.util.Constants#MOA_NS_URI default} MOA namespace URI + * should be used. + * + * @return The namespace of the SAML-Attribute. Must not be <code>null</code>. + */ + public String getNameSpace(); + + /** + * Specifies if this SAML-Attribute should be added to the AUTH-Block. + * <br> + * Depending on the returned value, this SAML-Attribute should be only added to the + * final SAML-Assertion passed to the online application (0), to both, the final + * assertion and the AUTH-Block (1) or to the AUTH-Block only (2). + * + * @return <ul> + * <li>0 - add this SAML-Attribute to the final SAML-Assertion only</li> + * <li>1 - add this SAML-Attribute to both, the final SAML-Assertion and the + * AUTH-Block</li> + * <li>2 - add this SAML-Attribute to the AUTH-Block only + * </ul> + */ + public int getAddToAUTHBlock(); + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/ExtendedSAMLAttributeImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/ExtendedSAMLAttributeImpl.java new file mode 100644 index 000000000..e7e490924 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/ExtendedSAMLAttributeImpl.java @@ -0,0 +1,137 @@ +package at.gv.egovernment.moa.id.auth.data; + +/** + * This class contains SAML attributes to be appended to the SAML assertion delivered to + * the Online application. + * + * @author Harald Bratko + */ +public class ExtendedSAMLAttributeImpl implements ExtendedSAMLAttribute { + + /** + * The value of this SAML attribute. Must be either of type <code>java.lang.String</code> + * or <code>org.w3c.Element</code>. + */ + protected Object value_; + + /** + * The name of this SAML attribute. + */ + protected String name_; + + /** + * The namespace URI of this SAML attribute. + */ + protected String namespace_; + + /** + * Specifies whether this SAML attribute should be appended to AUTH Block. + */ + protected int addToAUTHBlock_; + + /** + * Sets this ExtendedSAMLAttribute. + * @param name The name of this SAML Attribute. + * @param value The value of this SAML Attribute. Must be either of type + * <code>java.lang.String</code> or <code>org.w3c.dom.Element</code>. + * @param namespace The namespace of this SAML Attribute. + * @param addToAUTHBlock Specifies if this SAML Attribute should be added to the AUTHBlock. + * The following values are allowed: + * <ul> + * <li> + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#ADD_TO_AUTHBLOCK} + * </li> + * <li> + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#NOT_ADD_TO_AUTHBLOCK} + * </li> + * <li> + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#ADD_TO_AUTHBLOCK_ONLY} + * </li> + * </ul> + * + */ + public ExtendedSAMLAttributeImpl(String name, Object value, String namespace, int addToAUTHBlock) { + name_ = name; + value_ = value; + namespace_ = namespace; + addToAUTHBlock_ = addToAUTHBlock; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#getValue() + */ + public Object getValue() { + return value_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#getName() + */ + public String getName() { + return name_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#getNameSpace() + */ + public String getNameSpace() { + return namespace_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#getAddToAUTHBlock() + */ + public int getAddToAUTHBlock() { + return addToAUTHBlock_; + } + + /** + * Specifies if this SAML Attribute should be added to the AUTHBlock. + * + * @param addToAUTHBlock One of the following values: + * <ul> + * <li> + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#ADD_TO_AUTHBLOCK} + * </li> + * <li> + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#NOT_ADD_TO_AUTHBLOCK} + * </li> + * <li> + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#ADD_TO_AUTHBLOCK_ONLY} + * </li> + * </ul> + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute#ADD_TO_AUTHBLOCK} + */ + public void setAddToAUTHBlock(int addToAUTHBlock) { + addToAUTHBlock_ = addToAUTHBlock; + } + + /** + * Sets the name of this SAML attribute. + * + * @param name The name of this SAML attribute. + */ + public void setName(String name) { + name_ = name; + } + + /** + * Sets the namespace of this SAML attribute. + * + * @param namespace The namespace to set. + */ + public void setNamespace(String namespace) { + namespace_ = namespace; + } + + /** + * Sets the value of this SAML attribute. + * + * @param value The value of this SAML Attribute. Must be either of type + * <code>java.lang.String</code> or <code>org.w3c.dom.Element</code>. + */ + public void setValue(Object value) { + value_ = value; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/IdentityLink.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/IdentityLink.java new file mode 100644 index 000000000..844451035 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/IdentityLink.java @@ -0,0 +1,266 @@ +package at.gv.egovernment.moa.id.auth.data; + +import java.io.IOException; +import java.security.PublicKey; + +import javax.xml.transform.TransformerException; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.util.DOMUtils; + + +/** + * 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>"Stammzahl"</code>. + */ + private String identificationValue; + /** + * <code>"identificationType"</code> type of the identificationValue in the IdentityLink. + */ + private String identificationType; + /** + * first name + */ + private String givenName; + /** + * family name + */ + private String familyName; + + /** + * The name as (givenName + familyName) + */ + private String name; + /** + * date of birth + */ + private String dateOfBirth; + /** + * the original saml:Assertion-Element + */ + private Element samlAssertion; + /** + * the serializes saml:Assertion + */ + private String serializedSamlAssertion; + /** + * 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; + + /** + * The issuing time of the identity link SAML assertion. + */ + private String issueInstant; + + /** + * 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 name. + * @return The name. + */ + public String getName() { + if (name == null) { + name = givenName + " " + familyName; + } + return name; + } + + /** + * Returns the identificationValue. + * <code>"identificationValue"</code> is the translation of <code>"Stammzahl"</code>. + * @return String + */ + public String getIdentificationValue() { + return identificationValue; + } + + /** + * Returns the identificationType. + * <code>"identificationType"</code> type of the identificationValue in the IdentityLink. + * @return String + */ + public String getIdentificationType() { + return identificationType; + } + + /** + * 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>"Stammzahl"</code>. + * @param identificationValue The identificationValue to set + */ + public void setIdentificationValue(String identificationValue) { + this.identificationValue = identificationValue; + } + + /** + * Sets the Type of the identificationValue. + * @param identificationType The type of identificationValue to set + */ + public void setIdentificationType(String identificationType) { + this.identificationType = identificationType; + } + + /** + * Returns the samlAssertion. + * @return Element + */ + public Element getSamlAssertion() { + return samlAssertion; + } + + /** + * Returns the samlAssertion. + * @return Element + */ + public String getSerializedSamlAssertion() { + return serializedSamlAssertion; + } + + /** + * Sets the samlAssertion and the serializedSamlAssertion. + * @param samlAssertion The samlAssertion to set + */ + public void setSamlAssertion(Element samlAssertion) throws TransformerException, IOException { + this.samlAssertion = samlAssertion; + this.serializedSamlAssertion = DOMUtils.serializeNode(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; + } + + /** + * Returns the issuing time of the identity link SAML assertion. + * + * @return The issuing time of the identity link SAML assertion. + */ + public String getIssueInstant() { + return issueInstant; + } + + /** + * Sets the issuing time of the identity link SAML assertion. + * + * @param issueInstant The issueInstant to set. + */ + public void setIssueInstant(String issueInstant) { + this.issueInstant = issueInstant; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxToken.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxToken.java new file mode 100644 index 000000000..6b1f24aba --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxToken.java @@ -0,0 +1,65 @@ +package at.gv.egovernment.moa.id.auth.data; + +import org.w3c.dom.Element; + +/** + * Contains an infobox token included in an <code>InfoboxReadResponse</code>. + * Depending on whether the token is extracted from an <code>XMLContent</code> or a + * <code>Base64Content</code> it is either returned as DOM element + * ({@link #getXMLToken()}) or base64 encoded string ({@link #getBase64Token()}). + * + * @author Harald Bratko + */ +public interface InfoboxToken { + + /** + * The key of the corresponding infobox. + * This is the value of <code>Key</code> attribute of the <code><Pair></code> child + * element in an <code><AssocArrayData></code> content of an InfoboxReadResponse. + * Maybe <code>null</code> if the InfoboxReadResponse conatains <code>BinaryFileData</code>. + * + * + * @return The key identifier of the corresponding infobox. + */ + public String getKey(); + + /** + * Specifies if this token is the first token (e.g in an array of tokens) included in an + * <code>InfoboxReadResponse</code>. If <code>true</code> this token is the token to be + * validated by a corresponding + * {@link at.gv.egovernment.moa.id.auth.validator.InfoboxValidator InfoboxValidator}. + * If <code>false</code> this token maybe needed to validate the primary token. + * + * @return <code>True</code> if this token is the first token. + */ + public boolean isPrimary(); + + /** + * Returns the infobox token. + * Maybe <code>null</code> if the token is returned by method {@link #getBase64Token()} + * as base64 encoded string. + * <br> + * Note that this token is <code><i><b>not</b></i></code> validated against the + * application specific schema (e.g. Mandates schema in the mandates context). + * Thus the validating application <code><i><b>has to</b></i></code> validate the token + * against the appropriate schema. + * + * @return The infobox token. If <code>null</code> the token is returned by method + * {@link #getBase64Token()} as base64 encoded string. + */ + public Element getXMLToken(); + + /** + * Returns the infobox token. + * Maybe <code>null</code> if the token is returned by method {@link #getXMLToken()} + * as a DOM element. + * <br> + * Note, that the base64 encoded string actually may include more than one infobox + * elements. + * + * @return The infobox token. If <code>null</code> the token is returned by method + * {@link #getBase64Token()} as base64 encoded string. + */ + public String getBase64Token(); + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxTokenImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxTokenImpl.java new file mode 100644 index 000000000..f1ff30a8a --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxTokenImpl.java @@ -0,0 +1,129 @@ +package at.gv.egovernment.moa.id.auth.data; + +import org.w3c.dom.Element; + +/** + * This class contains an infobox token. + * + * @see at.gv.egovernment.moa.id.auth.data.InfoboxToken + * + * @author Harald Bratko + */ +public class InfoboxTokenImpl implements InfoboxToken { + + /** + * The key of the infobox token. + */ + private String key_; + + /** + * Specifies whether this token is the primary (first in an array) token. + */ + private boolean primary_; + + /** + * The infobox token. + */ + private Element xmlToken_; + + /** + * The base64 encoded infobox token. + */ + private String base64Token_; + + /** + * Sets an XML infobox token. + * + * @param key The key of the infobox token. + * @param primary <code>True</code> this token is the primary (e.g .first in an array) + * token, otherwise <code>false</code> + * @param xmlToken The infobox token. + */ + public InfoboxTokenImpl(String key, boolean primary, Element xmlToken) { + key_ = key; + primary_ = primary; + xmlToken_ = xmlToken; + base64Token_ = null; + } + + /** + * Sets a base64 encoded infobox token. + * + * @param key The key of the infobox token. + * @param primary <code>True</code> this token is the primary (e.g .first in an array) + * token, otherwise <code>false</code> + * @param base64Token The base64 encoded infobox token. + */ + public InfoboxTokenImpl(String key, boolean primary, String base64Token) { + key_ = key; + primary_ = primary; + base64Token_ = base64Token; + xmlToken_ = null; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxToken#getKey() + */ + public String getKey() { + return key_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxToken#isPrimary() + */ + public boolean isPrimary() { + return primary_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxToken#getXMLToken() + */ + public Element getXMLToken() { + return xmlToken_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxToken#getBase64Token() + */ + public String getBase64Token() { + return base64Token_; + } + + /** + * Sets the key of the infobox token. + * + * @param key The key of the infobox token. + */ + public void setKey(String key) { + key_ = key; + } + + /** + * Specifies whether this token is the primary (e.g. first in an array) token. + * + * @param primary <code>True</code> this token is the primary (e.g .first in an array) + * token, otherwise <code>false</code>. + */ + public void setPrimary(boolean primary) { + primary_ = primary; + } + + /** + * Sets the base64 encoded token. + * + * @param base64Token The base64 encoded token. + */ + public void setBase64Token(String base64Token) { + base64Token_ = base64Token; + } + + /** + * Sets the infobox token. + * + * @param xmlToken The infobox token. + */ + public void setXmlToken(Element xmlToken) { + xmlToken_ = xmlToken; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidationResult.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidationResult.java new file mode 100644 index 000000000..b8dd7f18e --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidationResult.java @@ -0,0 +1,57 @@ +package at.gv.egovernment.moa.id.auth.data; + + +/** + * Includes the result of an extended infobox validation. + * + * If validation succeeds, an array of + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute ExtendedSAMLAttributes} + * maybe provided. Each of these SAML-Attributes will be either appended to the + * final SAML-Assertion passed to the online application or to the AUTH-Block, + * or to both. + * <br> + * If validation fails the implementing class has to provide a short error message. + * + * @author Harald Bratko + */ +public interface InfoboxValidationResult { + + /** + * The method returns <code>true</code> if validation succeeds. In that case + * method {@link #getExtendedSamlAttributes()} may provide an array of + * {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute + * ExtendedSAMLAttributes} that should be appended to the final SAML-Assertion or the + * AUTH-Block or to both. + * <br> + * The method returns <code>false</code> if validation fails. In that case + * method {@link #getErrorMessage()} has to provide a short error description. + * + * @return <code>True</code> if validation succeeds, + * otherwise <code>false</code>. + */ + public boolean isValid(); + + /** + * Returns an array of {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute + * ExtendedSAMLAttributes} that should be added to the SAML-Assertion + * provided to the online application. + * The SAML-Attributes in that array will be added to the final + * SAML-Assertion, the AUTH-Block, or both, exactly in the order as they are arranged + * in the array this method returns. + * + * @return An array of {@link at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute + * ExtendedSAMLAttributes} that should be added to the SAML-Assertion + * provided to the online application, the AUTH-Block, or both. If no attributes should + * be added this array maybe <code>null</code> or empty. + */ + public ExtendedSAMLAttribute[] getExtendedSamlAttributes(); + + /** + * A short error description that should be displayed by MOA-ID if + * validation of the InfoBoxReadResponse fails. + * + * @return An short error message if validation fails. + */ + public String getErrorMessage(); + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidationResultImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidationResultImpl.java new file mode 100644 index 000000000..97f52fb55 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidationResultImpl.java @@ -0,0 +1,101 @@ +package at.gv.egovernment.moa.id.auth.data; + + +/** + * Default implementation of the {@link InfoboxValidationResult} interface. + * + * @author Harald Bratko + */ +public class InfoboxValidationResultImpl implements InfoboxValidationResult { + + /** + * Indicates whether the validation was successful or not. + */ + protected boolean valid_; + + /** + * The error message. + */ + protected String errorMessage_; + + /** + * The SAML attributes returned by the infobox validator. + */ + protected ExtendedSAMLAttribute[] extendedSamlAttributes_; + + /** + * Empty constructor. + */ + public InfoboxValidationResultImpl() { + } + + /** + * Constructor to set all values. + * + * @param valid Global validation result. + * @param extendedSamlAttributes SAML attributes that should be appended to the final + * <code>SAML Assertion</code> or to the <code>AUTH Block</code> + * or to both. + * @param errorMessage An error message if infobox validation fails. + */ + public InfoboxValidationResultImpl( + boolean valid, + ExtendedSAMLAttribute[] extendedSamlAttributes, + String errorMessage) + { + valid_ = valid; + extendedSamlAttributes_ = extendedSamlAttributes; + errorMessage_ = errorMessage; + } + + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidationResult#getErrorMessage() + */ + public String getErrorMessage() { + return errorMessage_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidationResult#getExtendedSamlAttributes() + */ + public ExtendedSAMLAttribute[] getExtendedSamlAttributes() { + return extendedSamlAttributes_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidationResult#isValid() + */ + public boolean isValid() { + return valid_; + } + + /** + * Sets the error message if validation fails.. + * + * @param errorMessage The error message to set. + */ + public void setErrorMessage(String errorMessage) { + errorMessage_ = errorMessage; + } + + /** + * Sets the SAML attributes returned by the infobox validatior.. + * + * @param extendedSamlAttributes The SAML attributes returned by the infobox validator. + */ + public void setExtendedSamlAttributes(ExtendedSAMLAttribute[] extendedSamlAttributes) { + extendedSamlAttributes_ = extendedSamlAttributes; + } + + /** + * Sets validation result.. + * + * @param valid <code>True</code> if the infobox could be validated successfully, + * otherwise <code>false</code>. + */ + public void setValid(boolean valid) { + valid_ = valid; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidatorParams.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidatorParams.java new file mode 100644 index 000000000..c7a557290 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidatorParams.java @@ -0,0 +1,172 @@ +package at.gv.egovernment.moa.id.auth.data; + +import java.security.PublicKey; +import java.util.List; + +import org.w3c.dom.Element; + +/** + * Parameters for validating an infobox. + * + * This interface is used by MOA-ID to provide parameters to an + * {link at.gv.egovernment.moa.id.auth.validator.InfoboxValidator + * InfoboxValidator}. + * + * @author Harald Bratko + */ +public interface InfoboxValidatorParams { + + /** + * Returns a list of {@link at.gv.egovernment.moa.id.auth.data.InfoboxToken InfoboxToken} + * objects. The first token in this list is the one to be validated. Each further token + * maybe needed to validate this first token. + * + * @return A list of {@link at.gv.egovernment.moa.id.auth.data.InfoboxToken InfoboxToken} + * objects. + */ + public List getInfoboxTokenList(); + + /** + * Returns the ID of the trust profile to be used for validating + * certificates. Maybe ignored by a validator, if no certificates + * has to be validated. + * + * @return The ID of a trust profile. + */ + public String getTrustProfileID(); + + /** + * Returns schema location URIs that may be needed by the + * validator to parse infobox tokens. + * Each entry in the list is a {@link Schema} specifying the location + * of an XML schema. + * + * @return A list of {@link Schema} objects each of them specifying the + * location of an XML schema. + */ + public List getSchemaLocations(); + + /** + * Returns the URL of the BKU. + * Maybe needed by a validator. + * + * @return The url of the BKU. + */ + public String getBkuURL(); + + /** + * Returns the target parameter. + * <code>null</code> in the case of a business service. + * + * @return The target parameter. + */ + public String getTarget(); + + /** + * Returns <code>true</code> if the application is a business + * service, otherwise <code>false</code>. This may be useful + * for the validating application. + * + * @return <code>True</code> if the application is a business + * service, otherwise <code>false</code> + */ + public boolean getBusinessApplication(); + + /** + * Returns the family name from the identity link. + * Maybe needed by a validator. + * + * @return The family name from the identity link. + */ + public String getFamilyName(); + + /** + * Returns the given name from the identity link. + * Maybe needed by a validator. + * + * @return The given name from the identity link. + */ + public String getGivenName(); + + /** + * The date of birth from the identity link. + * The method returns the value of the <pr:DateOfBirth> + * element from the identity link. + * Maybe needed by a validator. + * + * @return The date of birth from the identity link. + */ + public String getDateOfBirth(); + + /** + * Returns he identification value from the identity + * link. This may be the <code>Stammzahl</code> + * in the case of a public application or the + * <code>wbPK</code> in the case of a business + * application. This parameter is only returned + * if specified within the config file. + * + * @return The identification value from the identity link. + */ + public String getIdentificationValue(); + + /** + * Returns the type of the identification value + * from the identity link. This may be + * especially of interest for business + * applications. + * + * @return The type of the identification value + * from the identity link. + */ + public String getIdentificationType(); + + /** + * Returns the public keys from the identity link. + * Maybe needed by the application. + * + * @return PublicKey[] The public keys from the + * identity link. + */ + public PublicKey[] getPublicKeys(); + + /** + * Returns the identity link. + * A validator may need other data from the identity link, than + * family name, given name, date of birth and identification value. + * The identity link element is only returned if specified within the + * config file. + * + * @return The identity link. + */ + public Element getIdentityLink(); + + /** + * Indicates whether source pins (<code>Stammzahl</code>en) should be hidden or not. + * If an online application lying behind MOA-ID is not allowed to get source pins + * (<code>Stammzahl</code>en), any source pins within <code>SAML attributes</code> + * returned by the validator must suppressed:<br> + * If the parameter <code>getHideStammzahl</code> is <code>true</code>, then the validator + * <b>MUST</b> hide (replace by an empty string) any source pin (<code>Stammzahl</code>) + * that may be included in a <code>SAML attribute</code> returned by the validator. + * + * @return <code>true</code> if source pins (<code>Stammzahl</code>en) must be hidden, + * otherwise <code>false</code>. + */ + public boolean getHideStammzahl(); + + /** + * Returns application specific parameters. + * Each child element of this element contains + * a validating application specific parameter. The + * element is passed as specified within the config + * file and its up to the implementing class to + * parse and interprete its children. + * + * @return Application specific parameters. + */ + public Element getApplicationSpecificParams(); + +} + + diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidatorParamsImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidatorParamsImpl.java new file mode 100644 index 000000000..80ba5995f --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/InfoboxValidatorParamsImpl.java @@ -0,0 +1,348 @@ +package at.gv.egovernment.moa.id.auth.data; + +import java.security.PublicKey; +import java.util.List; + +import org.w3c.dom.Element; + +/** + * Parameters for validating an infobox. + * + * This interface is used by MOA-ID to provide parameters to an + * {link at.gv.egovernment.moa.id.auth.validator.InfoboxValidator + * InfoboxValidator}. + * + * @author Harald Bratko + */ +public class InfoboxValidatorParamsImpl implements InfoboxValidatorParams { + + /** + * A list of {@link at.gv.egovernment.moa.id.auth.data.InfoboxToken InfoboxToken} objects. + * The first token in this list is the one to be validated. Each further token + * maybe needed to validate this first token. + */ + protected List infoboxTokenList_; + + /** + * The ID of the trust profile used for validating certificates. + */ + protected String trustProfileID_; + + /** + * The locations of schemas that maybe needed for validating infobox tokens. + */ + protected List schemaLocations_; + + /** + * The URL of the BKU. + */ + protected String bkuURL_; + + /** + * Specifies whether the current online application is a business or a public application. + */ + protected boolean businessApplication_; + + /** + * The target parameter. + */ + protected String target_; + + /** + * The family name from the identity link. + */ + protected String familyName_; + + /** + * The given name from the identity link. + */ + protected String givenName_; + + /** + * The date of birth from the identity link. + */ + protected String dateOfBirth_; + + /** + * The date of identification value. + */ + protected String identificationValue_; + + /** + * The identification type. + */ + protected String identificationType_; + + /** + * The public keys from the identity link. + */ + protected PublicKey[] publicKeys_; + + /** + * The identity link. + */ + protected Element identityLink_; + + /** + * Indicates whether source pins (<code>Stammzahl</code>en) must be hidden or not. + */ + protected boolean hideStammzahl_; + + /** + * Application specific parameters. + */ + protected Element applicationSpecificParams_; + + /** + * Empty constructor. + */ + public InfoboxValidatorParamsImpl() { + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getInfoboxTokenList() + */ + public List getInfoboxTokenList() { + return infoboxTokenList_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getTrustProfileID() + */ + public String getTrustProfileID() { + return trustProfileID_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getSchemaLocations() + */ + public List getSchemaLocations() { + return schemaLocations_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getBkuURL() + */ + public String getBkuURL() { + return bkuURL_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getTarget() + */ + public String getTarget() { + return target_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getBusinessApplication() + */ + public boolean getBusinessApplication() { + return businessApplication_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getFamilyName() + */ + public String getFamilyName() { + return familyName_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getGivenName() + */ + public String getGivenName() { + return givenName_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getDateOfBirth() + */ + public String getDateOfBirth() { + return dateOfBirth_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getIdentificationValue() + */ + public String getIdentificationValue() { + return identificationValue_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getIdentificationType() + */ + public String getIdentificationType() { + return identificationType_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getPublicKeys() + */ + public PublicKey[] getPublicKeys() { + return publicKeys_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getIdentityLink() + */ + public Element getIdentityLink() { + return identityLink_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getHideStammzahl() + */ + public boolean getHideStammzahl() { + return hideStammzahl_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams#getApplicationSpecificParams() + */ + public Element getApplicationSpecificParams() { + return applicationSpecificParams_; + } + + /** + * Sets the application specific parameters. + * + * @param applicationSpecificParams The application specific parameters to set. + */ + public void setApplicationSpecificParams(Element applicationSpecificParams) { + applicationSpecificParams_ = applicationSpecificParams; + } + + /** + * Sets the bku URL. + * + * @param bkuURL The bku URL to set. + */ + public void setBkuURL(String bkuURL) { + bkuURL_ = bkuURL; + } + + /** + * Sets the business application parameter. + * + * @param businessApplication The business application parameter to set. + * (<code>True</code> if the application is a business + * application, otherwies <code>false</code>). + */ + public void setBusinessApplication(boolean businessApplication) { + businessApplication_ = businessApplication; + } + + /** + * Sets the date of birth. + * + * @param dateOfBirth The date of birth. + */ + public void setDateOfBirth(String dateOfBirth) { + dateOfBirth_ = dateOfBirth; + } + + /** + * Sets the family name. + * + * @param familyName The family name. + */ + public void setFamilyName(String familyName) { + familyName_ = familyName; + } + + /** + * Sets the given name. + * + * @param givenName The given name. + */ + public void setGivenName(String givenName) { + givenName_ = givenName; + } + + /** + * Sets the identification type. + * + * @param identificationType The identification type. + */ + public void setIdentificationType(String identificationType) { + identificationType_ = identificationType; + } + + /** + * Sets the identification value. + * + * @param identificationValue The identification value. + */ + public void setIdentificationValue(String identificationValue) { + identificationValue_ = identificationValue; + } + + /** + * Sets the identity link. + * + * @param identityLink The identity link. + */ + public void setIdentityLink(Element identityLink) { + identityLink_ = identityLink; + } + + /** + * Sets the infobox token to be validated. + * + * @param infoboxTokenList A list {@link at.gv.egovernment.moa.id.auth.data.InfoboxToken InfoboxToken} + * objects. + */ + public void setInfoboxTokenList(List infoboxTokenList) { + infoboxTokenList_ = infoboxTokenList; + } + + /** + * Sets the public Keys. + * + * @param publicKeys The public keys. + */ + public void setPublicKeys(PublicKey[] publicKeys) { + publicKeys_ = publicKeys; + } + + /** + * Sets the schema locations. + * + * @param schemaLocations The schema locations. A list of + * {@link Schema} objects. + */ + public void setSchemaLocations(List schemaLocations) { + schemaLocations_ = schemaLocations; + } + + /** + * Sets the target. + * + * @param target The target. + */ + public void setTarget(String target) { + target_ = target; + } + + /** + * Sets the ID of the trust profile used for validating certificates. + * + * @param trustProfileID the ID of the trust profile used for validating certificates. + */ + public void setTrustProfileID(String trustProfileID) { + trustProfileID_ = trustProfileID; + } + + /** + * Sets the {@link #hideStammzahl_} parameter. This indicates whether source pins + * (<code>Stammzahl</code>en) must be hidden or not. + * + * @param hideStammzahl <code>True</code> if source pins (<code>Stammzahl</code>en) should + * be hidden, otherwise <code>false</code>. + */ + public void setHideStammzahl(boolean hideStammzahl) { + hideStammzahl_ = hideStammzahl; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/SAMLAttribute.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/SAMLAttribute.java new file mode 100644 index 000000000..76ba6366d --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/SAMLAttribute.java @@ -0,0 +1,79 @@ +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 Object value; + + /** + * Constructor for SAMLAttribute. + */ + public SAMLAttribute(String name, String namespace, Object 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 Object 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(Object value) { + this.value = value; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/Schema.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/Schema.java new file mode 100644 index 000000000..c113f16b8 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/Schema.java @@ -0,0 +1,26 @@ +package at.gv.egovernment.moa.id.auth.data; + +/** + * Specifies the location of a schema. + * + * @author Harald Bratko + */ +public interface Schema { + + /** + * The namespace URI of this schema. + * + * @return The namespace of this schema. + */ + public String getNamespace(); + + /** + * The location URI of this schema. + * Relative paths have to be interpreted relative to the + * location of the MOA-ID config file. + * + * @return The location URI of this schema. + */ + public String getSchemaLocation(); + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/SchemaImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/SchemaImpl.java new file mode 100644 index 000000000..964725416 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/SchemaImpl.java @@ -0,0 +1,63 @@ +package at.gv.egovernment.moa.id.auth.data; + +/** + * This class specifies the location of a schema. + * + * @author Harald Bratko + */ +public class SchemaImpl implements Schema { + + /** + * The namespace of this schema. + */ + protected String namespace_; + + /** + * The location (URI) of this schema; + */ + protected String schemaLocation_; + + /** + * Sets the namespace and schema location URIS of this schema. + * + * @param namespace The namespace URI of this schema. + * @param schemaLocation The location URI of this schema. + */ + public SchemaImpl(String namespace, String schemaLocation) { + namespace_ = namespace; + schemaLocation_ = schemaLocation; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.Schema#getNamespace() + */ + public String getNamespace() { + return namespace_; + } + + /** + * @see at.gv.egovernment.moa.id.auth.data.Schema#getSchemaLocation() + */ + public String getSchemaLocation() { + return schemaLocation_; + } + + /** + * Sets the namespace. + * + * @param namespace The namespace to set. + */ + public void setNamespace(String namespace) { + namespace_ = namespace; + } + + /** + * Sets the location URI of this schema. + * + * @param schemaLocation The schemaLocation to set. + */ + public void setSchemaLocation(String schemaLocation) { + schemaLocation_ = schemaLocation; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/VerifyXMLSignatureResponse.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/data/VerifyXMLSignatureResponse.java new file mode 100644 index 000000000..8233d1478 --- /dev/null +++ b/id/server/idserverlib/src/main/java/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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/invoke/SignatureVerificationInvoker.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/invoke/SignatureVerificationInvoker.java new file mode 100644 index 000000000..a18cf7322 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/invoke/SignatureVerificationInvoker.java @@ -0,0 +1,92 @@ +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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/CreateXMLSignatureResponseParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/CreateXMLSignatureResponseParser.java new file mode 100644 index 000000000..a8b870f04 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/CreateXMLSignatureResponseParser.java @@ -0,0 +1,193 @@ +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.AuthenticationException; +import at.gv.egovernment.moa.id.ParseException; +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 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 = ":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 CreateXMLsignatureResponse */ + private Element sigResponse_; + + /** + * Parses and validates the document given as string and extracts the + * root element. + * + * @param xmlResponse <code><CreateXMLSignatureResponse></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 { + try { + InputStream s = new ByteArrayInputStream(xmlResponse.getBytes("UTF-8")); + init(s); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Parses and validates the document given as stream and extracts the + * root element. + * + * @param is <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 { + init(is); + } + + /** + * 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; + } + + /** + * Initializes the parser. + * Parses and validates the document given as stream and extracts the + * root element. + * + * @param is The CreateXMLSignatureResponse as stream. + * @throws AuthenticationException if an authentication error occurs. + * @throws ParseException If an error occurs on parsing the the document. + */ + private void init(InputStream is) throws AuthenticationException, ParseException { + try { + + Element responseElem = DOMUtils.parseXmlValidating(is); + + if ("CreateXMLSignatureResponse".equals(responseElem.getLocalName())) { + sigResponse_ = responseElem; + } else { + ErrorResponseParser erp = new ErrorResponseParser(responseElem); + throw new AuthenticationException("auth.08", new Object[] { erp.getErrorCode(), erp.getErrorInfo()}); + } + + } catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Unmarshalls the <@link sigResponse> to an + * <code><CreateXMLSignatureResponse></code> object. + * + * @return a <code><CreateXMLSignatureResponse></code> object + * @throws ParseException + */ + + public CreateXMLSignatureResponse parseResponse() throws ParseException { + CreateXMLSignatureResponse cResp; + try { + cResp = new CreateXMLSignatureResponse(); + String slPrefix = XPathUtils.getSlPrefix(sigResponse_); + cResp.setSamlNameIdentifier(XPathUtils.getElementValue(sigResponse_, "/" + slPrefix + SAML_SUBJECT_NAME_IDENTIFIER_XPATH, null)); + cResp.setSamlAssertion((Element) XPathUtils.selectSingleNode(sigResponse_, "/" + slPrefix + SAML_ASSERTION_XPATH)); + NodeIterator attrIter = XPathUtils.selectNodeIterator(sigResponse_, "/" + slPrefix + 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", ""); + Object attrValue; + Element attrValueElem = (Element)XPathUtils.selectSingleNode(samlAttr, SAML_ATTRIBUTE_VALUE_XPATH); + attrValue = DOMUtils.getElementFromNodeList(attrValueElem.getChildNodes()); + if (attrValue == null) { + if (null!=attrValueElem.getFirstChild()) { + attrValue = attrValueElem.getFirstChild().getNodeValue(); + } else { + attrValue = ""; + } + } + 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; + } + +// public CreateXMLSignatureResponse parseResponse() throws ParseException { +// CreateXMLSignatureResponse cResp; +// try { +// cResp = new CreateXMLSignatureResponse(); +// Element samlAssertion = (Element)sigResponse.getElementsByTagNameNS(Constants.SAML_NS_URI, "Assertion").item(0); +// cResp.setSamlAssertion(samlAssertion); +// Element samlAttributeStatement = (Element)samlAssertion.getElementsByTagNameNS(Constants.SAML_NS_URI, "AttributeStatement").item(0); +// Element samlSubject = (Element)samlAttributeStatement.getElementsByTagNameNS(Constants.SAML_NS_URI, "Subject").item(0); +// Element samlNameIdentifier = (Element)samlSubject.getElementsByTagNameNS(Constants.SAML_NS_URI, "NameIdentifier").item(0); +// cResp.setSamlNameIdentifier(samlNameIdentifier.getFirstChild().getNodeValue()); +// NodeList nl = samlAttributeStatement.getElementsByTagNameNS(Constants.SAML_NS_URI, "Attribute"); +// List samlAttributes = new ArrayList(); +// for (int i=0; i<nl.getLength(); i++) { +// Element samlAttribute = (Element)nl.item(i); +// String attrName = samlAttribute.getAttribute("AttributeName"); +// String attrNamespace = samlAttribute.getAttribute("AttributeNamespace"); +// String attrValue = ((Element)samlAttribute.getElementsByTagNameNS(Constants.SAML_NS_URI, "AttributeValue").item(0)).getFirstChild().getNodeValue(); +// 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/ErrorResponseParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/ErrorResponseParser.java new file mode 100644 index 000000000..e3c54095d --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/ErrorResponseParser.java @@ -0,0 +1,72 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.util.DOMUtils; + +/** + * Parses an <code><ErrorResponse></code>. + * + * @author Stefan Knirsch + * @version $Id$ + */ + +public class ErrorResponseParser { + + /** + * The error code included in this error response. + * <code>1000</code> is used as default value, if some problems occur on + * evaluating the error response. + */ + private String errorCode_ = "1000"; + + /** + * The error info included in this error response. + * <code><Unklassifizierter Fehler.></code> is used as default value, + * if some problems occur on evaluating the error response. + */ + private String errorInfo_ = "Unklassifizierter Fehler."; + + + /** + * This Constructor extracts the error code and error info included in this + * error response. + * + * @param errorElement The error element. This is the root element of + * the error response. + */ + public ErrorResponseParser(Element errorElement) throws ParseException { + if (errorElement != null) { + String namespace = errorElement.getNamespaceURI(); + NodeList nl = errorElement.getElementsByTagNameNS(namespace, "ErrorCode"); + if (nl.getLength() == 1) { + errorCode_ = ((Element)nl.item(0)).getFirstChild().getNodeValue(); + } + nl = errorElement.getElementsByTagNameNS(namespace, "Info"); + if (nl.getLength() == 1) { + errorInfo_ = ((Element)nl.item(0)).getFirstChild().getNodeValue(); + } + } + } + + /** + * Returns the error code included in this error response. + */ + public String getErrorCode() { + return errorCode_ ; + } + + /** + * Returns the information included in this error response. + * @return The error infomation String + */ + public String getErrorInfo() { + return errorInfo_ ; + } + + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/ExtendedInfoboxReadResponseParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/ExtendedInfoboxReadResponseParser.java new file mode 100644 index 000000000..e493f07fb --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/ExtendedInfoboxReadResponseParser.java @@ -0,0 +1,157 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.auth.data.InfoboxToken; +import at.gv.egovernment.moa.id.auth.data.InfoboxTokenImpl; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; + +/** + * Parses and unmarshales <code>InfoboxReadResponse<code>. + * This parser is especially used for parsing additional responses (additional to that + * one containing the <code>IdentityLink</code> retuned from the BKU as an answer of + * a <code><PushInfobox></code> request. + */ +public class ExtendedInfoboxReadResponseParser { + + /** + * Hide default constructor. + */ + private ExtendedInfoboxReadResponseParser() { + } + + /** + * Parses and unmarshales the given <code>infoboxReadResponse</code> to a list of + * {@link at.gv.egovernment.moa.id.auth.data.InfoboxToken InfoboxToken} objects. + * The method parses the given <code>infoboxReadResponse</code> + * + * @param infoboxReadResponse The infobox read response to be unmarshaled. + * @param infoboxName The name of the infobox the reponse corresponds to. + * + * @return A list of {@link at.gv.egovernment.moa.id.auth.data.InfoboxToken InfoboxToken} + * objects. Maybe empty. + * + * @throws ParseException If an error occurs on parsing and unmarshaling the response. + */ + public static List parseInfoboxReadResponse(String infoboxReadResponse, String infoboxName) + throws ParseException + { + Element infoboxReadResponseElem = null; + try { + Document doc = + DOMUtils.parseDocument(infoboxReadResponse, true, Constants.ALL_SCHEMA_LOCATIONS, null); + infoboxReadResponseElem = doc.getDocumentElement(); + } catch (Exception e) { + Logger.error("InfoboxReadResponse for \"" + infoboxName + + "\"-infobox could not be parsed successfully: " + e.getMessage()); + throw new ParseException("parser.01", new Object[] {infoboxName + "-InfoboxReadResponse"}); + } + + Vector infoboxTokenList = new Vector(); + + if (infoboxReadResponseElem != null) { + // avoid using namespace URI or prefix, because it might change within the response + // (e.g.: sl11-namespace, some child sl10-namespace + List infoboxReadResponseChildren = DOMUtils.getChildElements(infoboxReadResponseElem); + String key = null; + boolean primary = true; + Element infoboxReadResponseChild = (Element)infoboxReadResponseChildren.get(0); + String infoboxReadResponseChildName = infoboxReadResponseChild.getLocalName(); + if (infoboxReadResponseChildName.equals("AssocArrayData")) { + // get the <Pair> child elements from the <AssocArrayData> element + List assocArrayPairs = DOMUtils.getChildElements(infoboxReadResponseChild); + Iterator assocArrayPairIt = assocArrayPairs.iterator(); + int pairCount = 0; + // step through the <Pair> elemnts + while (assocArrayPairIt.hasNext()) { + Element assocArrayPair = (Element)assocArrayPairIt.next(); + // check if the element actually a "Pair" element and not only a "key" + if (assocArrayPair.getLocalName().equals("Key")) { + // do not accept only a Key + throw new ParseException("parser.07", new Object[] {infoboxName}); + } + key = assocArrayPair.getAttribute("Key"); + if (pairCount > 0) { + primary = false; + } + pairCount++; + infoboxTokenList.addAll(getTokenFromXMLOrBase64Content(assocArrayPair, infoboxName, key, primary)); + } + + } else if (infoboxReadResponseChildName.equals("BinaryFileData")) { + infoboxTokenList.addAll(getTokenFromXMLOrBase64Content(infoboxReadResponseChild, infoboxName, null, true)); + } + } + return infoboxTokenList; + } + + /** + * Unmarshales the <code><XMLContent></code> or + * <code><Base64Content></code> child of the given element to a list of + * infobox token. + * + * @param contentParent The elment including the <code><XMLContent></code> or + * <code><Base64Content></code> child to unmarshal the + * infobox token from. + * @param infoboxName The name of the infobox. + * @param key The key of an <code>AssocArrayData-Pair</code>. + * Maybe <code>null</code>. + * @param primary Specifies whether this token is the first (e.g. in an + * AssocArrayData) token. + * + * @return A infobox token list. + * + * @throws ParseException If the <code>contentParent</code> has no <code><XMLContent></code> + * or <code><Base64Content></code> child or the + * <code><XMLContent></code> is empty. + */ + public static List getTokenFromXMLOrBase64Content( + Element contentParent, + String infoboxName, + String key, + boolean primary) + throws ParseException + { + Vector tokenList = new Vector(); + // get the <XMLContent> or <Base64Content> + List content = DOMUtils.getChildElements(contentParent); + if (content.size() == 1) { + Element contentElem = (Element)content.get(0); + if (contentElem.getLocalName().equals("XMLContent")) { + List xmlContentChildren = DOMUtils.getChildElements(contentElem); + if (xmlContentChildren.size() == 0) { + throw new ParseException("parser.06", new Object[] {infoboxName, "Inhalt", "XMLContent"}); + } + int xmlCount = 0; + Iterator contentIt = xmlContentChildren.iterator(); + while (contentIt.hasNext()) { + Element xmlToken = (Element)contentIt.next(); + if (xmlCount > 0) { + primary = false; + } + InfoboxToken infoboxToken = new InfoboxTokenImpl(key, primary, xmlToken); + tokenList.add(infoboxToken); + xmlCount++; + } + } else { + String base64Token = contentElem.getFirstChild().getNodeValue(); + InfoboxToken infoboxToken = new InfoboxTokenImpl(key, primary, base64Token); + tokenList.add(infoboxToken); + } + } else { + throw new ParseException("parser.06", + new Object[] {infoboxName, "XMLContent oder Base64Content", contentParent.getLocalName()}); + } + return tokenList; + } + + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/IdentityLinkAssertionParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/IdentityLinkAssertionParser.java new file mode 100644 index 000000000..d8a57fd2f --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/IdentityLinkAssertionParser.java @@ -0,0 +1,319 @@ +package at.gv.egovernment.moa.id.auth.parser; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +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.ECDSAConverterException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.id.util.ECDSAKeyValueConverter; +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 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 = ""; + /** 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 */ + public static final String PERSON_IDENT_VALUE_XPATH = + PERSON_XPATH + + "/" + + PDATA + + "Identification/" + + PDATA + + "Value"; + + /** Xpath expression to the Identification Value element */ + private static final String PERSON_IDENT_TYPE_XPATH = + PERSON_XPATH + + "/" + + PDATA + + "Identification/" + + PDATA + + "Type"; + + /** 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 ECKeyValue element */ + private static final String ECDSA_KEY_VALUE_XPATH = + ROOT + + SAML + + "AttributeStatement/" + + SAML + + "Attribute/" + + SAML + + "AttributeValue/" + + ECDSA + + "ECDSAKeyValue"; + + + /** 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"; + + /** The IssueInstant attribute of the SAML assertion */ + private static final String ISSUE_INSTANT_ATTR = "IssueInstant"; + + /**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); + } + } + + /** + * Sets the <@link assertionElem>. + * @param xmlAssertion the assertion element + * @throws ParseException on any parsing error + */ + public IdentityLinkAssertionParser(Element xmlAssertion) throws ParseException { + assertionElem = xmlAssertion; + } + + /** + * 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(); + identityLink.setSamlAssertion(assertionElem); + identityLink.setIssueInstant(assertionElem.getAttribute(ISSUE_INSTANT_ATTR)); + identityLink.setPrPerson((Element) + XPathUtils.selectSingleNode(assertionElem, PERSON_XPATH)); + identityLink.setIdentificationValue( + XPathUtils.getElementValue(assertionElem, PERSON_IDENT_VALUE_XPATH, "")); + identityLink.setIdentificationType( + XPathUtils.getElementValue(assertionElem, PERSON_IDENT_TYPE_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, ECDSAConverterException{ + + + List pubKeys = new ArrayList(); + //Try to get RSA-Keys + NodeIterator rsaIter = + XPathUtils.selectNodeIterator(assertionElem, Constants.nSMap, 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); + } + + //Try to get ECDSA-Keys + NodeIterator ecdsaIter = + XPathUtils.selectNodeIterator(assertionElem, Constants.nSMap, ECDSA_KEY_VALUE_XPATH); + Element ecdsaElem; + PublicKey ecPubKey = null; + while ((ecdsaElem = (Element) ecdsaIter.nextNode()) != null) { + try { + ecPubKey = ECDSAKeyValueConverter.element2ECDSAPublicKey(ecdsaElem); + pubKeys.add(ecPubKey); + } + catch(Exception e) { + throw new ECDSAConverterException("parser.03", new Object[] { e.toString() }, e); + } + } + + 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/InfoboxReadResponseParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/InfoboxReadResponseParser.java new file mode 100644 index 000000000..e59c88ddc --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/InfoboxReadResponseParser.java @@ -0,0 +1,165 @@ +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 { + + /** This is the root element of the XML-Document provided by the Security Layer Card*/ + private Element infoBoxElem_; + + /** + * Parses and validates the document given as string and extracts the + * root element. + * + * @param xmlResponse <code><InfoboxReadResponse></code> as String + * @throws ParseException If an element cannot be parsed + * @throws AuthenticationException If any authentication error occurs + */ + public InfoboxReadResponseParser(String xmlResponse) throws ParseException, AuthenticationException { + + try { + InputStream s = new ByteArrayInputStream(xmlResponse.getBytes("UTF-8")); + init(s); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString()}, t); + } + } + + /** + * Parses and validates the document given as stream and extracts the + * root element. + * + * @param is <code><InfoboxReadResponse></code> as InputStream + * @throws ParseException If an element cannot be parsed + * @throws AuthenticationException If any authentication error occurs + */ + public InfoboxReadResponseParser(InputStream is) throws ParseException, AuthenticationException { + init(is); + } + + /** + * Initializes the parser. + * Parses and validates the document given as stream and extracts the + * root element. + * + * @param is The InfoBoxReadResponse as stream. + * @throws AuthenticationException If an authentication error occurs. + * @throws ParseException If an error occurs on parsing the the document. + */ + private void init(InputStream is) throws AuthenticationException, ParseException { + try { + + Element responseElem = DOMUtils.parseXmlValidating(is); + + if ("InfoboxReadResponse".equals(responseElem.getLocalName())) { + infoBoxElem_ = responseElem; + } else { + ErrorResponseParser erp = new ErrorResponseParser(responseElem); + throw new AuthenticationException("auth.08", new Object[] { erp.getErrorCode(), erp.getErrorInfo()}); + } + + } 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 { +// +// String slPrefix = XPathUtils.getSlPrefix(infoBoxElem_); +// StringBuffer sb = new StringBuffer("/"); +// sb.append(slPrefix); +// sb.append(":InfoboxReadResponse/"); +// sb.append(slPrefix); +// sb.append(":BinaryFileData/"); +// sb.append(slPrefix); +// sb.append(":XMLContent/"); +// sb.append(Constants.SAML_PREFIX); +// sb.append(":Assertion"); +// String samlAssertionXPath = sb.toString(); +// Element samlAssertion = (Element) XPathUtils.selectSingleNode(infoBoxElem_, samlAssertionXPath); +// return DOMUtils.serializeNode(samlAssertion); +// +// } +// 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 Element parseSAMLAssertion() throws ParseException { + try { + + String slPrefix = XPathUtils.getSlPrefix(infoBoxElem_); + StringBuffer sb = new StringBuffer("/"); + sb.append(slPrefix); + sb.append(":InfoboxReadResponse/"); + sb.append(slPrefix); + sb.append(":BinaryFileData/"); + sb.append(slPrefix); + sb.append(":XMLContent/"); + sb.append(Constants.SAML_PREFIX); + sb.append(":Assertion"); + String samlAssertionXPath = sb.toString(); + Element samlAssertion = (Element) XPathUtils.selectSingleNode(infoBoxElem_, samlAssertionXPath); + return 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(); +// } + + /** + * Parses the identity link from the <code><saml:Assertion></code> + * @return Identity link + * @throws ParseException on any parsing error + */ + public IdentityLink parseIdentityLink() throws ParseException { + Element samlAssertion = parseSAMLAssertion(); + IdentityLinkAssertionParser ilParser = new IdentityLinkAssertionParser(samlAssertion); + return ilParser.parseIdentityLink(); + } + + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/SAMLArtifactParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/SAMLArtifactParser.java new file mode 100644 index 000000000..7c4c01abe --- /dev/null +++ b/id/server/idserverlib/src/main/java/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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/VerifyXMLSignatureResponseParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/VerifyXMLSignatureResponseParser.java new file mode 100644 index 000000000..4c49afb76 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/parser/VerifyXMLSignatureResponseParser.java @@ -0,0 +1,156 @@ +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 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/" + + MOA + "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", new Object[] { t.toString() }, 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_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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/AuthServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/AuthServlet.java new file mode 100644 index 000000000..96e8e6dd6 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/AuthServlet.java @@ -0,0 +1,202 @@ +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.List; +import java.util.Map; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; + +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-auth.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(null != errorMessage) { + Logger.error(errorMessage); + req.setAttribute("ErrorMessage", errorMessage ); + } + + if (null != exceptionThrown) { + if(null == errorMessage) errorMessage = exceptionThrown.getMessage(); + Logger.error(errorMessage, exceptionThrown); + req.setAttribute("ExceptionThrown", exceptionThrown); + } + + //forward this to errorpage-auth.jsp where the HTML error page is generated + ServletContext context = getServletContext(); + RequestDispatcher dispatcher = context.getRequestDispatcher("/errorpage-auth.jsp"); + try { + dispatcher.forward(req, resp); + } catch (ServletException e) { + Logger.error(e); + } catch (IOException e) { + Logger.error(e); + } + + } + /** + * 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", ex.getMessage()); + + // forward this to errorpage-auth.jsp where the HTML error page is generated + ServletContext context = getServletContext(); + RequestDispatcher dispatcher = context.getRequestDispatcher("/errorpage-auth.jsp"); + try { + dispatcher.forward(req, resp); + } catch (ServletException e) { + Logger.error(e); + } catch (IOException e) { + Logger.error(e); + } + } + + /** + * Logs all servlet parameters for debugging purposes. + */ + protected void logParameters(HttpServletRequest req) { + for (Enumeration params = req.getParameterNames(); params.hasMoreElements(); ) { + String parname = (String)params.nextElement(); + Logger.debug("Parameter " + parname + req.getParameter(parname)); + } + } + + /** + * Parses the request input stream for parameters, assuming parameters are encoded UTF-8 + * (no standard exists how browsers should encode them). + * + * @param req servlet request + * + * @return mapping parameter name -> value + * + * @throws IOException if parsing request parameters fails. + * + * @throws FileUploadException if parsing request parameters fails. + */ + protected Map getParameters(HttpServletRequest req) + throws IOException, FileUploadException { + + Map parameters = new HashMap(); + + + if (ServletFileUpload.isMultipartContent(req)) + { + // request is encoded as mulitpart/form-data + FileItemFactory factory = new DiskFileItemFactory(); + ServletFileUpload upload = null; + upload = new ServletFileUpload(factory); + List items = null; + items = upload.parseRequest(req); + for (int i = 0; i < items.size(); i++) + { + FileItem item = (FileItem) items.get(i); + if (item.isFormField()) + { + // Process only form fields - no file upload items + parameters.put(item.getFieldName(), item.getString("UTF-8")); + Logger.debug("Processed multipart/form-data request parameter: \nName: " + + item.getFieldName() + "\nValue: " + + item.getString("UTF-8")); + } + } + } + + else + { + // request is encoded as application/x-www-urlencoded + 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(); + } + + /** + * Calls the web application initializer. + * + * @see javax.servlet.Servlet#init(ServletConfig) + */ + public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/ConfigurationServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/ConfigurationServlet.java new file mode 100644 index 000000000..f33377547 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/ConfigurationServlet.java @@ -0,0 +1,75 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; + +import javax.servlet.ServletConfig; +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.HTTPRequestJSPForwarder; +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 { + + /** + * 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(); + + try { + MOAIDAuthInitializer.initialized=false; + MOAIDAuthInitializer.initialize(); + String message = msg.getMessage("config.00", new Object[] + { DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.GERMAN).format(new Date())} ); + + Logger.info(message); + HTTPRequestJSPForwarder.forwardNamed(message, "/message-auth.jsp", getServletContext(), request, response); + + } catch (Throwable t) { + String errorMessage = msg.getMessage("config.04", null); + Logger.error(errorMessage, t); + HTTPRequestJSPForwarder.forwardNamed(errorMessage, "/message-auth.jsp", getServletContext(), request, response); + } + } + + /** + * 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); + } + + /** + * Calls the web application initializer. + * + * @see javax.servlet.Servlet#init(ServletConfig) + */ + public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + } + +} + + diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/GetAuthenticationDataService.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/GetAuthenticationDataService.java new file mode 100644 index 000000000..c41b514c8 --- /dev/null +++ b/id/server/idserverlib/src/main/java/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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/SelectBKUServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/SelectBKUServlet.java new file mode 100644 index 000000000..4dc69c70b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/SelectBKUServlet.java @@ -0,0 +1,99 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +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 { + super.init(servletConfig); + MOAIDAuthInitializer.initialize(); + Logger.debug("default platform file.encoding: " + System.getProperty("file.encoding")); + 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.setContentType("text/html"); + resp.sendRedirect(redirectURL); + Logger.info("REDIRECT TO: " + redirectURL); + } else { + // bkuSelectionType==HTMLSelect + String htmlForm = returnValue; + resp.setContentType("text/html;charset=UTF-8"); + Logger.debug("HTML-Form: " + htmlForm); + Writer out = new OutputStreamWriter(resp.getOutputStream(), "UTF8"); + out.write(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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/StartAuthenticationServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/StartAuthenticationServlet.java new file mode 100644 index 000000000..6098f5138 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/StartAuthenticationServlet.java @@ -0,0 +1,103 @@ +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;charset=UTF-8"); + 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 { + super.init(servletConfig); + 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/VerifyAuthenticationBlockServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/VerifyAuthenticationBlockServlet.java new file mode 100644 index 000000000..6ec4a247d --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/VerifyAuthenticationBlockServlet.java @@ -0,0 +1,120 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import at.gv.egovernment.moa.util.URLEncoder; //java.net.URLEncoder; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileUploadException; + +import at.gv.egovernment.moa.id.MOAIDException; +import at.gv.egovernment.moa.id.auth.AuthenticationServer; +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>(only if the online application is + * a public service) 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; + try + { + parameters = getParameters(req); + } catch (FileUploadException e) + { + Logger.error("Parsing mulitpart/form-data request parameters failed: " + e.getMessage()); + throw new IOException(e.getMessage()); + } + String sessionID = req.getParameter(PARAM_SESSIONID); + String createXMLSignatureResponse = (String)parameters.get(PARAM_XMLRESPONSE); + + try { + AuthenticationSession session = AuthenticationServer.getSession(sessionID); + String samlArtifactBase64 = + AuthenticationServer.getInstance().verifyAuthenticationBlock(sessionID, createXMLSignatureResponse); + String redirectURL = session.getOAURLRequested(); + if (!session.getBusinessService()) { + redirectURL = addURLParameter(redirectURL, PARAM_TARGET, URLEncoder.encode(session.getTarget(), "UTF-8")); + } + redirectURL = addURLParameter(redirectURL, PARAM_SAMLARTIFACT, URLEncoder.encode(samlArtifactBase64, "UTF-8")); + redirectURL = resp.encodeRedirectURL(redirectURL); + resp.setContentType("text/html"); + resp.setStatus(302); + resp.addHeader("Location", redirectURL); + Logger.debug("REDIRECT TO: " + redirectURL); + } + + 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/VerifyIdentityLinkServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/VerifyIdentityLinkServlet.java new file mode 100644 index 000000000..2134c1444 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/servlet/VerifyIdentityLinkServlet.java @@ -0,0 +1,107 @@ +package at.gv.egovernment.moa.id.auth.servlet; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.commons.fileupload.FileUploadException; + +import at.gv.egovernment.moa.id.MOAIDException; +import at.gv.egovernment.moa.id.auth.AuthenticationServer; +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; + try + { + parameters = getParameters(req); + } catch (FileUploadException e) + { + Logger.error("Parsing mulitpart/form-data request parameters failed: " + e.getMessage()); + throw new IOException(e.getMessage()); + } + String sessionID = req.getParameter(PARAM_SESSIONID); + + + try { + AuthenticationSession session = AuthenticationServer.getSession(sessionID); + + String createXMLSignatureRequest = + AuthenticationServer.getInstance().verifyIdentityLink(sessionID, parameters); + resp.setStatus(307); + String dataURL = new DataURLBuilder().buildDataURL( + session.getAuthURL(), AuthenticationServer.REQ_VERIFY_AUTH_BLOCK, sessionID); + resp.addHeader("Location", dataURL); + + //TODO test impact of explicit setting charset with older versions of BKUs (HotSign) + resp.setContentType("text/xml;charset=UTF-8"); + + OutputStream out = resp.getOutputStream(); + out.write(createXMLSignatureRequest.getBytes("UTF-8")); + out.flush(); + out.close(); + Logger.debug("Finished POST VerifyIdentityLink"); + } + catch (MOAIDException ex) { + handleError(null, ex, req, resp); + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/CreateXMLSignatureResponseValidator.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/CreateXMLSignatureResponseValidator.java new file mode 100644 index 000000000..e6c9f4bee --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/CreateXMLSignatureResponseValidator.java @@ -0,0 +1,274 @@ +package at.gv.egovernment.moa.id.auth.validator; + +import java.util.Iterator; +import java.util.List; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.auth.builder.AuthenticationBlockAssertionBuilder; +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.id.auth.data.CreateXMLSignatureResponse; +import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.id.auth.data.SAMLAttribute; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.StringUtils; +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 expression to the dsig:Signature element */ + private static final String SIGNATURE_XPATH = Constants.DSIG_PREFIX + ":Signature"; + + + /** 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 session + * @throws ValidateException + */ + public void validate(CreateXMLSignatureResponse createXMLSignatureResponse, AuthenticationSession session) + throws ValidateException { + + // A3.056: more then one /saml:Assertion/saml:AttributeStatement/saml:Subject/saml:NameIdentifier + + String gbTarget = session.getTarget(); + String oaURL = session.getPublicOAURLPrefix(); + boolean businessService = session.getBusinessService(); + + IdentityLink identityLink = session.getIdentityLink(); + + Element samlAssertion = createXMLSignatureResponse.getSamlAssertion(); + String issuer = samlAssertion.getAttribute("Issuer"); + if (issuer == null) { + // should not happen, because parser would dedect this + throw new ValidateException("validator.32", null); + } + String issueInstant = samlAssertion.getAttribute("IssueInstant"); + if (!issueInstant.equals(session.getIssueInstant())) { + throw new ValidateException("validator.39", new Object[] {issueInstant, session.getIssueInstant()}); + } + + String name = identityLink.getName(); + if (!issuer.equals(name)) { + throw new ValidateException("validator.33", new Object[] {issuer, name}); + } + + SAMLAttribute[] samlAttributes = createXMLSignatureResponse.getSamlAttributes(); + + boolean foundOA = false; + boolean foundGB = false; + boolean foundWBPK = false; + int offset = 0; + + // check number of SAML aatributes + List extendedSAMLAttributes = session.getExtendedSAMLAttributesAUTH(); + int extendedSAMLAttributesNum = 0; + if (extendedSAMLAttributes != null) { + extendedSAMLAttributesNum = extendedSAMLAttributes.size(); + } + int expectedSAMLAttributeNumber = + AuthenticationBlockAssertionBuilder.NUM_OF_SAML_ATTRIBUTES + extendedSAMLAttributesNum; + if (!session.getSAMLAttributeGebeORwbpk()) expectedSAMLAttributeNumber--; + int actualSAMLAttributeNumber = samlAttributes.length; + if (actualSAMLAttributeNumber != expectedSAMLAttributeNumber) { + Logger.error("Wrong number of SAML attributes in CreateXMLSignatureResponse: expected " + + expectedSAMLAttributeNumber + ", but was " + actualSAMLAttributeNumber); + throw new ValidateException( + "validator.36", + new Object[] {String.valueOf(actualSAMLAttributeNumber), String.valueOf(expectedSAMLAttributeNumber)}); + } + + SAMLAttribute samlAttribute; + if (session.getSAMLAttributeGebeORwbpk()) { + // check the first attribute ("Geschaeftsbereich" or "wbPK") + samlAttribute = samlAttributes[0]; + if (businessService) { + if (!samlAttribute.getName().equals("wbPK")) { + if (samlAttribute.getName().equals("Geschaeftsbereich")) { + throw new ValidateException("validator.26", null); + } else { + throw new ValidateException( + "validator.37", + new Object[] {samlAttribute.getName(), "wbPK", String.valueOf(1)}); + } + } + if (samlAttribute.getNamespace().equals("http://reference.e-government.gv.at/namespace/moa/20020822#")) { + foundWBPK = true; + try { + Element attrValue = (Element)samlAttribute.getValue(); + String value = ((Element)attrValue.getElementsByTagNameNS(Constants.PD_NS_URI, "Value").item(0)).getFirstChild().getNodeValue(); + String type = ((Element)attrValue.getElementsByTagNameNS(Constants.PD_NS_URI, "Type").item(0)).getFirstChild().getNodeValue(); + if (!value.equals(identityLink.getIdentificationValue())) { + throw new ValidateException("validator.28", null); + } + if (!type.equals(identityLink.getIdentificationType())) { + throw new ValidateException("validator.28", null); + } + } catch (Exception ex) { + throw new ValidateException("validator.29", null); + } + } else { + throw new ValidateException("validator.30", null); + } + } else { + if (!samlAttribute.getName().equals("Geschaeftsbereich")) { + if (samlAttribute.getName().equals("wbPK")) { + throw new ValidateException("validator.26", null); + } else { + throw new ValidateException( + "validator.37", + new Object[] {samlAttribute.getName(), "Geschaeftsbereich", String.valueOf(1)}); + } + } + if (samlAttribute.getNamespace().equals("http://reference.e-government.gv.at/namespace/moa/20020822#")) { + foundGB = true; + if (!gbTarget.equals((String)samlAttribute.getValue())) { + throw new ValidateException("validator.13", null); + } + } else { + throw new ValidateException("validator.12", null); + } + } + } else { + offset--; + } + + // check the second attribute (must be "OA") + samlAttribute = samlAttributes[1 + offset]; + if (!samlAttribute.getName().equals("OA")) { + throw new ValidateException( + "validator.37", + new Object[] {samlAttribute.getName(), "OA", String.valueOf(2)}); + } + if (samlAttribute.getNamespace().equals("http://reference.e-government.gv.at/namespace/moa/20020822#")) { + foundOA = true; + if (!oaURL.equals((String)samlAttribute.getValue())) { // CHECKS für die AttributeVALUES fehlen noch + throw new ValidateException("validator.16", new Object[] {":gefunden wurde '" + oaURL + "', erwartet wurde '" + samlAttribute.getValue()}); + } + } else { + throw new ValidateException("validator.15", null); + } + + // check the third attribute (must be "Geburtsdatum") + samlAttribute = samlAttributes[2 + offset]; + if (!samlAttribute.getName().equals("Geburtsdatum")) { + throw new ValidateException( + "validator.37", + new Object[] {samlAttribute.getName(), "Geburtsdatum", String.valueOf(3)}); + } + if (samlAttribute.getNamespace().equals("http://reference.e-government.gv.at/namespace/moa/20020822#")) { + String samlDateOfBirth = (String)samlAttribute.getValue(); + String dateOfBirth = identityLink.getDateOfBirth(); + if (!samlDateOfBirth.equals(dateOfBirth)) { + throw new ValidateException("validator.34", new Object[] {samlDateOfBirth, dateOfBirth}); + } + } else { + throw new ValidateException("validator.35", null); + } + + // now check the extended SAML attributes + int i = AuthenticationBlockAssertionBuilder.NUM_OF_SAML_ATTRIBUTES + offset; + if (extendedSAMLAttributes != null) { + Iterator it = extendedSAMLAttributes.iterator(); + while (it.hasNext()) { + ExtendedSAMLAttribute extendedSAMLAttribute = (ExtendedSAMLAttribute)it.next(); + samlAttribute = samlAttributes[i]; + String actualName = samlAttribute.getName(); + String expectedName = extendedSAMLAttribute.getName(); + if (!actualName.equals(expectedName)) { + throw new ValidateException( + "validator.38", + new Object[] {"Name", String.valueOf((i+1)), actualName, actualName, expectedName }); + } + String actualNamespace = samlAttribute.getNamespace(); + String expectedNamespace = extendedSAMLAttribute.getNameSpace(); + if (!actualNamespace.equals(expectedNamespace)) { + throw new ValidateException( + "validator.38", + new Object[] {"Namespace", String.valueOf((i+1)), actualName, actualNamespace, expectedNamespace, }); + } + Object expectedValue = extendedSAMLAttribute.getValue(); + Object actualValue = samlAttribute.getValue(); + try { + if (expectedValue instanceof String) { + // replace \r\n because text might be base64-encoded + String expValue = StringUtils.replaceAll((String)expectedValue,"\r",""); + expValue = StringUtils.replaceAll(expValue,"\n",""); + String actValue = StringUtils.replaceAll((String)actualValue,"\r",""); + actValue = StringUtils.replaceAll(actValue,"\n",""); + if (!expValue.equals(actValue)) { + throw new ValidateException( + "validator.38", + new Object[] {"Wert", String.valueOf((i+1)), actualName, actualValue, expectedValue }); + } + } else if (expectedValue instanceof Element) { + // only check the name of the element + String actualElementName = ((Element)actualValue).getNodeName(); + String expectedElementName = ((Element)expectedValue).getNodeName(); + if (!(expectedElementName.equals(actualElementName))){ + throw new ValidateException( + "validator.38", + new Object[] {"Wert", String.valueOf((i+1)), actualName, actualElementName, expectedElementName}); + } + } else { + // should not happen + throw new ValidateException( + "validator.38", + new Object[] {"Typ", String.valueOf((i+1)), expectedName, "java.lang.String oder org.wrc.dom.Element", expectedValue.getClass().getName()}); + } + } catch (ClassCastException e) { + throw new ValidateException( + "validator.38", + new Object[] {"Typ", String.valueOf((i+1)), expectedName, expectedValue.getClass().getName(), actualValue.getClass().getName()}); + } + i++; + } + } + + + if (!foundOA) throw new ValidateException("validator.14", null); + if (businessService) { + if (session.getSAMLAttributeGebeORwbpk() && !foundWBPK) throw new ValidateException("validator.31", null); + } else { + if (!foundGB) throw new ValidateException("validator.11", null); + } + + //Check if dsig:Signature exists +// NodeList nl = createXMLSignatureResponse.getSamlAssertion().getElementsByTagNameNS(Constants.DSIG_NS_URI, "Signature"); +// if (nl.getLength() != 1) { +// throw new ValidateException("validator.05", null); +// } + Element dsigSignature = (Element) XPathUtils.selectSingleNode(samlAssertion, SIGNATURE_XPATH); + if (dsigSignature == null) { + throw new ValidateException("validator.05", new Object[] {"im AUTHBlock"}) ; + } + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/IdentityLinkValidator.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/IdentityLinkValidator.java new file mode 100644 index 000000000..444f706e4 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/IdentityLinkValidator.java @@ -0,0 +1,159 @@ +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 = ""; + /** 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 { + + Element samlAssertion = identityLink.getSamlAssertion(); + //Search the SAML:ASSERTION Object (A2.054) + if (samlAssertion == 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(samlAssertion, 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(samlAssertion, 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#") || + attributeNS.equals("urn:publicid:gv.at:namespaces:identitylink:1.2")) { + Element attributeValue = + (Element) XPathUtils.selectSingleNode((Element) nl.item(i),nSMap, SAML + "AttributeValue/" + DSIG + "RSAKeyValue"); + if (attributeValue==null) + attributeValue = + (Element) XPathUtils.selectSingleNode((Element)nl.item(i), nSMap, SAML + "AttributeValue/" + ECDSA + "ECDSAKeyValue"); + if (attributeValue == null) + throw new ValidateException("validator.02", null); + + } + else + throw new ValidateException("validator.03", new Object [] {attributeNS} ); + } + else + throw new ValidateException("validator.04", new Object [] {attributeName} ); + } + + //Check if dsig:Signature exists + Element dsigSignature = (Element) XPathUtils.selectSingleNode(samlAssertion,ROOT + DSIG + "Signature"); + if (dsigSignature==null) throw new ValidateException("validator.05", new Object[] {"in der Personenbindung"}); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/InfoboxValidator.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/InfoboxValidator.java new file mode 100644 index 000000000..c776418ab --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/InfoboxValidator.java @@ -0,0 +1,32 @@ +package at.gv.egovernment.moa.id.auth.validator; + +import at.gv.egovernment.moa.id.auth.data.InfoboxValidationResult; +import at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams; + +/** + * Validates an InfoboxReadResponse. + * An implementing class has to validate the content of the InfoboxReadResponse + * according to the type specific rules and guidelines of the underlying + * application. + */ +public interface InfoboxValidator { + + /** + * This method validates an InfoboxReadResponse. + * The method validates the content of the passed <code>infoboxReadResponse</code> + * according to the type specific rules and guidelines of the underlying + * application. + * + * @param params {@link at.gv.egovernment.moa.id.auth.data.InfoboxValidatorParams + * Parameters} needed by the validator. + * + * @return <code>True</code> if validation succeeds, + * otherwise <code>false</code>. + * + * @throws ValidateException If an error occurs on validating the + * InfoboxReadResponse. + */ + public InfoboxValidationResult validate (InfoboxValidatorParams params) + throws ValidateException; + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/ValidateException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/ValidateException.java new file mode 100644 index 000000000..a6685fca8 --- /dev/null +++ b/id/server/idserverlib/src/main/java/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/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/VerifyXMLSignatureResponseValidator.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/VerifyXMLSignatureResponseValidator.java new file mode 100644 index 000000000..1f2ebc37c --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/validator/VerifyXMLSignatureResponseValidator.java @@ -0,0 +1,177 @@ +package at.gv.egovernment.moa.id.auth.validator; + +import iaik.asn1.structures.Name; +import iaik.security.ecc.ecdsa.ECPublicKey; +import iaik.utils.RFC2253NameParserException; +import iaik.x509.X509Certificate; +import iaik.x509.X509ExtensionInitException; + +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.List; + +import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; +import at.gv.egovernment.moa.id.auth.data.IdentityLink; +import at.gv.egovernment.moa.id.auth.data.VerifyXMLSignatureResponse; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; + +/** + * 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 + * @param ignoreManifestValidationResult specifies whether the validation result of the + * manifest has to be ignored (identityLink validation if + * the OA is a business service) or not + * @throws ValidateException on any validation error + */ + public void validate(VerifyXMLSignatureResponse verifyXMLSignatureResponse, + List identityLinkSignersSubjectDNNames, + String whatToCheck, + boolean ignoreManifestValidationResult) + throws ValidateException { + + if (verifyXMLSignatureResponse.getSignatureCheckCode() != 0) + throw new ValidateException("validator.06", null); + + if (verifyXMLSignatureResponse.getCertificateCheckCode() != 0) { + String checkFailedReason =""; + if (verifyXMLSignatureResponse.getCertificateCheckCode() == 1) + checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.21", null); + if (verifyXMLSignatureResponse.getCertificateCheckCode() == 2) + checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.22", null); + if (verifyXMLSignatureResponse.getCertificateCheckCode() == 3) + checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.23", null); + if (verifyXMLSignatureResponse.getCertificateCheckCode() == 4) + checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.24", null); + if (verifyXMLSignatureResponse.getCertificateCheckCode() == 5) + checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.25", null); + + if (whatToCheck.equals(CHECK_IDENTITY_LINK)) + throw new ValidateException("validator.07", new Object[] { checkFailedReason } ); + else + throw new ValidateException("validator.19", new Object[] { checkFailedReason } ); + } + + if (ignoreManifestValidationResult) { + Logger.debug("OA type is business service, thus ignoring DSIG manifest validation result"); + } else { + if (verifyXMLSignatureResponse.isXmlDSIGManigest()) + if (verifyXMLSignatureResponse.getXmlDSIGManifestCheckCode() != 0) + throw new ValidateException("validator.08", null); + } + + + // TODO See Bug #322 + // Check result of SignatureManifestCheck + + + //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); + } + // check the authorisation to sign the identity link + if (!identityLinkSignersSubjectDNNames.contains(subjectDN)) { + // subject DN check failed, try OID check: + try { + if (x509Cert.getExtension(MOAIDAuthConstants.IDENTITY_LINK_SIGNER_OID) == null) { + throw new ValidateException("validator.18", new Object[] { subjectDN }); + } else { + Logger.debug("Identity link signer cert accepted for signing identity link: " + + "subjectDN check failed, but OID check successfully passed."); + } + } catch (X509ExtensionInitException e) { + throw new ValidateException("validator.49", null); + } + } else { + Logger.debug("Identity link signer cert accepted for signing identity link: " + + "subjectDN check successfully passed."); + } + + } + } + + /** + * Method validateCertificate. + * @param verifyXMLSignatureResponse The VerifyXMLSignatureResponse + * @param idl The Identitylink + * @throws ValidateException + */ + public void validateCertificate( + VerifyXMLSignatureResponse verifyXMLSignatureResponse, + IdentityLink idl) + throws ValidateException { + + X509Certificate x509Response = verifyXMLSignatureResponse.getX509certificate(); + PublicKey[] pubKeysIdentityLink = (PublicKey[]) idl.getPublicKey(); + + PublicKey pubKeySignature = x509Response.getPublicKey(); + + boolean found = false; + for (int i = 0; i < pubKeysIdentityLink.length; i++) { + + //compare RSAPublicKeys + if ((idl.getPublicKey()[i] instanceof java.security.interfaces.RSAPublicKey) && + (pubKeySignature instanceof java.security.interfaces.RSAPublicKey)) { + + RSAPublicKey rsaPubKeySignature = (RSAPublicKey) pubKeySignature; + RSAPublicKey rsakey = (RSAPublicKey) pubKeysIdentityLink[i]; + + if (rsakey.getModulus().equals(rsaPubKeySignature.getModulus()) + && rsakey.getPublicExponent().equals(rsaPubKeySignature.getPublicExponent())) + found = true; + } + + //compare ECDSAPublicKeys + if((idl.getPublicKey()[i] instanceof iaik.security.ecc.ecdsa.ECPublicKey) && + (pubKeySignature instanceof iaik.security.ecc.ecdsa.ECPublicKey)) { + + ECPublicKey ecdsaPubKeySignature = (ECPublicKey) pubKeySignature; + ECPublicKey ecdsakey = (ECPublicKey) pubKeysIdentityLink[i]; + + if(ecdsakey.equals(ecdsaPubKeySignature)) + found = true; + } + } + + if (!found) + throw new ValidateException("validator.09", null); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationBuilder.java new file mode 100644 index 000000000..90b780526 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationBuilder.java @@ -0,0 +1,894 @@ +package at.gv.egovernment.moa.id.config; + +import iaik.pki.pathvalidation.ChainingModes; +import iaik.utils.RFC2253NameParser; +import iaik.utils.RFC2253NameParserException; + +import java.math.BigInteger; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.traversal.NodeIterator; + +import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; +import at.gv.egovernment.moa.id.auth.data.Schema; +import at.gv.egovernment.moa.id.auth.data.SchemaImpl; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; +import at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameter; +import at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameters; +import at.gv.egovernment.moa.id.data.IssuerAndSerial; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.BoolUtils; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.FileUtils; +import at.gv.egovernment.moa.util.StringUtils; +import at.gv.egovernment.moa.util.XPathException; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * A class that builds configuration data from a DOM based representation. + * + * @author Patrick Peck + * @author Stefan Knirsch + * @version $Id$ + */ +public class ConfigurationBuilder { + + // + // XPath namespace prefix shortcuts + // + /** an XPATH-Expression */ + protected static final String CONF = Constants.MOA_ID_CONFIG_PREFIX + ":"; + /** an XPATH-Expression */ + protected static final String DSIG = Constants.DSIG_PREFIX + ":"; + + // + // chaining mode constants appearing in the configuration file + // + /** an XPATH-Expression */ + protected static final String CM_CHAINING = "chaining"; + /** an XPATH-Expression */ + protected static final String CM_PKIX = "pkix"; + /** an XPATH-Expression */ + protected static final String DEFAULT_ENCODING = "UTF-8"; + + // + // XPath expressions to select certain parts of the configuration + // + /** an XPATH-Expression */ + protected static final String ROOT = "/" + CONF + "MOA-IDConfiguration/"; + + /** an XPATH-Expression */ + protected static final String AUTH_BKU_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "BKUSelection"; + /** an XPATH-Expression */ + protected static final String AUTH_BKUSELECT_TEMPLATE_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "Templates/" + CONF + "BKUSelectionTemplate/@URL"; + /** an XPATH-Expression */ + protected static final String AUTH_TEMPLATE_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "Templates/" + CONF + "Template/@URL"; + /** an XPATH-Expression */ + public static final String AUTH_SECLAYER_TRANSFORMS_INFO_FILENAME_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "SecurityLayer/" + CONF + "TransformsInfo/@filename"; + /** an XPATH-Expression */ + protected static final String AUTH_MOA_SP_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "MOA-SP"; + /** an XPATH-Expression */ + protected static final String AUTH_MOA_SP_VERIFY_IDENTITY_TRUST_ID_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "MOA-SP/" + CONF + "VerifyIdentityLink/" + CONF + "TrustProfileID"; + /** an XPATH-Expression */ + protected static final String AUTH_MOA_SP_VERIFY_AUTH_TRUST_ID_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "MOA-SP/" + CONF + "VerifyAuthBlock/" + CONF + "TrustProfileID"; + /** an XPATH-Expression */ + protected static final String AUTH_MOA_SP_VERIFY_AUTH_VERIFY_ID_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "MOA-SP/" + CONF + "VerifyAuthBlock/" + CONF + "VerifyTransformsInfoProfileID"; + + /** an XPATH-Expression */ + protected static final String AUTH_IDENTITY_LINK_X509SUBJECTNAME_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "IdentityLinkSigners/" + CONF + "X509SubjectName"; + + /** an XPATH-Expression */ + public static final String AUTH_VERIFY_INFOBOXES_XPATH = + ROOT + CONF + "AuthComponent/" + CONF + "VerifyInfoboxes"; + + /** an XPATH-Expression */ + protected static final String OA_XPATH = ROOT + CONF + "OnlineApplication"; + /** an XPATH-Expression */ + protected static final String OA_LOGIN_XPATH = ROOT + CONF + "OnlineApplication/@loginURL"; + /** an XPATH-Expression */ + protected static final String OA_AUTH_COMPONENT_XPATH = CONF + "AuthComponent"; + /** an XPATH-Expression */ + protected static final String OA_AUTH_COMPONENT_IDENT_NUMBER_XPATH = CONF + "IdentificationNumber"; + /** an XPATH-Expression */ + protected static final String OA_AUTH_COMPONENT_BKUSELECT_TEMPLATE_XPATH = + CONF + "Templates/" + CONF + "BKUSelectionTemplate/@URL"; + /** an XPATH-Expression */ + protected static final String OA_AUTH_COMPONENT_TEMPLATE_XPATH = + CONF + "Templates/" + CONF + "Template/@URL"; + /** an XPATH-Expression */ + protected static final String OA_AUTH_COMPONENT_TRANSFORMS_INFO_FILENAME_XPATH = CONF + "TransformsInfo/@filename"; + /** an XPATH-Expression */ + protected static final String OA_AUTH_COMPONENT_VERIFY_INFOBOXES_XPATH = CONF + "VerifyInfoboxes"; + /** an XPATH-Expression */ + protected static final String CONNECTION_PARAMETER_URL_XPATH = + CONF + "ConnectionParameter/@URL"; + /** an XPATH-Expression */ + protected static final String CONNECTION_PARAMETER_ACCEPTED_CERTS_XPATH = + CONF + "ConnectionParameter/" + CONF + "AcceptedServerCertificates"; + /** an XPATH-Expression */ + protected static final String CONNECTION_PARAMETERN_KEYSTORE_XPATH = + CONF + "ConnectionParameter/" + CONF + "ClientKeyStore"; + /** an XPATH-Expression */ + protected static final String CONNECTION_PARAMETER_KEYSTORE_PASS_XPATH = + CONNECTION_PARAMETERN_KEYSTORE_XPATH + "/@password"; + /** an XPATH-Expression */ + protected static final String GENERIC_CONFIGURATION_XPATH = + ROOT + CONF + "GenericConfiguration"; + + /** an XPATH-Expression */ + protected static final String CHAINING_MODES_XPATH = + ROOT + CONF + "ChainingModes"; + /** an XPATH-Expression */ + protected static final String CHAINING_MODES_DEFAULT_XPATH = + CHAINING_MODES_XPATH + "/@systemDefaultMode"; + /** an XPATH-Expression */ + protected static final String TRUST_ANCHOR_XPATH = + ROOT + CONF + "ChainingModes/" + CONF + "TrustAnchor"; + /** an XPATH-Expression */ + protected static final String ISSUER_XPATH = DSIG + "X509IssuerName"; + /** an XPATH-Expression */ + protected static final String SERIAL_XPATH = DSIG + "X509SerialNumber"; + /** an XPATH-Expression */ + protected static final String TRUSTED_CA_CERTIFICATES_XPATH = + ROOT + CONF + "TrustedCACertificates"; + + /** an XPATH-Expression */ + protected static final String VERIFY_INFOBOXES_DEFAULT_TRUST_PROFILE_XPATH = CONF + "DefaultTrustProfile"; + /** an XPATH-Expression */ + protected static final String VERIFY_INFOBOXES_TRUST_PROFILE_ID_XPATH = CONF + "TrustProfileID"; + /** an XPATH-Expression */ + protected static final String VERIFY_INFOBOXES_INFOBOX_XPATH = CONF + "Infobox"; + + + + + /** + * main configuration file directory name used to configure MOA-ID + */ + protected String rootConfigFileDir_; + + /** The root element of the MOA-ID configuration */ + protected Element configElem_; + + /** + * Creates a new <code>MOAConfigurationProvider</code>. + * + * @param configElem The root element of the MOA-ID configuration. + */ + public ConfigurationBuilder(Element configElem, String rootConfigDir) { + configElem_ = configElem; + rootConfigFileDir_ = rootConfigDir; + } + + /** + * Returns the root element of the MOA-ID configuration. + * + * @return The root element of the MOA-ID configuration. + */ + public Element getConfigElem() { + return configElem_; + } + + /** + * Build a ConnectionParameter object containing all information + * of the moa-sp element in the authentication component + * @return ConnectionParameter of the authentication component moa-sp element + */ + public ConnectionParameter buildAuthBKUConnectionParameter() { + + Element authBKU = (Element) XPathUtils.selectSingleNode(configElem_, AUTH_BKU_XPATH); + if (authBKU==null) return null; + return buildConnectionParameter(authBKU); + } + + /** + * Method buildAuthBKUSelectionType. + * + * Build a string with the configuration value of BKUSelectionAlternative + * + * @return String + */ + public String buildAuthBKUSelectionType() { + + Element authBKU = (Element) XPathUtils.selectSingleNode(configElem_, AUTH_BKU_XPATH); + if (authBKU==null) return null; + return (authBKU).getAttribute("BKUSelectionAlternative"); + } + + /** + * Build a string array with all filenames leading + * to the Transforms Information for the Security Layer + * @param contextNode The node from which should be searched + * @param xpathExpr The XPATH expression for the search + * @return String[] of filenames to the Security Layer Transforms Information + * or <code>null</code> if no transforms are included + */ + public String[] buildTransformsInfoFileNames(Node contextNode, String xpathExpr) { + + List transformsInfoFileNames = new ArrayList(); + + try { + NodeIterator tiIter = XPathUtils.selectNodeIterator(contextNode, xpathExpr); + + Attr tiElem; + while ((tiElem = (Attr) tiIter.nextNode()) != null) { + String tiFileName = tiElem.getNodeValue(); + transformsInfoFileNames.add(tiFileName); + } + + String[] result = new String[transformsInfoFileNames.size()]; + transformsInfoFileNames.toArray(result); + + return result; + } catch (XPathException xpe) { + return new String[0]; + } + } + + + /** + * Loads the <code>transformsInfos</code> from files. + * @throws Exception on any exception thrown + */ + public String[] loadTransformsInfos(String[] transformsInfoFileNames) throws Exception { + + String[] transformsInfos = new String[transformsInfoFileNames.length]; + for (int i = 0; i < transformsInfoFileNames.length; i++) { + String fileURL = transformsInfoFileNames[i]; + + //if fileURL is relative to rootConfigFileDir make it absolute + fileURL = FileUtils.makeAbsoluteURL(fileURL, rootConfigFileDir_); + String transformsInfo = FileUtils.readURL(fileURL, DEFAULT_ENCODING); + transformsInfos[i] = transformsInfo; + } + return transformsInfos; + } + + /** + * Build a ConnectionParameter bean containing all information + * of the authentication component moa-sp element + * @return ConnectionParameter of the authentication component moa-sp element + */ + public ConnectionParameter buildMoaSpConnectionParameter() { + + Element connectionParameter = (Element) XPathUtils.selectSingleNode(configElem_, AUTH_MOA_SP_XPATH); + if (connectionParameter==null) return null; + return buildConnectionParameter(connectionParameter); + } + + /** + * Return a string with a url-reference to the VerifyIdentityLink trust + * profile id within the moa-sp part of the authentication component + * @return String with a url-reference to the VerifyIdentityLink trust profile ID + */ + public String getMoaSpIdentityLinkTrustProfileID() { + return XPathUtils.getElementValue( + configElem_, + AUTH_MOA_SP_VERIFY_IDENTITY_TRUST_ID_XPATH, + ""); + } + /** + * Return a string representation of an URL pointing to trusted CA Certificates + * @return String representation of an URL pointing to trusted CA Certificates + */ + public String getTrustedCACertificates() { + return XPathUtils.getElementValue( + configElem_, + TRUSTED_CA_CERTIFICATES_XPATH,null); + } + + /** + * Return a string with a url-reference to the VerifyAuthBlock trust + * profile id within the moa-sp part of the authentication component + * @return String with a url-reference to the VerifyAuthBlock trust profile ID + */ + public String getMoaSpAuthBlockTrustProfileID() { + return XPathUtils.getElementValue( + configElem_, + AUTH_MOA_SP_VERIFY_AUTH_TRUST_ID_XPATH, + ""); + } + /** + * Build a string array with references to all verify transform info + * IDs within the moa-sp part of the authentication component + * @return A string array containing all urls to the + * verify transform info IDs + */ + public String[] buildMoaSpAuthBlockVerifyTransformsInfoIDs() { + + List verifyTransformsInfoIDs = new ArrayList(); + NodeIterator vtIter = + XPathUtils.selectNodeIterator( + configElem_, + AUTH_MOA_SP_VERIFY_AUTH_VERIFY_ID_XPATH); + Element vtElem; + + while ((vtElem = (Element) vtIter.nextNode()) != null) { + + String vtInfoIDs = DOMUtils.getText(vtElem); + verifyTransformsInfoIDs.add(vtInfoIDs); + } + String[] result = new String[verifyTransformsInfoIDs.size()]; + verifyTransformsInfoIDs.toArray(result); + + return result; + } + + + /** + * Returns a list containing all X509 Subject Names + * of the Identity Link Signers + * @return a list containing the configured identity-link signer X509 subject names + */ + public List getIdentityLink_X509SubjectNames() { + + Vector x509SubjectNameList = new Vector(); + NodeIterator x509Iter = + XPathUtils.selectNodeIterator( + configElem_, + AUTH_IDENTITY_LINK_X509SUBJECTNAME_XPATH); + Element x509Elem; + + while ((x509Elem = (Element) x509Iter.nextNode()) != null) { + String vtInfoIDs = DOMUtils.getText(x509Elem); + x509SubjectNameList.add(vtInfoIDs); + } + + // now add the default identity link signers + String[] identityLinkSignersWithoutOID = MOAIDAuthConstants.IDENTITY_LINK_SIGNERS_WITHOUT_OID; + for (int i=0; i<identityLinkSignersWithoutOID.length; i++) { + String identityLinkSigner = identityLinkSignersWithoutOID[i]; + if (!x509SubjectNameList.contains(identityLinkSigner)) { + x509SubjectNameList.add(identityLinkSigner); + } + } + + return x509SubjectNameList; + } + + /** + * Build an array of the OnlineApplication Parameters containing information + * about the authentication component + * + * @param defaultVerifyInfoboxParameters Default parameters for verifying additional + * infoboxes. Maybe <code>null</code>. + * @param moaSpIdentityLinkTrustProfileID The ID of the trust profile used for validating + * the identity link signer certificate. Needed for + * checking if this ID is not used for validating other + * infoboxes. + * + * @return An OAProxyParameter array containing beans + * with all relevant information for the authentication component of the online + * application + */ + public OAAuthParameter[] buildOnlineApplicationAuthParameters( + VerifyInfoboxParameters defaultVerifyInfoboxParameters, String moaSpIdentityLinkTrustProfileID) + throws ConfigurationException + { + + String bkuSelectionTemplateURL = + XPathUtils.getAttributeValue(configElem_, AUTH_BKUSELECT_TEMPLATE_XPATH, null); + String templateURL = + XPathUtils.getAttributeValue(configElem_, AUTH_TEMPLATE_XPATH, null); + + List OA_set = new ArrayList(); + NodeList OAIter = XPathUtils.selectNodeList(configElem_, OA_XPATH); + + for (int i = 0; i < OAIter.getLength(); i++) { + Element oAElem = (Element) OAIter.item(i); + Element authComponent = + (Element) XPathUtils.selectSingleNode(oAElem, OA_AUTH_COMPONENT_XPATH); + + OAAuthParameter oap = new OAAuthParameter(); + String publicURLPrefix = oAElem.getAttribute("publicURLPrefix"); + oap.setPublicURLPrefix(publicURLPrefix); + oap.setKeyBoxIdentier(oAElem.getAttribute("keyBoxIdentifier")); + + // get the type of the online application + String oaType = oAElem.getAttribute("type"); + oap.setOaType(oaType); + String slVersion = "1.1"; + if ("businessService".equalsIgnoreCase(oaType)) { + if (authComponent==null) { + Logger.error("Missing \"AuthComponent\" for OA of type \"businessService\""); + throw new ConfigurationException("config.02", null); + } + Element identificationNumberElem = + (Element) XPathUtils.selectSingleNode(authComponent, OA_AUTH_COMPONENT_IDENT_NUMBER_XPATH); + if (identificationNumberElem==null) { + Logger.error("Missing \"IdentificationNumber\" for OA of type \"businessService\""); + throw new ConfigurationException("config.02", null); + } + Element identificationNumberChild = DOMUtils.getElementFromNodeList(identificationNumberElem.getChildNodes()); + if (identificationNumberChild == null) { + Logger.error("Missing \"IdentificationNumber\" for OA of type \"businessService\""); + throw new ConfigurationException("config.02", null); + } + if ("false".equalsIgnoreCase(oAElem.getAttribute("calculateHPI"))) { + oap.setIdentityLinkDomainIdentifier(buildIdentityLinkDomainIdentifier(identificationNumberChild)); + } else { + // If we have business service and want to dealt with GDA, the security layer can be advised to calulate + // the Health Professional Identifier HPI instead of the wbPK + Logger.info("OA uses HPI for Identification"); + oap.setIdentityLinkDomainIdentifier(Constants.URN_PREFIX_HPI); + } + + // if OA type is "businessSErvice" set slVersion to 1.2 and ignore parameter in config file + Logger.info("OA type is \"businessService\"; setting Security Layer version to 1.2"); + slVersion = "1.2"; + + } else { + + if (authComponent!=null) { + slVersion = authComponent.getAttribute("slVersion"); + } + + } + oap.setSlVersion(slVersion); + //Check if there is an Auth-Block to read from configuration + + if (authComponent!=null) + { + oap.setProvideStammzahl(BoolUtils.valueOf(authComponent.getAttribute("provideStammzahl"))); + oap.setProvideAuthBlock(BoolUtils.valueOf(authComponent.getAttribute("provideAUTHBlock"))); + oap.setProvideIdentityLink(BoolUtils.valueOf(authComponent.getAttribute("provideIdentityLink"))); + oap.setProvideCertificate(BoolUtils.valueOf(authComponent.getAttribute("provideCertificate"))); + oap.setBkuSelectionTemplateURL(buildTemplateURL(authComponent, OA_AUTH_COMPONENT_BKUSELECT_TEMPLATE_XPATH, bkuSelectionTemplateURL)); + oap.setTemplateURL(buildTemplateURL(authComponent, OA_AUTH_COMPONENT_TEMPLATE_XPATH, templateURL)); + // load OA specific transforms if present + String[] transformsInfoFileNames = buildTransformsInfoFileNames(authComponent, OA_AUTH_COMPONENT_TRANSFORMS_INFO_FILENAME_XPATH); + try { + oap.setTransformsInfos(loadTransformsInfos(transformsInfoFileNames)); + } catch (Exception ex) { + Logger.error("Error loading transforms specified for OA \"" + publicURLPrefix + "\"; using default transforms."); + } + Node verifyInfoboxParamtersNode = XPathUtils.selectSingleNode(authComponent, OA_AUTH_COMPONENT_VERIFY_INFOBOXES_XPATH); + oap.setVerifyInfoboxParameters(buildVerifyInfoboxParameters( + verifyInfoboxParamtersNode, defaultVerifyInfoboxParameters, moaSpIdentityLinkTrustProfileID)); + } + OA_set.add(oap); + } + OAAuthParameter[] result = + new OAAuthParameter[OA_set.size()]; + OA_set.toArray(result); + + return result; + + } + + /** + * Builds the URL for a BKUSelectionTemplate or a Template. The method selects + * the uri string from the MOA ID configuration file via the given xpath expression + * and returns either this string or the default value. + * + * @param oaAuthComponent The AuthComponent element to get the template from. + * @param xpathExpr The xpath expression for selecting the template uri. + * @param defaultURL The default template url. + * @return The template url. This may either the via xpath selected uri + * or, if no template is specified within the online appliacation, + * the default url. Both may be <code>null</code>. + */ + protected String buildTemplateURL(Element oaAuthComponent, String xpathExpr, String defaultURL) { + String templateURL = XPathUtils.getAttributeValue(oaAuthComponent, xpathExpr, defaultURL); + if (templateURL != null) { + templateURL = FileUtils.makeAbsoluteURL(templateURL, rootConfigFileDir_); + } + return templateURL; + } + + + /** + * Method buildConnectionParameter: internal Method for creating a + * ConnectionParameter object with all data found in the incoming element + * @param root This Element contains the ConnectionParameter + * @return ConnectionParameter + */ + protected ConnectionParameter buildConnectionParameter(Element root) + { + ConnectionParameter result = new ConnectionParameter(); + result.setAcceptedServerCertificates( + XPathUtils.getElementValue(root,CONNECTION_PARAMETER_ACCEPTED_CERTS_XPATH,null)); + + result.setAcceptedServerCertificates(FileUtils.makeAbsoluteURL( + result.getAcceptedServerCertificates(), rootConfigFileDir_)); + + result.setUrl( + XPathUtils.getAttributeValue(root, CONNECTION_PARAMETER_URL_XPATH, "")); + result.setClientKeyStore( + XPathUtils.getElementValue(root,CONNECTION_PARAMETERN_KEYSTORE_XPATH,null)); + + result.setClientKeyStore(FileUtils.makeAbsoluteURL( + result.getClientKeyStore(), rootConfigFileDir_)); + + result.setClientKeyStorePassword( + XPathUtils.getAttributeValue(root,CONNECTION_PARAMETER_KEYSTORE_PASS_XPATH,"")); + + if ((result.getAcceptedServerCertificates()==null) + && (result.getUrl()=="") + && (result.getClientKeyStore()==null) + && (result.getClientKeyStorePassword()=="")) + return null; + + return result; + } + + + /** + * Build the mapping of generic configuration properties. + * + * @return a {@link Map} of generic configuration properties (a name to value + * mapping) from the configuration. + */ + public Map buildGenericConfiguration() { + + Map genericConfiguration = new HashMap(); + NodeIterator gcIter = + XPathUtils.selectNodeIterator( + configElem_, + GENERIC_CONFIGURATION_XPATH); + Element gcElem; + + while ((gcElem = (Element) gcIter.nextNode()) != null) { + String gcName = gcElem.getAttribute("name"); + String gcValue = gcElem.getAttribute("value"); + + genericConfiguration.put(gcName, gcValue); + } + + return genericConfiguration; + } + + + /** + * Returns the default chaining mode from the configuration. + * + * @return The default chaining mode. + */ + public String getDefaultChainingMode() { + String defaultChaining = + XPathUtils.getAttributeValue( + configElem_, + CHAINING_MODES_DEFAULT_XPATH, + CM_CHAINING); + + return translateChainingMode(defaultChaining); + + } + /** + * Build the chaining modes for all configured trust anchors. + * + * @return The mapping from trust anchors to chaining modes. + */ + public Map buildChainingModes() { + Map chainingModes = new HashMap(); + NodeIterator trustIter = + XPathUtils.selectNodeIterator(configElem_, TRUST_ANCHOR_XPATH); + Element trustAnchorElem; + + while ((trustAnchorElem = (Element) trustIter.nextNode()) != null) { + IssuerAndSerial issuerAndSerial = buildIssuerAndSerial(trustAnchorElem); + String mode = trustAnchorElem.getAttribute("mode"); + + if (issuerAndSerial != null) { + chainingModes.put(issuerAndSerial, translateChainingMode(mode)); + } + } + + return chainingModes; + } + + /** + * Build an <code>IssuerAndSerial</code> from the DOM representation. + * + * @param root The root element (being of type <code>dsig: + * X509IssuerSerialType</code>. + * @return The issuer and serial number contained in the <code>root</code> + * element or <code>null</code> if could not be built for any reason. + */ + protected IssuerAndSerial buildIssuerAndSerial(Element root) { + String issuer = XPathUtils.getElementValue(root, ISSUER_XPATH, null); + String serial = XPathUtils.getElementValue(root, SERIAL_XPATH, null); + + if (issuer != null && serial != null) { + try { + RFC2253NameParser nameParser = new RFC2253NameParser(issuer); + Principal issuerDN = nameParser.parse(); + + return new IssuerAndSerial(issuerDN, new BigInteger(serial)); + } catch (RFC2253NameParserException e) { + warn("config.09", new Object[] { issuer, serial }, e); + return null; + } catch (NumberFormatException e) { + warn("config.09", new Object[] { issuer, serial }, e); + return null; + } + } + return null; + } + + /** + * Translate the chaining mode from the configuration file to one used in the + * IAIK MOA API. + * + * @param chainingMode The chaining mode from the configuration. + * @return The chaining mode as provided by the <code>ChainingModes</code> + * interface. + * @see iaik.pki.pathvalidation.ChainingModes + */ + protected String translateChainingMode(String chainingMode) { + if (chainingMode.equals(CM_CHAINING)) { + return ChainingModes.CHAIN_MODE; + } else if (chainingMode.equals(CM_PKIX)) { + return ChainingModes.PKIX_MODE; + } else { + return ChainingModes.CHAIN_MODE; + } + } + + /** + * Builds the IdentityLinkDomainIdentifier as needed for providing it to the + * SecurityLayer for computation of the wbPK. + * <p>e.g.:<br> + * input element: + * <br> + * <code><pr:Firmenbuchnummer Identifier="FN">000468 i</pr:Firmenbuchnummer></code> + * <p> + * return value: <code>urn:publicid:gv.at+wbpk+FN468i</code> + * + * @param number The element holding the identification number of the business + * company. + * @return The domain identifier + */ + protected String buildIdentityLinkDomainIdentifier(Element number) { + if (number == null) { + return null; + } + String identificationNumber = number.getFirstChild().getNodeValue(); + String identifier = number.getAttribute("Identifier"); + // remove all blanks + identificationNumber = StringUtils.removeBlanks(identificationNumber); + if (number.getLocalName().equals("Firmenbuchnummer") || identifier.equalsIgnoreCase("fn")) { + // delete zeros from the beginning of the number + identificationNumber = StringUtils.deleteLeadingZeros(identificationNumber); + // remove hyphens + identificationNumber = StringUtils.removeToken(identificationNumber, "-"); + } + StringBuffer identityLinkDomainIdentifier = new StringBuffer(Constants.URN_PREFIX_WBPK); + identityLinkDomainIdentifier.append("+"); + if (!identificationNumber.startsWith(identifier)) { + identityLinkDomainIdentifier.append(identifier); + } + identityLinkDomainIdentifier.append("+"); + identityLinkDomainIdentifier.append(identificationNumber); + return identityLinkDomainIdentifier.toString(); + } + + /** + * Builds the parameters for verifying additional infoboxes (additional to the + * IdentityLink infobox). + * + * @param verifyInfoboxesElem The <code>VerifyInfoboxes</code> element from the + * config file. This maybe the global element or the + * elment from an Online application. + * @param defaultVerifyInfoboxParameters Default parameters to be used, if no + * <code>VerifyInfoboxes</code> element is present. + * This only applies to parameters + * of an specific online application and is set to + * <code>null</code> when building the global parameters. + * @param moaSpIdentityLinkTrustProfileID The ID of the trust profile used for validating + * the identity link signer certificate. Needed for + * checking if this ID is not used for validating other + * infoboxes. + * + * @return A {@link at.gv.egovernment.moa.id.config.auth.VerifyInfoboxParameters VerifyInfoboxParameters} + * object needed for verifying additional infoboxes. + * + * @throws ConfigurationException If the trust profile for validating the identity link + * signer certificate is used for validating another infobox. + */ + public VerifyInfoboxParameters buildVerifyInfoboxParameters( + Node verifyInfoboxesElem, + VerifyInfoboxParameters defaultVerifyInfoboxParameters, + String moaSpIdentityLinkTrustProfileID) + throws ConfigurationException + { + + if ((verifyInfoboxesElem == null) && (defaultVerifyInfoboxParameters == null)) { + return null; + } + Vector identifiers = new Vector(); + List defaultIdentifiers = null; + Map defaultInfoboxParameters = null; + if (defaultVerifyInfoboxParameters != null) { + defaultIdentifiers = defaultVerifyInfoboxParameters.getIdentifiers(); + defaultInfoboxParameters = defaultVerifyInfoboxParameters.getInfoboxParameters(); + } + Hashtable infoboxParameters = new Hashtable(); + if (verifyInfoboxesElem != null) { + // get the DefaultTrustProfileID + String defaultTrustProfileID = null; + Node defaultTrustProfileNode = + XPathUtils.selectSingleNode(verifyInfoboxesElem, VERIFY_INFOBOXES_DEFAULT_TRUST_PROFILE_XPATH); + if (defaultTrustProfileNode != null) { + Node trustProfileIDNode = + XPathUtils.selectSingleNode(defaultTrustProfileNode, VERIFY_INFOBOXES_TRUST_PROFILE_ID_XPATH); + defaultTrustProfileID = trustProfileIDNode.getFirstChild().getNodeValue(); + if (defaultTrustProfileID.equals(moaSpIdentityLinkTrustProfileID)) { + throw new ConfigurationException("config.15", new Object[] {moaSpIdentityLinkTrustProfileID}); + } + } + // get the Infoboxes + NodeList infoboxes = + XPathUtils.selectNodeList(verifyInfoboxesElem, VERIFY_INFOBOXES_INFOBOX_XPATH); + for (int i=0; i<infoboxes.getLength(); i++) { + Element infoBoxElem = (Element)infoboxes.item(i); + // get the identifier of the infobox + String identifier = infoBoxElem.getAttribute("Identifier"); + identifiers.add(identifier); + VerifyInfoboxParameter verifyInfoboxParameter = new VerifyInfoboxParameter(identifier); + verifyInfoboxParameter.setFriendlyName(identifier); + // get the attributes + // (1) required: override global value in any case + verifyInfoboxParameter.setRequired(BoolUtils.valueOf( + infoBoxElem.getAttribute("required"))); + // (2) provideStammzahl: override global value in any case + verifyInfoboxParameter.setProvideStammzahl(BoolUtils.valueOf( + infoBoxElem.getAttribute("provideStammzahl"))); + // (3) proviedIdentityLink: override global value in any case + verifyInfoboxParameter.setProvideIdentityLink(BoolUtils.valueOf( + infoBoxElem.getAttribute("provideIdentityLink"))); + // set default trustprofileID + if (defaultTrustProfileID != null) { + verifyInfoboxParameter.setTrustProfileID(defaultTrustProfileID); + } + // get the parameter elements + boolean localValidatorClass = false; + boolean localFriendlyName = false; + List params = DOMUtils.getChildElements(infoBoxElem); + Iterator it = params.iterator(); + while (it.hasNext()) { + Element paramElem = (Element)it.next(); + String paramName = paramElem.getLocalName(); + if (paramName.equals("FriendlyName")) { + verifyInfoboxParameter.setFriendlyName(paramElem.getFirstChild().getNodeValue()); + localFriendlyName = true; + } else if (paramName.equals("TrustProfileID")) { + String trustProfileID = paramElem.getFirstChild().getNodeValue(); + if (trustProfileID != null) { + if (trustProfileID.equals(moaSpIdentityLinkTrustProfileID)) { + throw new ConfigurationException("config.15", new Object[] {moaSpIdentityLinkTrustProfileID}); + } + verifyInfoboxParameter.setTrustProfileID(trustProfileID); + } + } else if (paramName.equals("ValidatorClass")) { + String validatorClassName = paramElem.getFirstChild().getNodeValue(); + if (validatorClassName != null) { + verifyInfoboxParameter.setValidatorClassName(validatorClassName); + localValidatorClass = true; + } + } else if (paramName.equals("SchemaLocations")) { + List schemaElems = DOMUtils.getChildElements(paramElem); + List schemaLocations = new Vector(schemaElems.size()); + Iterator schemaIterator = schemaElems.iterator(); + while (schemaIterator.hasNext()) { + Element schemaElem = (Element)schemaIterator.next(); + String namespace = schemaElem.getAttribute("namespace"); + String schemaLocation = schemaElem.getAttribute("schemaLocation"); + // avoid adding the same schema twice + Iterator schemaLocationIterator = schemaLocations.iterator(); + boolean add = true; + while (schemaLocationIterator.hasNext()) { + String existingNamespace = ((Schema)schemaLocationIterator.next()).getNamespace(); + if (namespace.equals(existingNamespace)) { + Logger.warn("Multiple schemas specified for namespace \"" + namespace + + "\"; only using the first one."); + add = false; + break; + } + } + if (add) { + schemaLocations.add(new SchemaImpl(namespace, schemaLocation)); + } + } + verifyInfoboxParameter.setSchemaLocations(schemaLocations); + } else if (paramName.equals("ApplicationSpecificParameters")) { + verifyInfoboxParameter.setApplicationSpecificParams(paramElem); + } + } + // use default values for those parameters not yet set by local configuration + if (defaultInfoboxParameters != null) { + Object defaultVerifyIP = defaultInfoboxParameters.get(identifier); + if (defaultVerifyIP != null) { + VerifyInfoboxParameter defaultVerifyInfoboxParameter = + (VerifyInfoboxParameter)defaultVerifyIP; + // if no friendly is set, use default + if (!localFriendlyName) { + verifyInfoboxParameter.setFriendlyName( + defaultVerifyInfoboxParameter.getFriendlyName()); + } + // if no TrustProfileID is set, use default, if available + if (verifyInfoboxParameter.getTrustProfileID() == null) { + verifyInfoboxParameter.setTrustProfileID( + defaultVerifyInfoboxParameter.getTrustProfileID()); + } + // if no local validator class is set, use default + if (!localValidatorClass) { + verifyInfoboxParameter.setValidatorClassName( + defaultVerifyInfoboxParameter.getValidatorClassName()); + } + // if no schema locations set, use default + if (verifyInfoboxParameter.getSchemaLocations() == null) { + verifyInfoboxParameter.setSchemaLocations( + defaultVerifyInfoboxParameter.getSchemaLocations()); + } + // if no application specific parameters set, use default + if (verifyInfoboxParameter.getApplicationSpecificParams() == null) { + verifyInfoboxParameter.setApplicationSpecificParams( + defaultVerifyInfoboxParameter.getApplicationSpecificParams()); + } + } + } + infoboxParameters.put(identifier, verifyInfoboxParameter); + } + // add the infobox identifiers not present within the local configuration to the + // identifier list + if (defaultIdentifiers != null) { + Iterator identifierIterator = defaultIdentifiers.iterator(); + while (identifierIterator.hasNext()) { + String defaultIdentifier = (String)identifierIterator.next(); + if (!identifiers.contains(defaultIdentifier)) { + identifiers.add(defaultIdentifier); + } + } + } + return new VerifyInfoboxParameters(identifiers, infoboxParameters); + } else { + return new VerifyInfoboxParameters(defaultIdentifiers, infoboxParameters); + } + } + + /** + * Method warn. + * @param messageId to identify a country-specific message + * @param parameters for the logger + */ + // + // various utility methods + // + + protected static void warn(String messageId, Object[] parameters) { + Logger.warn(MOAIDMessageProvider.getInstance().getMessage(messageId, parameters)); + } + + /** + * Method warn. + * @param messageId to identify a country-specific message + * @param args for the logger + * @param t as throwabl + */ + protected static void warn(String messageId, Object[] args, Throwable t) { + Logger.warn(MOAIDMessageProvider.getInstance().getMessage(messageId, args), t); + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationException.java new file mode 100644 index 000000000..2ebec0398 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationException.java @@ -0,0 +1,31 @@ +package at.gv.egovernment.moa.id.config; + +import at.gv.egovernment.moa.id.MOAIDException; + +/** + * Exception signalling an error in the configuration. + * + * @author Patrick Peck + * @version $Id$ + */ +public class ConfigurationException extends MOAIDException { + + /** + * Create a <code>MOAConfigurationException</code>. + */ + public ConfigurationException(String messageId, Object[] parameters) { + super(messageId, parameters); + } + + /** + * Create a <code>MOAConfigurationException</code>. + */ + public ConfigurationException( + String messageId, + Object[] parameters, + Throwable wrapped) { + + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationProvider.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationProvider.java new file mode 100644 index 000000000..e65c47bad --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConfigurationProvider.java @@ -0,0 +1,128 @@ +package at.gv.egovernment.moa.id.config; + +import java.math.BigInteger; +import java.security.Principal; +import java.security.cert.X509Certificate; +import java.util.Map; + +import at.gv.egovernment.moa.id.data.IssuerAndSerial; + +/** + * Base class for <code>AuthConfigurationProvider</code> and <code>ProxyConfigurationProvider</code>, + * providing functions common to both of them. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class ConfigurationProvider { + + /** + * Constructor + */ + public ConfigurationProvider() { + super(); + } + + /** + * The name of the system property which contains the file name of the + * configuration file. + */ + public static final String CONFIG_PROPERTY_NAME = + "moa.id.configuration"; + + /** + * The name of the generic configuration property giving the certstore directory path. + */ + public static final String DIRECTORY_CERTSTORE_PARAMETER_PROPERTY = + "DirectoryCertStoreParameters.RootDir"; + + /** + * The name of the generic configuration property switching the ssl revocation checking on/off + */ + public static final String TRUST_MANAGER_REVOCATION_CHECKING = + "TrustManager.RevocationChecking"; + + + /** + * A <code>Map</code> which contains generic configuration information. Maps a + * configuration name (a <code>String</code>) to a configuration value (also a + * <code>String</code>). + */ + protected Map genericConfiguration; + + /** The default chaining mode. */ + protected String defaultChainingMode; + + /** + * A <code>Map</code> which contains the <code>IssuerAndSerial</code> to + * chaining mode (a <code>String</code>) mapping. + */ + protected Map chainingModes; + + /** + * the URL for the trusted CA Certificates + */ + protected String trustedCACertificates; + + /** + * main configuration file directory name used to configure MOA-ID + */ + protected String rootConfigFileDir; + + /** + * Returns the main configuration file directory used to configure MOA-ID + * + * @return the directory + */ + public String getRootConfigFileDir() { + return rootConfigFileDir; + } + + /** + * Returns the mapping of generic configuration properties. + * + * @return The mapping of generic configuration properties (a name to value + * mapping) from the configuration. + */ + public Map getGenericConfiguration() { + return genericConfiguration; + } + + /** + * Returns the value of a parameter from the generic configuration section. + * + * @return the parameter value; <code>null</code> if no such parameter + */ + public String getGenericConfigurationParameter(String parameter) { + if (! genericConfiguration.containsKey(parameter)) + return null; + return (String)genericConfiguration.get(parameter); + } + + /** + * Return the chaining mode for a given trust anchor. + * + * @param trustAnchor The trust anchor for which the chaining mode should be + * returned. + * @return The chaining mode for the given trust anchor. If the trust anchor + * has not been configured separately, the system default will be returned. + */ + public String getChainingMode(X509Certificate trustAnchor) { + Principal issuer = trustAnchor.getIssuerDN(); + BigInteger serial = trustAnchor.getSerialNumber(); + IssuerAndSerial issuerAndSerial = new IssuerAndSerial(issuer, serial); + + String mode = (String) chainingModes.get(issuerAndSerial); + return mode != null ? mode : defaultChainingMode; + } + + /** + * Returns the trustedCACertificates. + * @return String + */ + public String getTrustedCACertificates() { + + return trustedCACertificates; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConnectionParameter.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConnectionParameter.java new file mode 100644 index 000000000..30b09cfe0 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/ConnectionParameter.java @@ -0,0 +1,106 @@ +package at.gv.egovernment.moa.id.config; + +/** + * This bean class is used to store data for various connectionParameter + * within the MOA-ID configuration + * + * @author Stefan Knirsch + * @version $Id$ + */ +public class ConnectionParameter { + + /** + * Server URL + */ + private String url; + /** + * File URL for a directory containing PKCS#12 server SSL certificates. + * From these certificates, a X509 trust store will be assembled for use + * by a JSSE <code>TrustManager</code>. + * This field will only be used in case of an HTTPS URL. + */ + private String acceptedServerCertificates; + /** + * File URL of a X509 key store containing the private key to be used + * for an HTTPS connection when the server requires client authentication. + * This field will only be used in case of an HTTPS URL. + */ + private String clientKeyStore; + /** + * Password protecting the client key store. + */ + private String clientKeyStorePassword; + + /** + * Checks whether the URL scheme is <code>"https"</code>. + * @return true in case of an URL starting with <code>"https"</code> + */ + public boolean isHTTPSURL() { + return getUrl().indexOf("https") == 0; + } + + /** + * Returns the url. + * @return String + */ + public String getUrl() { + return url; + } + + /** + * Returns the acceptedServerCertificates. + * @return String + */ + public String getAcceptedServerCertificates() { + return acceptedServerCertificates; + } + + /** + * Sets the acceptedServerCertificates. + * @param acceptedServerCertificates The acceptedServerCertificates to set + */ + public void setAcceptedServerCertificates(String acceptedServerCertificates) { + this.acceptedServerCertificates = acceptedServerCertificates; + } + + /** + * Sets the url. + * @param url The url to set + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * Returns the clientKeyStore. + * @return String + */ + public String getClientKeyStore() { + return clientKeyStore; + } + + /** + * Returns the clientKeyStorePassword. + * @return String + */ + public String getClientKeyStorePassword() { + return clientKeyStorePassword; + } + + /** + * Sets the clientKeyStore. + * @param clientKeyStore The clientKeyStore to set + */ + public void setClientKeyStore(String clientKeyStore) { + this.clientKeyStore = clientKeyStore; + } + + /** + * Sets the clientKeyStorePassword. + * @param clientKeyStorePassword The clientKeyStorePassword to set + */ + public void setClientKeyStorePassword(String clientKeyStorePassword) { + this.clientKeyStorePassword = clientKeyStorePassword; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/OAParameter.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/OAParameter.java new file mode 100644 index 000000000..19a27b463 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/OAParameter.java @@ -0,0 +1,76 @@ +package at.gv.egovernment.moa.id.config; + +/** + * Configuration parameters belonging to an online application, + * to be used within both, the MOA ID Auth and the + * MOA ID PROXY component. + * + * @author Harald Bratko + */ +public class OAParameter { + + /** + * type of the online application (maybe "PublicService" or "BusinessService") + */ + private String oaType; + + /** + * specifies whether the online application is a business application or not + * (<code>true</code> if value of {@link #oaType} is "businessService" + */ + private boolean businessService; + + /** + * public URL prefix of the online application + */ + private String publicURLPrefix; + + /** + * Returns the type of the online application. + * @return the type of the online application. + */ + public String getOaType() { + return oaType; + } + + /** + * Returns <code>true</code> is the OA is a businss application, otherwise + * <code>false</code>. + * @return <code>true</code> is the OA is a businss application, otherwise + * <code>false</code> + */ + public boolean getBusinessService() { + return this.businessService; + } + + /** + * Returns the publicURLPrefix. + * @return String + */ + public String getPublicURLPrefix() { + return publicURLPrefix; + } + + /** + * + * Sets the type of the online application. + * If the type is "businessService" the value of <code>businessService</code> + * ({@link #getBusinessService()}) is also set to <code>true</code> + * @param oaType The type of the online application. + */ + public void setOaType(String oaType) { + this.oaType = oaType; + if ("businessService".equalsIgnoreCase(oaType)) { + this.businessService = true; + } + } + + /** + * Sets the publicURLPrefix. + * @param publicURLPrefix The publicURLPrefix to set + */ + public void setPublicURLPrefix(String publicURLPrefix) { + this.publicURLPrefix = publicURLPrefix; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/AuthConfigurationProvider.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/AuthConfigurationProvider.java new file mode 100644 index 000000000..b4af6592c --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/AuthConfigurationProvider.java @@ -0,0 +1,414 @@ +package at.gv.egovernment.moa.id.config.auth; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import at.gv.egovernment.moa.id.config.ConfigurationBuilder; +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.logging.Logger; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.FileUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * A class providing access to the Auth Part of the MOA-ID configuration data. + * + * <p>Configuration data is read from an XML file, whose location is given by + * the <code>moa.id.configuration</code> system property.</p> + * <p>This class implements the Singleton pattern. The <code>reload()</code> + * method can be used to update the configuration data. Therefore, it is not + * guaranteed that consecutive calls to <code>getInstance()</code> will return + * the same <code>AuthConfigurationProvider</code> all the time. During the + * processing of a web service request, the current + * <code>TransactionContext</code> should be used to obtain the + * <code>AuthConfigurationProvider</code> local to that request.</p> + * + * @author Patrick Peck + * @author Stefan Knirsch + * + * @version $Id$ + */ +public class AuthConfigurationProvider extends ConfigurationProvider { + + /** DEFAULT_ENCODING is "UTF-8" */ + private static final String DEFAULT_ENCODING="UTF-8"; + /** + * The name of the generic configuration property giving the authentication session time out. + */ + public static final String AUTH_SESSION_TIMEOUT_PROPERTY = + "AuthenticationSession.TimeOut"; + /** + * The name of the generic configuration property giving the authentication data time out. + */ + public static final String AUTH_DATA_TIMEOUT_PROPERTY = + "AuthenticationData.TimeOut"; + + /** + * BKUSelectionType HTMLComplete, according to schema type <code>BKUSelectionType</code> + */ + public static final String BKU_SELECTION_TYPE_HTMLCOMPLETE = + "HTMLComplete"; + + /** + * BKUSelectionType HTMLSelect, according to schema type <code>BKUSelectionType</code> + */ + public static final String BKU_SELECTION_TYPE_HTMLSELECT = + "HTMLSelect"; + + /** + * The name of the generic configuration property allowing https connection to + * the user frontend servlets ("StartAuthentication" and "SelectBKU" servlets) + */ + public static final String FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY = + "FrontendServlets.EnableHTTPConnection"; + + /** + * The name of the generic configuration property allowing to set a individual + * DATA URL used to communicate with the BKU (SecurityLayer) + */ + public static final String INDIVIDUAL_DATA_URL_PREFIX = + "FrontendServlets.DataURLPrefix"; + + /** Singleton instance. <code>null</code>, if none has been created. */ + private static AuthConfigurationProvider instance; + + // + // configuration data + // + + /** + * configuration files containing transformations for rendering in the + * secure viewer of the security layer implementation; + * multiple files can be given for different mime types + */ + private String[] transformsInfoFileNames; + + /** + * transformations for rendering in the secure viewer of the security layer implementation, + * read from {@link transformsInfoFileNames}; + * multiple transformation can be given for different mime types + */ + private String[] transformsInfos; + + /** + * parameters for connection to MOA SP component + */ + private ConnectionParameter moaSpConnectionParameter; + /** + * trust profile ID to be used for verifying the identity link signature via MOA ID SP + */ + private String moaSpIdentityLinkTrustProfileID; + /** + * trust profile ID to be used for verifying the AUTH block signature via MOA ID SP + */ + private String moaSpAuthBlockTrustProfileID; + /** + * transformations to be used for verifying the AUTH block signature via MOA ID SP + */ + private String[] moaSpAuthBlockVerifyTransformsInfoIDs; + /** + * X509 SubjectNames which will be trusted + */ + private List identityLinkX509SubjectNames; + /** + * default parameters for verifying additional infoboxes. + */ + private VerifyInfoboxParameters defaultVerifyInfoboxParameters; + + /** + * configuration parameters for online applications + */ + private OAAuthParameter[] onlineApplicationAuthParameters; + /** + * the Selection Type of the bku Selection Element + */ + private String bKUSelectionType; + /** + * is the bku Selection Element present? + */ + private boolean bKUSelectable; + /** + * the bku Selection Connection Parameters + */ + private ConnectionParameter bKUConnectionParameter; + + /** + * Return the single instance of configuration data. + * + * @return AuthConfigurationProvider The current configuration data. + * @throws ConfigurationException + */ + public static synchronized AuthConfigurationProvider getInstance() + throws ConfigurationException { + + if (instance == null) { + reload(); + } + return instance; + } + + /** + * Reload the configuration data and set it if successful. + * + * @return AuthConfigurationProvider The loaded configuration data. + * @throws ConfigurationException Failure to load the configuration data. + */ + public static synchronized AuthConfigurationProvider reload() + throws ConfigurationException { + String fileName = System.getProperty(ConfigurationProvider.CONFIG_PROPERTY_NAME); + if (fileName == null) { + throw new ConfigurationException("config.01", null); + } + Logger.info("Loading MOA-ID-AUTH configuration " + fileName); + + instance = new AuthConfigurationProvider(fileName); + return instance; + } + + /** + * Constructor for AuthConfigurationProvider. + * @param fileName + * @throws ConfigurationException + */ + public AuthConfigurationProvider(String fileName) + throws ConfigurationException { + + load(fileName); + } + + /** + * Load the configuration data from XML file with the given name and build + * the internal data structures representing the MOA ID configuration. + * + * @param fileName The name of the XML file to load. + * @throws ConfigurationException The MOA configuration could not be + * read/built. + */ + private void load(String fileName) throws ConfigurationException { + InputStream stream = null; + Element configElem; + ConfigurationBuilder builder; + + try { + // load the main config file + stream = new BufferedInputStream(new FileInputStream(fileName)); + configElem = DOMUtils.parseXmlValidating(stream); + } catch (Throwable t) { + throw new ConfigurationException("config.03", null, t); + } + finally { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + } + } + try { + // determine the directory of the root config file + rootConfigFileDir = new File(fileName).getParent(); + try { + rootConfigFileDir = new File(rootConfigFileDir).toURL().toString(); + } catch (MalformedURLException t) { + throw new ConfigurationException("config.03", null, t); + } + + // build the internal datastructures + builder = new ConfigurationBuilder(configElem, rootConfigFileDir); + bKUConnectionParameter = builder.buildAuthBKUConnectionParameter(); + bKUSelectable = (bKUConnectionParameter!=null); + bKUSelectionType = builder.buildAuthBKUSelectionType(); + genericConfiguration = builder.buildGenericConfiguration(); + transformsInfoFileNames = builder.buildTransformsInfoFileNames(builder.getConfigElem(), ConfigurationBuilder.AUTH_SECLAYER_TRANSFORMS_INFO_FILENAME_XPATH); + transformsInfos = builder.loadTransformsInfos(transformsInfoFileNames); + moaSpConnectionParameter = builder.buildMoaSpConnectionParameter(); + moaSpIdentityLinkTrustProfileID = builder.getMoaSpIdentityLinkTrustProfileID(); + moaSpAuthBlockTrustProfileID = builder.getMoaSpAuthBlockTrustProfileID(); + moaSpAuthBlockVerifyTransformsInfoIDs = builder.buildMoaSpAuthBlockVerifyTransformsInfoIDs(); + defaultVerifyInfoboxParameters = null; + Node defaultVerifyInfoboxParamtersElem = XPathUtils.selectSingleNode(configElem, ConfigurationBuilder.AUTH_VERIFY_INFOBOXES_XPATH); + if (defaultVerifyInfoboxParamtersElem != null) { + defaultVerifyInfoboxParameters = + builder.buildVerifyInfoboxParameters((Element)defaultVerifyInfoboxParamtersElem, null, moaSpIdentityLinkTrustProfileID); + } + onlineApplicationAuthParameters = builder.buildOnlineApplicationAuthParameters(defaultVerifyInfoboxParameters, moaSpIdentityLinkTrustProfileID); + identityLinkX509SubjectNames = builder.getIdentityLink_X509SubjectNames(); + defaultChainingMode = builder.getDefaultChainingMode(); + chainingModes = builder.buildChainingModes(); + trustedCACertificates = builder.getTrustedCACertificates(); + trustedCACertificates = FileUtils.makeAbsoluteURL(trustedCACertificates, rootConfigFileDir); + + } catch (Throwable t) { + throw new ConfigurationException("config.02", null, t); + } + } + + /** + * Loads the <code>transformsInfos</code> from files. + * @throws Exception on any exception thrown + */ +// private void loadTransformsInfos() throws Exception { +// +// transformsInfos = new String[transformsInfoFileNames.length]; +// for (int i = 0; i < transformsInfoFileNames.length; i++) { +// String fileURL = transformsInfoFileNames[i]; +// +// //if fileURL is relative to rootConfigFileDir make it absolute +// fileURL = FileUtils.makeAbsoluteURL(fileURL, rootConfigFileDir); +// String transformsInfo = FileUtils.readURL(fileURL, DEFAULT_ENCODING); +// transformsInfos[i] = transformsInfo; +// } +// } + + /** + * Loads the <code>transformsInfos</code> from files. + * @throws Exception on any exception thrown + */ + private String[] loadTransformsInfos(String[] transformsInfoFileNames) throws Exception { + + String[] transformsInfos = new String[transformsInfoFileNames.length]; + for (int i = 0; i < transformsInfoFileNames.length; i++) { + String fileURL = transformsInfoFileNames[i]; + + //if fileURL is relative to rootConfigFileDir make it absolute + fileURL = FileUtils.makeAbsoluteURL(fileURL, rootConfigFileDir); + String transformsInfo = FileUtils.readURL(fileURL, DEFAULT_ENCODING); + transformsInfos[i] = transformsInfo; + } + return transformsInfos; + } + /** + * Return a string array with all filenames leading + * to the Transforms Information for the Security Layer + * @return String[] of filenames to the Security Layer Transforms Information + */ + public String[] getTransformsInfoFileNames() { + return transformsInfoFileNames; + } + + /** + * Build an array of the OnlineApplication Parameters containing information + * about the authentication component + * @return An OAProxyParameter array containing beans + * with all relevant information for theauthentication component of the online + * application + */ + public OAAuthParameter[] getOnlineApplicationParameters() { + return onlineApplicationAuthParameters; + } + + /** + * Provides configuration information regarding the online application behind + * the given URL, relevant to the MOA-ID Auth component. + * + * @param oaURL URL requested for an online application + * @return an <code>OAAuthParameter</code>, or <code>null</code> + * if none is applicable + */ + public OAAuthParameter getOnlineApplicationParameter(String oaURL) { + OAAuthParameter[] oaParams = getOnlineApplicationParameters(); + for (int i = 0; i < oaParams.length; i++) { + OAAuthParameter oaParam = oaParams[i]; + if (oaURL.indexOf(oaParam.getPublicURLPrefix()) == 0) + return oaParam; + } + return null; + } + + /** + * Return a string with a url-reference to the VerifyAuthBlock trust + * profile id within the moa-sp part of the authentication component + * + * @return String with a url-reference to the VerifyAuthBlock trust profile ID + */ + public String getMoaSpAuthBlockTrustProfileID() { + return moaSpAuthBlockTrustProfileID; + } + + /** + * Return a string array with references to all verify transform info + * IDs within the moa-sp part of the authentication component + * @return A string array containing all urls to the + * verify transform info IDs + */ + public String[] getMoaSpAuthBlockVerifyTransformsInfoIDs() { + return moaSpAuthBlockVerifyTransformsInfoIDs; + } + + /** + * Return a ConnectionParameter bean containing all information + * of the authentication component moa-sp element + * @return ConnectionParameter of the authentication component moa-sp element + */ + public ConnectionParameter getMoaSpConnectionParameter() { + return moaSpConnectionParameter; + } + + /** + * Return a string with a url-reference to the VerifyIdentityLink trust + * profile id within the moa-sp part of the authentication component + * @return String with a url-reference to the VerifyIdentityLink trust profile ID + */ + public String getMoaSpIdentityLinkTrustProfileID() { + return moaSpIdentityLinkTrustProfileID; + } + /** + * Returns the transformsInfos. + * @return String[] + */ + public String[] getTransformsInfos() { + return transformsInfos; + } + + /** + * Returns the identityLinkX509SubjectNames. + * @return List + */ + public List getIdentityLinkX509SubjectNames() { + return identityLinkX509SubjectNames; + } + + /** + * Returns the bKUConnectionParameter. + * @return ConnectionParameter + */ + public ConnectionParameter getBKUConnectionParameter() { + return bKUConnectionParameter; + } + + /** + * Returns the bKUSelectable. + * @return boolean + */ + public boolean isBKUSelectable() { + return bKUSelectable; + } + + /** + * Returns the bKUSelectionType. + * @return String + */ + public String getBKUSelectionType() { + return bKUSelectionType; + } + + /** + * Returns the defaultVerifyInfoboxParameters. + * + * @return The defaultVerifyInfoboxParameters. + */ + public VerifyInfoboxParameters getDefaultVerifyInfoboxParameters() { + return defaultVerifyInfoboxParameters; + } + +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/OAAuthParameter.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/OAAuthParameter.java new file mode 100644 index 000000000..ba3b61f9d --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/OAAuthParameter.java @@ -0,0 +1,268 @@ +package at.gv.egovernment.moa.id.config.auth; + +import at.gv.egovernment.moa.id.config.OAParameter; + +/** + * Configuration parameters belonging to an online application, + * to use with the MOA ID Auth component. + * + * @author Stefan Knirsch + * @version $Id$ + */ +/** + * + * + * @author Harald Bratko + */ +public class OAAuthParameter extends OAParameter { + /** + * Sercurity Layer version + */ + private String slVersion; + /** + * true, if the Security Layer version is version 1.2, otherwise false + */ + private boolean slVersion12; + /** + * identityLinkDomainIdentifier + * (e.g <code>urn:publicid:gv.at+wbpk+FN468i</code> for a "Firmenbuchnummer") + * <br> + * only used within a business application context for providing it to the + * security layer as input for wbPK computation + */ + private String identityLinkDomainIdentifier; + /** + * key box Identifier (e.g. CertifiedKeypair, SecureSignatureKeypair) + */ + private String keyBoxIdentifier; + /** + * transformations for rendering in the secure viewer of the security layer + * implementation; multiple transformation can be given for different mime types + */ + private String[] transformsInfos; + /** + * determines whether "Stammzahl" is to be included in the authentication data + */ + private boolean provideStammzahl; + /** + * determines whether AUTH block is to be included in the authentication data + */ + private boolean provideAuthBlock; + /** + * determines whether identity link is to be included in the authentication data + */ + private boolean provideIdentityLink; + /** + * determines whether the certificate is to be included in the authentication data + */ + private boolean provideCertificate; + /** + * url to a template for web page "Auswahl der Bürgerkartenumgebung" + */ + private String bkuSelectionTemplateURL; + /** + * template for web page "Anmeldung mit Bürgerkarte" + */ + private String templateURL; + /** + * Parameters for verifying infoboxes. + */ + private VerifyInfoboxParameters verifyInfoboxParameters; + + /** + * Returns <code>true</code> if the Security Layer version is version 1.2, + * otherwise <code>false</code>. + * @return <code>true</code> if the Security Layer version is version 1.2, + * otherwise <code>false</code> + */ + public boolean getSlVersion12() { + return slVersion12; + } + + /** + * Returns the security layer version. + * @return the security layer version. + */ + public String getSlVersion() { + return slVersion; + } + + /** + * Returns the identityLinkDomainIdentifier. + * @return the identityLinkDomainIdentifier. + */ + public String getIdentityLinkDomainIdentifier() { + return identityLinkDomainIdentifier; + } + + /** + * Returns the transformsInfos. + * @return the transformsInfos. + */ + public String[] getTransformsInfos() { + return transformsInfos; + } + + /** + * Returns the provideAuthBlock. + * @return String + */ + public boolean getProvideAuthBlock() { + return provideAuthBlock; + } + + /** + * Returns the provideIdentityLink. + * @return String + */ + public boolean getProvideIdentityLink() { + return provideIdentityLink; + } + + /** + * Returns the provideStammzahl. + * @return String + */ + public boolean getProvideStammzahl() { + return provideStammzahl; + } + + /** + * Returns <code>true</code> if the certificate should be provided within the + * authentication data, otherwise <code>false</code>. + * @return <code>true</code> if the certificate should be provided, + * otherwise <code>false</code> + */ + public boolean getProvideCertifcate() { + return provideCertificate; + } + + /** + * Returns the key box identifier. + * @return String + */ + public String getKeyBoxIdentifier() { + return keyBoxIdentifier; + } + + /** + * Returns the BkuSelectionTemplate url. + * @return The BkuSelectionTemplate url or <code>null</code> if no url for + * a BkuSelectionTemplate is set. + */ + public String getBkuSelectionTemplateURL() { + return bkuSelectionTemplateURL; + } + + /** + * Returns the TemplateURL url. + * @return The TemplateURL url or <code>null</code> if no url for + * a Template is set. + */ + public String getTemplateURL() { + return templateURL; + } + + /** + * Returns the parameters for verifying additional infoboxes. + * + * @return The parameters for verifying additional infoboxes. + * Maybe <code>null</code>. + */ + public VerifyInfoboxParameters getVerifyInfoboxParameters() { + return verifyInfoboxParameters; + } + + /** + * Sets the security layer version. + * Also sets <code>slVersion12</code> ({@link #getSlVersion12()}) + * to <code>true</code> if the Security Layer version is 1.2. + * @param slVersion The security layer version to be used. + */ + public void setSlVersion(String slVersion) { + this.slVersion = slVersion; + if ("1.2".equals(slVersion)) { + this.slVersion12 = true; + } + } + /** + * Sets the IdentityLinkDomainIdentifier. + * @param identityLinkDomainIdentifier The IdentityLinkDomainIdentifiern number of the online application. + */ + public void setIdentityLinkDomainIdentifier(String identityLinkDomainIdentifier) { + this.identityLinkDomainIdentifier = identityLinkDomainIdentifier; + } + /** + * Sets the transformsInfos. + * @param transformsInfos The transformsInfos to be used. + */ + public void setTransformsInfos(String[] transformsInfos) { + this.transformsInfos = transformsInfos; + } + /** + * Sets the provideAuthBlock. + * @param provideAuthBlock The provideAuthBlock to set + */ + public void setProvideAuthBlock(boolean provideAuthBlock) { + this.provideAuthBlock = provideAuthBlock; + } + + /** + * Sets the provideIdentityLink. + * @param provideIdentityLink The provideIdentityLink to set + */ + public void setProvideIdentityLink(boolean provideIdentityLink) { + this.provideIdentityLink = provideIdentityLink; + } + + /** + * Sets the provideStammzahl. + * @param provideStammzahl The provideStammzahl to set + */ + public void setProvideStammzahl(boolean provideStammzahl) { + this.provideStammzahl = provideStammzahl; + } + + /** + * Sets the provideCertificate variable. + * @param provideCertificate The provideCertificate value to set + */ + public void setProvideCertificate(boolean provideCertificate) { + this.provideCertificate = provideCertificate; + } + + /** + * Sets the key box identifier. + * @param keyBoxIdentifier to set + */ + public void setKeyBoxIdentier(String keyBoxIdentifier) { + this.keyBoxIdentifier = keyBoxIdentifier; + } + + /** + * Sets the BkuSelectionTemplate url. + * @param bkuSelectionTemplateURL The url string specifying the location + * of a BkuSelectionTemplate. + */ + public void setBkuSelectionTemplateURL(String bkuSelectionTemplateURL) { + this.bkuSelectionTemplateURL = bkuSelectionTemplateURL; + } + + /** + * Sets the Template url. + * @param templateURL The url string specifying the location + * of a Template. + */ + public void setTemplateURL(String templateURL) { + this.templateURL = templateURL; + } + + /** + * Sets the parameters for verifying additonal (to the identitylink infobox) infoboxes. + * + * @param verifyInfoboxParameters The verifyInfoboxParameters to set. + */ + public void setVerifyInfoboxParameters(VerifyInfoboxParameters verifyInfoboxParameters) { + this.verifyInfoboxParameters = verifyInfoboxParameters; + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/VerifyInfoboxParameter.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/VerifyInfoboxParameter.java new file mode 100644 index 000000000..fbd42f975 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/VerifyInfoboxParameter.java @@ -0,0 +1,360 @@ +package at.gv.egovernment.moa.id.config.auth; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import javax.xml.transform.TransformerException; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.auth.data.Schema; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.StringUtils; + +/** + * This class is a container for parameters that maybe needed for verifying an infobox. + * + * @author Harald Bratko + */ +public class VerifyInfoboxParameter { + + /** + * The default package name (first part) of a infobox validator class. + */ + public static final String DEFAULT_PACKAGE_TRUNK = "at.gv.egovernment.moa.id.auth.validator."; + + /** + * The identifier of the infobox to be verified. This identifier must exactly the + * identifier of the infobox returned by BKU. + */ + protected String identifier_; + + /** + * The friendly name of the infobox. + * This name is used within browser messages, thus it should be the german equivalent of + * the {@link #identifier_ infobox identifier} (e.g. "<code>Stellvertretungen</code>" + * for "<code>Mandates</code>" or "<code>GDAToken</code>" for + * "<code>EHSPToken</code>". + * <br>If not specified within the config file the {@link #identifier_ infobox identifier} + * will be used. + */ + protected String friendlyName_; + + /** + * The Id of the TrustProfile to be used for validating certificates. + */ + protected String trustProfileID_; + + /** + * The full name of the class to be used for verifying the infobox. + */ + protected String validatorClassName_; + + /** + * Schema location URIs that may be needed by the + * validator to parse infobox tokens. + * Each entry in the list is a {@link at.gv.egovernment.moa.id.auth.data.Schema Schema} + * specifying the location of an XML schema. + */ + protected List schemaLocations_; + + /** + * Application specific parameters that may be needed for verifying an infobox. + */ + protected Element applicationSpecificParams_; + + /** + * Specifies if the infobox is be required to be returned by the BKU. + */ + protected boolean required_; + + /** + * Specifies whether the <code>Stammzahl</code> should be passed to the verifying + * application or not. + */ + protected boolean provideStammzahl_; + + /** + * Specifies whether the <code>identity link</code> should be passed to the verifying + * application or not. + */ + protected boolean provideIdentityLink_; + + /** + * Initializes this VerifiyInfoboxParamater with the given identifier and a default + * validator class name. + * + * @param identifier The identifier of the infobox to be verified. + */ + public VerifyInfoboxParameter(String identifier) { + identifier_ = identifier; + StringBuffer sb = new StringBuffer(DEFAULT_PACKAGE_TRUNK); + sb.append(identifier.toLowerCase()); + sb.append("."); + sb.append(identifier.substring(0, 1).toUpperCase()); + sb.append(identifier.substring(1)); + sb.append("Validator"); + validatorClassName_ = sb.toString(); + } + + /** + * Returns application specific parameters. + * Each child element of this element contains a verifying application specific parameter. {@link #applicationSpecificParams_} + * + * @see #applicationSpecificParams_ + * + * @return Application specific parameters. + */ + public Element getApplicationSpecificParams() { + return applicationSpecificParams_; + } + + /** + * Sets the application specific parameters. + * + * @see #applicationSpecificParams_ + * + * @param applicationSpecificParams The application specific parameters to set. + */ + public void setApplicationSpecificParams(Element applicationSpecificParams) { + applicationSpecificParams_ = applicationSpecificParams; + } + + /** + * Returns the friendly name. + * + * @see #friendlyName_ + * + * @return The friendly name. + */ + public String getFriendlyName() { + return friendlyName_; + } + + /** + * Sets the friendly name. + * + * @param friendlyName The friendly name to set. + */ + public void setFriendlyName(String friendlyName) { + friendlyName_ = friendlyName; + } + + /** + * Returns the infobox identifier. + * + * @see #identifier_ + * + * @return The infobox identifier. + */ + public String getIdentifier() { + return identifier_; + } + + /** + * Sets the the infobox identifier. + * + * @see #identifier_ + * + * @param identifier The infobox identifier to set. + */ + public void setIdentifier(String identifier) { + identifier_ = identifier; + } + + /** + * Specifies whether the identity link should be passed to the verifying application + * or not. + * + * @return <code>True</code> if the identity link should be passed to the verifying + * application, otherwise <code>false</code>. + */ + public boolean getProvideIdentityLink() { + return provideIdentityLink_; + } + + /** + * Sets the {@link #provideIdentityLink_} parameter. + * + * @param provideIdentityLink <code>True</code> if the identity link should be passed to + * the verifying application, otherwise <code>false</code>. + */ + public void setProvideIdentityLink(boolean provideIdentityLink) { + provideIdentityLink_ = provideIdentityLink; + } + + /** + * Specifies whether the <code>Stammzahl</code> should be passed to the verifying + * application or not. + * + * @return <code>True</code> if the <code>Stammzahl</code> should be passed to the + * verifying application, otherwise <code>false</code>. + */ + public boolean getProvideStammzahl() { + return provideStammzahl_; + } + + /** + * Sets the {@link #provideStammzahl_} parameter. + * + * @param provideStammzahl <code>True</code> if the <code>Stammzahl</code> should be + * passed to the verifying application, otherwise <code>false</code>. + */ + public void setProvideStammzahl(boolean provideStammzahl) { + provideStammzahl_ = provideStammzahl; + } + + /** + * Specifies whether the infobox is required or not. + * + * @return <code>True</code> if the infobox is required to be returned by the BKU, + * otherwise <code>false</code>. + */ + public boolean isRequired() { + return required_; + } + + /** + * Sets the {@link #required_} parameter. + * + * @param required <code>True</code> if the infobox is required to be returned by the + * BKU, otherwise <code>false</code>. + */ + public void setRequired(boolean required) { + required_ = required; + } + + /** + * Schema location URIs that may be needed by the + * validator to parse infobox tokens. + * Each entry in the list is a {@link at.gv.egovernment.moa.id.auth.data.Schema Schema} + * specifying the location of an XML schema. + * + * @return A list of {@link at.gv.egovernment.moa.id.auth.data.Schema Schema} objects + * each of them specifying the location of an XML schema. + */ + public List getSchemaLocations() { + return schemaLocations_; + } + + /** + * Sets the schema locations. + * + * @see #schemaLocations_ + * + * @param schemaLocations The schema location list to be set. + */ + public void setSchemaLocations(List schemaLocations) { + schemaLocations_ = schemaLocations; + } + + /** + * Returns the ID of the trust profile to be used for verifying certificates. + * + * @return The ID of the trust profile to be used for verifying certificates. + * Maybe <code>null</code>. + */ + public String getTrustProfileID() { + return trustProfileID_; + } + + /** + * Sets the ID of the trust profile to be used for verifying certificates. + * + * @param trustProfileID The ID of the trust profile to be used for verifying certificates. + */ + public void setTrustProfileID(String trustProfileID) { + trustProfileID_ = trustProfileID; + } + + /** + * Returns the name of the class to be used for verifying this infobox. + * + * @return The name of the class to be used for verifying this infobox. + */ + public String getValidatorClassName() { + return validatorClassName_; + } + + /** + * Sets the name of the class to be used for verifying this infobox. + * + * @param validatorClassName The name of the class to be used for verifying this infobox. + */ + public void setValidatorClassName(String validatorClassName) { + validatorClassName_ = validatorClassName; + } + + /** + * Get a string representation of this object. + * This method is for debugging purposes only. + * + * @return A string representation of this object. + */ + public String toString() { + + StringBuffer buffer = new StringBuffer(1024); + + buffer.append(" <Infobox Identifier=\""); + buffer.append(identifier_); + buffer.append("\" required=\""); + buffer.append(required_); + buffer.append("\" provideStammzahl=\""); + buffer.append(provideStammzahl_); + buffer.append("\" provideIdentityLink=\""); + buffer.append(provideIdentityLink_); + buffer.append("\">"); + buffer.append("\n"); + if (friendlyName_ != null) { + buffer.append(" <FriendlyName>"); + buffer.append(friendlyName_); + buffer.append("</FriendlyName>"); + buffer.append("\n"); + } + if (trustProfileID_ != null) { + buffer.append(" <TrustProfileID>"); + buffer.append(trustProfileID_); + buffer.append("</TrustProfileID>"); + buffer.append("\n"); + } + if (validatorClassName_ != null) { + buffer.append(" <ValidatorClass>"); + buffer.append(validatorClassName_); + buffer.append("</ValidatorClass>"); + buffer.append("\n"); + } + if (schemaLocations_ != null) { + buffer.append(" <SchemaLocations>"); + buffer.append("\n"); + Iterator it = schemaLocations_.iterator(); + while (it.hasNext()) { + buffer.append(" <Schema namespace=\""); + Schema schema = (Schema)it.next(); + buffer.append(schema.getNamespace()); + buffer.append("\" schemaLocation=\""); + buffer.append(schema.getSchemaLocation()); + buffer.append("\"/>\n"); + } + buffer.append(" </SchemaLocations>"); + buffer.append("\n"); + } + if (applicationSpecificParams_ != null) { + try { + String applicationSpecificParams = DOMUtils.serializeNode(applicationSpecificParams_); + buffer.append(" "); + buffer.append(StringUtils.removeXMLDeclaration(applicationSpecificParams)); + buffer.append("\n"); + } catch (TransformerException e) { + // do nothing + } catch (IOException e) { + // do nothing + } + } + buffer.append(" </Infobox>"); + + + return buffer.toString() ; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/VerifyInfoboxParameters.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/VerifyInfoboxParameters.java new file mode 100644 index 000000000..15c0c328c --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/auth/VerifyInfoboxParameters.java @@ -0,0 +1,135 @@ +package at.gv.egovernment.moa.id.config.auth; + +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This class contains the parameters for verifying all the infoboxes configured for an + * online application. + * + * @author Harald Bratko + */ +public class VerifyInfoboxParameters { + + /** + * A map of {@link VerifyInfoboxParameter} objects. + * Each of these objects contains parameters that maybe needed for validating an + * infobox. + */ + protected Map infoboxParameters_; + + /** + * A list of the identifiers of the infoboxes supported by this + * VerifyInfoboxParameters; + */ + protected List identifiers_; + + /** + * Holds the (comma separated) identifiers of those infoboxes MOA-IF is able to validate + * in the context of the actual online application. + * The string will be added as value of the <code>PushInfobox</code> parameter in the + * HTML form used for reading the infoboxes from the BKU. + */ + protected String pushInfobox_; + + /** + * Initializes this VerifyInfoboxParameters with an empty {@link #infoboxParameters_} + * map. + */ + public VerifyInfoboxParameters() { + infoboxParameters_ = new Hashtable(); + pushInfobox_ = ""; + } + + /** + * Initializes this VerifyInfoboxParameters with the given + * <code>infoboxParameters</code> map and builds the {@link #pushInfobox_} string + * from the keys of the given map. + */ + public VerifyInfoboxParameters(List identifiers, Map infoboxParameters) { + identifiers_ = identifiers; + infoboxParameters_ = infoboxParameters; + // build the pushInfobox string + if ((identifiers != null) && (!identifiers.isEmpty())) { + StringBuffer identifiersSB = new StringBuffer(); + int identifiersNum = identifiers.size(); + int i = 1; + Iterator it = identifiers.iterator(); + while (it.hasNext()) { + identifiersSB.append((String)it.next()); + if (i != identifiersNum) { + identifiersSB.append(","); + } + i++; + } + pushInfobox_ = identifiersSB.toString(); + } else { + pushInfobox_ = ""; + } + } + + /** + * Returns the (comma separated) identifiers of the infoboxes configured for the actual + * online application. + * + * @see #pushInfobox_ + * + * @return The (comma separated) identifiers of the infoboxes configured for the actual + * online application. + */ + public String getPushInfobox() { + return pushInfobox_; + } + + /** + * Sets the {@link #pushInfobox_} string. + * + * @param pushInfobox The pushInfobox string to be set. + */ + public void setPushInfobox(String pushInfobox) { + pushInfobox_ = pushInfobox; + } + + /** + * Returns map of {@link VerifyInfoboxParameter} objects. + * Each of these objects contains parameters that maybe needed for validating an + * infobox. + * + * @return The map of {@link VerifyInfoboxParameter} objects. + */ + public Map getInfoboxParameters() { + return infoboxParameters_; + } + + /** + * Sets the map of {@link VerifyInfoboxParameter} objects. + * + * @see #infoboxParameters_ + * + * @param infoboxParameters The infoboxParameters to set. + */ + public void setInfoboxParameters(Map infoboxParameters) { + infoboxParameters_ = infoboxParameters; + } + + /** + * Returns the identifiers of the supported infoboxes. + * + * @return The identifiers. + */ + public List getIdentifiers() { + return identifiers_; + } + + /** + * Sets the identifiers. + * + * @param identifiers The identifiers to set. + */ + public void setIdentifiers(List identifiers) { + identifiers_ = identifiers; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/OAConfiguration.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/OAConfiguration.java new file mode 100644 index 000000000..d25cb3b2b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/OAConfiguration.java @@ -0,0 +1,173 @@ +package at.gv.egovernment.moa.id.config.proxy; + +import java.util.HashMap; +import java.util.Map; + +/** + * Holds configuration data concerning an online application for use by the MOA-ID Proxy component. + * These include the login type (stateful or stateless), the HTTP authentication type, + * and information needed to add authentication parameters or headers for a URL connection + * to the remote online application. + * @see <code>MOAIDConfiguration-1.1.xsd</code>, element <code>Configuration</code> + * + * @author Stefan Knirsch + * @version $Id$ + */ +public class OAConfiguration { + + /** Constant for an login method */ + public static final String LOGINTYPE_STATEFUL = "stateful"; + /** Constant for an login method */ + public static final String LOGINTYPE_STATELESS = "stateless"; + + /** Constant for an auth method */ + public static final String BASIC_AUTH = "basic"; + /** Constant for an auth method */ + public static final String HEADER_AUTH = "header"; + /** Constant for an auth method */ + public static final String PARAM_AUTH = "param"; + + + /** Constant for binding */ + public static final String BINDUNG_USERNAME = "userName"; + /** Constant for binding */ + public static final String BINDUNG_FULL = "full"; + /** Constant for binding */ + public static final String BINDUNG_NONE = "none"; + /** Constant for binding */ + public static final String BINDUNG_NOMATCH = "noMatch"; + + /** login type: stateful or stateless */ + String loginType; + /** authentication type: basic, header, or param */ + String authType; + /** + * mapping of parameter names to AuthenticationData field names + * in case of authentication type <code>"header-auth"</code> + */ + Map paramAuthMapping; + /** + * mapping of parameter names to AuthenticationData field names + * in case of authentication type <code>"param-auth"</code> + */ + Map headerAuthMapping; + /** mapping for user ID to be used in case of authentication type <code>"basic-auth"</code> */ + String basicAuthUserIDMapping; + /** mapping for password to be used in case of authentication type <code>"basic-auth"</code> */ + String basicAuthPasswordMapping; + /** Binding for basic authentication */ + String binding; + + /** + * Returns the basicAuthPasswordMapping. + * @return String + */ + public String getBasicAuthPasswordMapping() { + return basicAuthPasswordMapping; + } + + /** + * Returns the basicAuthUserIDMapping. + * @return String + */ + public String getBasicAuthUserIDMapping() { + return basicAuthUserIDMapping; + } + + /** + * Returns the headerAuthMapping. + * @return HashMap + */ + public Map getHeaderAuthMapping() { + return headerAuthMapping; + } + + /** + * Returns the loginType. + * @return String + */ + public String getLoginType() { + return loginType; + } + + /** + * Returns the paramAuthMapping. + * @return HashMap + */ + public Map getParamAuthMapping() { + return paramAuthMapping; + } + + /** + * Returns the binding. + * @return String + */ + public String getBinding() { + return binding; + } + + /** + * Sets the basicAuthPasswordMapping. + * @param basicAuthPassword The basicAuthPasswordMapping to set + */ + public void setBasicAuthPasswordMapping(String basicAuthPassword) { + this.basicAuthPasswordMapping = basicAuthPassword; + } + + /** + * Sets the basicAuthUserIDMapping. + * @param basicAuthUserID The basicAuthUserIDMapping to set + */ + public void setBasicAuthUserIDMapping(String basicAuthUserID) { + this.basicAuthUserIDMapping = basicAuthUserID; + } + + /** + * Sets the headerAuthMapping. + * @param headerAuth The headerAuthMapping to set + */ + public void setHeaderAuthMapping(HashMap headerAuth) { + this.headerAuthMapping = headerAuth; + } + + /** + * Sets the loginType. + * @param loginType The loginType to set + */ + public void setLoginType(String loginType) { + this.loginType = loginType; + } + + /** + * Sets the paramAuthMapping. + * @param paramAuth The paramAuthMapping to set + */ + public void setParamAuthMapping(HashMap paramAuth) { + this.paramAuthMapping = paramAuth; + } + + /** + * Returns the authType. + * @return String + */ + public String getAuthType() { + return authType; + } + + /** + * Sets the authType. + * @param authLoginType The authType to set + */ + public void setAuthType(String authLoginType) { + this.authType = authLoginType; + } + + /** + * Sets the binding. + * @param binding The binding to be set. + */ + public void setBinding (String binding) { + this.binding = binding; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/OAProxyParameter.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/OAProxyParameter.java new file mode 100644 index 000000000..83ab73dcd --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/OAProxyParameter.java @@ -0,0 +1,184 @@ +package at.gv.egovernment.moa.id.config.proxy; + +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.id.config.OAParameter; + +/** + * Configuration parameters belonging to an online application, + * to use with the MOA ID Proxy component. + * + * @author Stefan Knirsch + * @version $Id$ + */ +public class OAProxyParameter extends OAParameter { + +// /** +// * public URL prefix of the online application +// */ +// private String publicURLPrefix; + /** + * URL of online application configuration file; + * defaults to relative URL <code>/moaconfig.xml</code> + */ + private String configFileURL; + /** + * implementation of {@link at.gv.egovernment.moa.id.proxy.LoginParameterResolver} interface + * to be used for authenticating the online application; + * defaults to {@link at.gv.egovernment.moa.id.proxy.DefaultLoginParameterResolver} + */ + private String loginParameterResolverImpl; + + /** + * Configuration Parameter of LoginParameterResolver + */ + private String loginParameterResolverConfiguration; + + /** + * implementation of {@link at.gv.egovernment.moa.id.proxy.ConnectionBuilder} interface + * to be used for connecting to the online application; + * defaults to {@link at.gv.egovernment.moa.id.proxy.DefaultConnectionBuilder} + */ + private String connectionBuilderImpl; + /** + * session time out to be used in case of a stateless online application + */ + private int sessionTimeOut; + /** + * parameters regarding the connection from the proxy to the online application + */ + private ConnectionParameter connectionParameter; + /** + * parameters for logging into the online application + */ + private OAConfiguration oaConfiguration; + + + /** + * Returns the configFileURL. + * @return String + */ + public String getConfigFileURL() { + return configFileURL; + } + + /** + * Returns the sessionTimeOut. + * @return int + */ + public int getSessionTimeOut() { + return sessionTimeOut; + } + + /** + * Returns the connectionParameter. + * @return ConnectionParameter + */ + public ConnectionParameter getConnectionParameter() { + return connectionParameter; + } + + /** + * Sets the configFileURL for the proxy. + * @param oaProxyConfigFileURL The configFileURL to set + */ + public void setConfigFileURL(String oaProxyConfigFileURL) { + this.configFileURL = oaProxyConfigFileURL; + } + + /** + * Sets the sessionTimeOut for the proxy. + * @param oaProxySessionTimeOut The sessionTimeOut to set + */ + public void setSessionTimeOut(int oaProxySessionTimeOut) { + this.sessionTimeOut = oaProxySessionTimeOut; + } + + /** + * Sets the connectionParameter for the proxy. + * @param proxyConnectionParameter The connectionParameter to set + */ + public void setConnectionParameter(ConnectionParameter proxyConnectionParameter) { + this.connectionParameter = proxyConnectionParameter; + } + +// /** +// * Returns the publicURLPrefix. +// * @return String +// */ +// public String getPublicURLPrefix() { +// return publicURLPrefix; +// } +// +// /** +// * Sets the publicURLPrefix. +// * @param publicURLPrefix The publicURLPrefix to set +// */ +// public void setPublicURLPrefix(String url) { +// this.publicURLPrefix = url; +// } + + /** + * Returns the connectionBuilderImpl. + * @return String + */ + public String getConnectionBuilderImpl() { + return connectionBuilderImpl; + } + + /** + * Returns the loginParameterResolverImpl. + * @return String + */ + public String getLoginParameterResolverImpl() { + return loginParameterResolverImpl; + } + + /** + * Returns the loginParameterResolverConfiguration. + * @return String + */ + public String getLoginParameterResolverConfiguration() { + return loginParameterResolverConfiguration; + } + + /** + * Sets the connectionBuilderImpl for the proxy. + * @param connectionBuilderImpl The connectionBuilderImpl to set + */ + public void setConnectionBuilderImpl(String connectionBuilderImpl) { + this.connectionBuilderImpl = connectionBuilderImpl; + } + + /** + * Sets the loginParameterResolverImpl for the proxy. + * @param loginParameterResolverImpl The loginParameterResolverImpl to set + */ + public void setLoginParameterResolverImpl(String loginParameterResolverImpl) { + this.loginParameterResolverImpl = loginParameterResolverImpl; + } + + /** + * Sets the loginParameterResolverConfiguration for the proxy. + * @param loginParameterResolverConfiguration The loginParameterResolverImpl to set + */ + public void setLoginParameterResolverConfiguration(String loginParameterResolverConfiguration) { + this.loginParameterResolverConfiguration = loginParameterResolverConfiguration; + } + + /** + * Returns the oaConfiguration. + * @return OAConfiguration + */ + public OAConfiguration getOaConfiguration() { + return oaConfiguration; + } + + /** + * Sets the oaConfiguration. + * @param oaConfiguration The oaConfiguration to set + */ + public void setOaConfiguration(OAConfiguration oaConfiguration) { + this.oaConfiguration = oaConfiguration; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/ProxyConfigurationBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/ProxyConfigurationBuilder.java new file mode 100644 index 000000000..f578ea634 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/ProxyConfigurationBuilder.java @@ -0,0 +1,268 @@ +// Copyright (C) 2002 IAIK +// http://jce.iaik.tugraz.at +// +// Copyright (C) 2007 Stiftung Secure Information and +// Communication Technologies SIC +// http://www.sic.st +// +// All rights reserved. +// +// This source is provided for inspection purposes and recompilation only, +// unless specified differently in a contract with IAIK. This source has to +// be kept in strict confidence and must not be disclosed to any third party +// under any circumstances. Redistribution in source and binary forms, with +// or without modification, are <not> permitted in any case! +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +package at.gv.egovernment.moa.id.config.proxy; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.w3c.dom.traversal.NodeIterator; + +import at.gv.egovernment.moa.id.config.ConfigurationBuilder; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.FileUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Builds the configuration for MOA-ID Proxy. + */ +public class ProxyConfigurationBuilder extends ConfigurationBuilder { + + /** + * Default online application configuration file name + * (used when <code>/OnlineApplication/ProxyComponent@configFileURL</code> is <code>null</code>). + */ + public static final String DEFAULT_OA_CONFIG_FILENAME = "MOAConfig.xml"; + + /** an XPATH-Expression */ + private static final String PROXY_AUTH_XPATH = + ROOT + CONF + "ProxyComponent/" + CONF + "AuthComponent"; + /** an XPATH-Expression */ + protected static final String ROOTOA = "/" + CONF + "Configuration/"; + /** an XPATH-Expression */ + private static final String OA_PROXY_COMPONENT_XPATH = CONF + "ProxyComponent"; + /** an XPATH-Expression */ + private static final String OA_PROXY_COMPONENT_ABSOLUTE_XPATH = ROOT + CONF + "OnlineApplication/" + CONF + "ProxyComponent"; + /** an XPATH-Expression */ + private static final String OA_PROXY_URL_XPATH = CONF + "ProxyComponent/@configFileURL"; + /** an XPATH-Expression */ + private static final String OA_PROXY_SESSION_TIMEOUT_XPATH = CONF + "ProxyComponent/@sessionTimeOut"; + /** an XPATH-Expression */ + private static final String OA_PROXY_LOGIN_PARA_XPATH = CONF + "ProxyComponent/@loginParameterResolverImpl"; + /** an XPATH-Expression */ + private static final String OA_PROXY_LOGIN_PARA_CONF_XPATH = CONF + "ProxyComponent/@loginParameterResolverConfiguration"; + /** an XPATH-Expression */ + private static final String OA_PROXY_CONNECTION_BUILDER_XPATH = CONF + "ProxyComponent/@connectionBuilderImpl"; + /** an XPATH-Expression */ + protected static final String OACONF_LOGIN_TYPE_XPATH = + ROOTOA + CONF + "LoginType"; + /** an XPATH-Expression */ + protected static final String OACONF_BINDING_TYPE_XPATH = + ROOTOA + CONF + "Binding"; + /** an XPATH-Expression */ + protected static final String OACONF_PARAM_AUTH_PARAMETER_XPATH = + ROOTOA + CONF + "ParamAuth/" + CONF + "Parameter"; + /** an XPATH-Expression */ + protected static final String OACONF_USER_ID_XPATH = + ROOTOA + CONF + "BasicAuth/" + CONF + "UserID"; + /** an XPATH-Expression */ + protected static final String OACONF_PASSWORD_XPATH = + ROOTOA + CONF + "BasicAuth/" + CONF + "Password"; + /** an XPATH-Expression */ + protected static final String OACONF_HEADER_AUTH_HEADER_XPATH = + ROOTOA + CONF + "HeaderAuth/" + CONF + "Header"; + + /** + * Creates a new <code>MOAConfigurationProvider</code>. + * + * @param configElem The root element of the MOA-ID configuration. + */ + public ProxyConfigurationBuilder(Element configElem, String rootConfigDir) { + super(configElem, rootConfigDir); + } + + /** + * Method buildOAConfiguration. + * + * Build an {@link OAConfiguration} Object from the given configuration DOM element + * + * @param root + * @return OAConfiguration + * @throws ConfigurationException + */ + public OAConfiguration buildOAConfiguration(Element root) throws ConfigurationException{ + + OAConfiguration oaConfiguration = new OAConfiguration(); + + //The LoginType hast to be "stateless" or "stateful" to be valid + + oaConfiguration.setLoginType( + XPathUtils.getElementValue(root, OACONF_LOGIN_TYPE_XPATH, null)); + + oaConfiguration.setBinding( + XPathUtils.getElementValue(root, OACONF_BINDING_TYPE_XPATH, OAConfiguration.BINDUNG_FULL)); + + //Try to build the Parameter Auth Parameters + NodeIterator paramAuthIter = + XPathUtils.selectNodeIterator( + root, + OACONF_PARAM_AUTH_PARAMETER_XPATH); + Element paramAuthElem; + HashMap paramAuthMap = new HashMap(); + while ((paramAuthElem = (Element) paramAuthIter.nextNode()) != null) { + String name = XPathUtils.getAttributeValue(paramAuthElem, "@Name", null); + String value = XPathUtils.getAttributeValue(paramAuthElem, "@Value", null); + if (paramAuthMap.containsKey(name)) + throw new ConfigurationException("config.06", new Object[]{"Doppelter Wert für Parameter per HeaderAuthentication"}); + paramAuthMap.put(name, value); + } + oaConfiguration.setParamAuthMapping(paramAuthMap); + // Try to build the BasicAuthParameters + oaConfiguration.setBasicAuthUserIDMapping( + XPathUtils.getElementValue(root, OACONF_USER_ID_XPATH, null)); + oaConfiguration.setBasicAuthPasswordMapping( + XPathUtils.getElementValue(root, OACONF_PASSWORD_XPATH, null)); + + //Try to build the Parameter Auth Parameters + NodeIterator headerAuthIter = XPathUtils.selectNodeIterator(root,OACONF_HEADER_AUTH_HEADER_XPATH); + + Element headerAuthElem; + HashMap headerAuthMap = new HashMap(); + while ((headerAuthElem = (Element) headerAuthIter.nextNode()) != null) { + String name = + XPathUtils.getAttributeValue(headerAuthElem, "@Name", null); + String value = + XPathUtils.getAttributeValue(headerAuthElem, "@Value", null); + // Contains Key (Neue Config-Exception: doppelte werte) + if (headerAuthMap.containsKey(name)) + throw new ConfigurationException("config.06", new Object[]{"Doppelter Wert für Parameter per HeaderAuthentication"}); + headerAuthMap.put(name, value); + } + oaConfiguration.setHeaderAuthMapping(headerAuthMap); + + if (paramAuthMap.size() == 0) { + if (oaConfiguration.getBasicAuthUserIDMapping() == null) { + oaConfiguration.setAuthType(OAConfiguration.HEADER_AUTH); + } + else + oaConfiguration.setAuthType(OAConfiguration.BASIC_AUTH); + } + else + oaConfiguration.setAuthType(OAConfiguration.PARAM_AUTH); + + return oaConfiguration; + } + + + /** + * Build an array of OnlineApplication Parameter Beans containing information + * about the proxy component + * @return An OAProxyParameter array containing beans + * with all relevant information for the proxy component of the online + * application + */ + public OAProxyParameter[] buildOnlineApplicationProxyParameters() throws ConfigurationException{ + + List oA_list = new ArrayList(); + NodeList OAIter = XPathUtils.selectNodeList(configElem_, OA_XPATH); + + for (int i = 0; i < OAIter.getLength(); i++) { + Element oAElem = (Element) OAIter.item(i); + + Element proxyComponentElem = (Element) XPathUtils.selectSingleNode(oAElem,OA_PROXY_COMPONENT_XPATH); + if (proxyComponentElem != null) { + OAProxyParameter oap = new OAProxyParameter(); + + oap.setPublicURLPrefix(oAElem.getAttribute("publicURLPrefix")); + oap.setOaType(oAElem.getAttribute("type")); + oap.setConfigFileURL(XPathUtils.getAttributeValue(oAElem, OA_PROXY_URL_XPATH, null)); + oap.setConfigFileURL(FileUtils.makeAbsoluteURL(oap.getConfigFileURL(), rootConfigFileDir_)); + // default session time out: 3600 sec = 1 h + oap.setSessionTimeOut(new Integer(XPathUtils.getAttributeValue(oAElem,OA_PROXY_SESSION_TIMEOUT_XPATH,"3600")).intValue()); + oap.setLoginParameterResolverImpl(XPathUtils.getAttributeValue(oAElem, OA_PROXY_LOGIN_PARA_XPATH, null)); + oap.setLoginParameterResolverConfiguration(XPathUtils.getAttributeValue(oAElem, OA_PROXY_LOGIN_PARA_CONF_XPATH, null)); + oap.setLoginParameterResolverConfiguration(FileUtils.makeAbsoluteURL(oap.getLoginParameterResolverConfiguration(), rootConfigFileDir_)); + oap.setConnectionBuilderImpl(XPathUtils.getAttributeValue(oAElem,OA_PROXY_CONNECTION_BUILDER_XPATH, null)); + + ConnectionParameter conPara = buildConnectionParameter(proxyComponentElem); + oap.setConnectionParameter(conPara); + + OAConfiguration oaConf = buildOAConfiguration(getOAConfigElement(oap)); + oap.setOaConfiguration(oaConf); + + oA_list.add(oap); + } + } + OAProxyParameter[] result = + new OAProxyParameter[oA_list.size()]; + oA_list.toArray(result); + + return result; + + } + + /** + * Reads the configuration file of the online application, and creates a DOM tree from it. + * If <code>/OnlineApplication/ProxyComponent@configFileURL</code> is not given, + * uses default configuration file location. + * + * @param oap configuration data of online application, meant for use by MOA-ID-PROXY + * @return Element DOM tree root element + * @throws ConfigurationException on any exception thrown + */ + private Element getOAConfigElement(OAProxyParameter oap) throws ConfigurationException + { + try { + String configFileURL = oap.getConfigFileURL(); + if (configFileURL == null) { + // use default config file URL, when config file URL is not given + configFileURL = oap.getConnectionParameter().getUrl(); + if (configFileURL.charAt(configFileURL.length() - 1) != '/') + configFileURL += "/"; + configFileURL += DEFAULT_OA_CONFIG_FILENAME; + } + Logger.info("Loading MOA-OA configuration " + configFileURL); + Element configElem = DOMUtils.parseXmlValidating( + new ByteArrayInputStream(FileUtils.readURL(configFileURL))); + return configElem; + } + catch (Throwable t) { + throw new ConfigurationException("config.03", new Object[] {"OAConfiguration"} , t); + } + } + + /** + * Build a bean containing all information about the ProxyComponent + * @return The ConnectionParameter for the Proxy Component + */ + public ConnectionParameter buildAuthComponentConnectionParameter() + { + + Element connectionParameter = (Element) XPathUtils.selectSingleNode(configElem_, PROXY_AUTH_XPATH); + if (connectionParameter==null) return null; + return buildConnectionParameter(connectionParameter); + + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/ProxyConfigurationProvider.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/ProxyConfigurationProvider.java new file mode 100644 index 000000000..572ce5708 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/proxy/ProxyConfigurationProvider.java @@ -0,0 +1,188 @@ +package at.gv.egovernment.moa.id.config.proxy; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.MalformedURLException; + +import org.w3c.dom.Element; + +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.logging.Logger; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.FileUtils; + +/** + * A class providing access to the Proxy Part of the MOA-ID configuration data. + * + * <p>Configuration data is read from an XML file, whose location is given by + * the <code>moa.id.configuration</code> system property.</p> + * <p>This class implements the Singleton pattern. The <code>reload()</code> + * method can be used to update the configuration data. Therefore, it is not + * guaranteed that consecutive calls to <code>getInstance()</code> will return + * the same <code>ProxyConfigurationProvider</code> all the time. During the + * processing of a web service request, the current + * <code>TransactionContext</code> should be used to obtain the + * <code>ProxyConfigurationProvider</code> local to that request.</p> + * + * @author Stefan Knirsch + */ +public class ProxyConfigurationProvider extends ConfigurationProvider { + + /** Singleton instance. <code>null</code>, if none has been created. */ + private static ProxyConfigurationProvider instance; + + + // + // configuration data + // + /** + * connection parameters for connection to MOA ID Auth component + */ + private ConnectionParameter authComponentConnectionParameter; + /** + * configuration parameters for online applications + */ + private OAProxyParameter[] onlineApplicationProxyParameter; + + /** + * Return the single instance of configuration data. + * + * @return ProxyConfigurationProvider The current configuration data. + * @throws ConfigurationException + */ + public static synchronized ProxyConfigurationProvider getInstance() + throws ConfigurationException { + + if (instance == null) { + reload(); + } + return instance; + } + + /** + * Reload the configuration data and set it if successful. + * + * @return ProxyConfigurationProvider The loaded configuration data. + * @throws ConfigurationException Failure to load the configuration data. + */ + public static synchronized ProxyConfigurationProvider reload() + throws ConfigurationException { + String fileName = System.getProperty(CONFIG_PROPERTY_NAME); + if (fileName == null) { + throw new ConfigurationException("config.01", null); + } + Logger.info("Loading MOA-ID-PROXY configuration " + fileName); + + instance = new ProxyConfigurationProvider(fileName); + return instance; + } + + /** + * Constructor for ProxyConfigurationProvider. + */ + public ProxyConfigurationProvider(String fileName) + throws ConfigurationException { + + load(fileName); + } + + /** + * Load the configuration data from XML file with the given name and build + * the internal data structures representing the MOA configuration. + * + * @param fileName The name of the XML file to load. + * @throws ConfigurationException The MOA configuration could not be + * read/built. + */ + private void load(String fileName) throws ConfigurationException { + FileInputStream stream = null; + Element configElem; + ProxyConfigurationBuilder builder; + + try { + // load the main config file + stream = new FileInputStream(fileName); + configElem = DOMUtils.parseXmlValidating(stream); + } + catch (Throwable t) { + throw new ConfigurationException("config.03", null, t); + } + finally { + try { + if (stream != null) { + stream.close(); + } + } + catch (IOException e) { + } + } + try { + // determine the directory of the root config file + rootConfigFileDir = new File(fileName).getParent(); + try { + rootConfigFileDir = new File(rootConfigFileDir).toURL().toString(); + } catch (MalformedURLException t) { + throw new ConfigurationException("config.03", null, t); + } + + // build the internal datastructures + builder = new ProxyConfigurationBuilder(configElem, rootConfigFileDir); + authComponentConnectionParameter = builder.buildAuthComponentConnectionParameter(); + + onlineApplicationProxyParameter = builder.buildOnlineApplicationProxyParameters(); + for(int i = 0; i < onlineApplicationProxyParameter.length; i++) { + onlineApplicationProxyParameter[i].setConfigFileURL(FileUtils.makeAbsoluteURL(onlineApplicationProxyParameter[i].getConfigFileURL(), rootConfigFileDir)); + } + + genericConfiguration = builder.buildGenericConfiguration(); + defaultChainingMode = builder.getDefaultChainingMode(); + chainingModes = builder.buildChainingModes(); + trustedCACertificates = builder.getTrustedCACertificates(); + trustedCACertificates = FileUtils.makeAbsoluteURL(trustedCACertificates, rootConfigFileDir); + + } + catch (Throwable t) { + throw new ConfigurationException("config.02", null, t); + } + } + + /** + * Return a bean containing all information about the ProxyComponent + * @return The ConnectionParameter for the Proxy Component + */ + public ConnectionParameter getAuthComponentConnectionParameter() { + return authComponentConnectionParameter; + } + + /** + * Build an array of OnlineApplication Parameter Beans containing all + * information about the proxy component of the online application + * @return An OAProxyParameter array containing beans + * with all relevant information for the proxy component of the online + * application + */ + public OAProxyParameter[] getOnlineApplicationParameters() { + return onlineApplicationProxyParameter; + } + /** + * Provides configuration information regarding the online application behind + * the given URL, relevant to the MOA-ID Proxy component. + * + * @param oaURL URL requested for an online application + * @return an <code>OAProxyParameter</code>, or <code>null</code> + * if none is applicable + */ + public OAProxyParameter getOnlineApplicationParameter(String oaURL) { + OAProxyParameter[] oaParams = getOnlineApplicationParameters(); + for (int i = 0; i < oaParams.length; i++) { + OAProxyParameter oaParam = oaParams[i]; + if (oaURL.startsWith(oaParam.getPublicURLPrefix())) + return oaParam; + } + return null; + } + +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/AuthenticationData.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/AuthenticationData.java new file mode 100644 index 000000000..4fc35c1e6 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/AuthenticationData.java @@ -0,0 +1,394 @@ +package at.gv.egovernment.moa.id.data; + +import java.util.Date; + +/** + * Encapsulates authentication data contained in a <code><saml:Assertion></code>. + * + * @author Paul Ivancsics + * @version $Id$ + */ + +public class AuthenticationData { + /** + * major version number of the SAML assertion + */ + private int majorVersion; + /** + * minor version number of the SAML assertion + */ + private int minorVersion; + /** + * identifier for this assertion + */ + private String assertionID; + /** + * URL of the MOA-ID Auth component issueing this assertion + */ + private String issuer; + /** + * time instant of issue of this assertion + */ + private String issueInstant; + /** + * user identification value (Stammzahl); <code>null</code>, + * if the authentication module is configured not to return this data + */ + private String identificationValue; + /** + * user identification type + */ + private String identificationType; + /** + * application specific user identifier (bPK) + */ + private String bPK; + /** + * private sector-specific personal identifier (wbPK) + */ + private String wbPK; + /** + * given name of the user + */ + private String givenName; + /** + * family name of the user + */ + private String familyName; + /** + * date of birth of the user + */ + private String dateOfBirth; + /** + * says whether the certificate is a qualified certificate or not + */ + private boolean qualifiedCertificate; + /** + * says whether the certificate is a public authority or not + */ + private boolean publicAuthority; + /** + * public authority code (Behördenkennzeichen - BKZ) + */ + private String publicAuthorityCode; + /** + * The base64 encoded signer certificate. + */ + private String signerCertificate; + /** + * URL of the BKU + */ + private String bkuURL; + /** + * the corresponding <code>lt;saml:Assertion></code> + */ + private String samlAssertion; + /** + * creation timestamp + */ + Date timestamp; + + /** + * Constructor for AuthenticationData. + */ + public AuthenticationData() { + timestamp = new Date(); + } + + /** + * Returns the minorVersion. + * @return int + */ + public int getMinorVersion() { + return minorVersion; + } + + /** + * Returns the publicAuthority. + * @return boolean + */ + public boolean isPublicAuthority() { + return publicAuthority; + } + + /** + * Returns the publicAuthorityCode. + * @return String + */ + public String getPublicAuthorityCode() { + return publicAuthorityCode; + } + + /** + * Returns the qualifiedCertificate. + * @return boolean + */ + public boolean isQualifiedCertificate() { + return qualifiedCertificate; + } + + /** + * Returns the bPK. + * @return String + */ + public String getBPK() { + return bPK; + } + + /** + * Returns the wbPK. + * @return String the wbPK. + */ + public String getWBPK() { + return wbPK; + } + + /** + * Sets the minorVersion. + * @param minorVersion The minorVersion to set + */ + public void setMinorVersion(int minorVersion) { + this.minorVersion = minorVersion; + } + + /** + * Sets the publicAuthority. + * @param publicAuthority The publicAuthority to set + */ + public void setPublicAuthority(boolean publicAuthority) { + this.publicAuthority = publicAuthority; + } + + /** + * Sets the publicAuthorityCode. + * @param publicAuthorityIdentification The publicAuthorityCode to set + */ + public void setPublicAuthorityCode(String publicAuthorityIdentification) { + this.publicAuthorityCode = publicAuthorityIdentification; + } + + /** + * Sets the qualifiedCertificate. + * @param qualifiedCertificate The qualifiedCertificate to set + */ + public void setQualifiedCertificate(boolean qualifiedCertificate) { + this.qualifiedCertificate = qualifiedCertificate; + } + + /** + * Sets the bPK. + * @param bPK The bPK to set + */ + public void setBPK(String bPK) { + this.bPK = bPK; + } + + /** + * Sets the wbPK. + * @param wbPK The wbPK to set + */ + public void setWBPK(String wbPK) { + this.wbPK = wbPK; + } + + /** + * Returns the assertionID. + * @return String + */ + public String getAssertionID() { + return assertionID; + } + + /** + * Returns the dateOfBirth. + * @return String + */ + 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. + * @return String + */ + public String getIdentificationValue() { + return identificationValue; + } + + /** + * Returns the identificationType + * @return String + */ + public String getIdentificationType() { + return identificationType; + } + + /** + * Returns the issueInstant. + * @return String + */ + public String getIssueInstant() { + return issueInstant; + } + + /** + * Returns the issuer. + * @return String + */ + public String getIssuer() { + return issuer; + } + + /** + * Returns the majorVersion. + * @return int + */ + public int getMajorVersion() { + return majorVersion; + } + + /** + * Returns the BKU URL. + * @return String + */ + public String getBkuURL() { + return bkuURL; + } + + /** + * Returns the signer certificate. + * @return String + */ + public String getSignerCertificate() { + return signerCertificate; + } + + /** + * Sets the assertionID. + * @param assertionID The assertionID to set + */ + public void setAssertionID(String assertionID) { + this.assertionID = assertionID; + } + + /** + * 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. + * @param identificationValue The identificationValue to set + */ + public void setIdentificationValue(String identificationValue) { + this.identificationValue = identificationValue; + } + + /** + * Sets the identificationType. + * @param identificationType The identificationType to set + */ + public void setIdentificationType(String identificationType) { + this.identificationType = identificationType; + } + + /** + * Sets the issueInstant. + * @param issueInstant The issueInstant to set + */ + public void setIssueInstant(String issueInstant) { + this.issueInstant = issueInstant; + } + + /** + * Sets the issuer. + * @param issuer The issuer to set + */ + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + /** + * Sets the majorVersion. + * @param majorVersion The majorVersion to set + */ + public void setMajorVersion(int majorVersion) { + this.majorVersion = majorVersion; + } + + /** + * Sets the bkuURL + * @param url The BKU URL to set + */ + public void setBkuURL(String url) { + this.bkuURL = url; + } + + /** + * Sets the signer certificate + * @param signerCertificate The signer certificate + */ + public void setSignerCertificate(String signerCertificate) { + this.signerCertificate = signerCertificate; + } + + /** + * Returns the samlAssertion. + * @return String + */ + public String getSamlAssertion() { + return samlAssertion; + } + + /** + * Sets the samlAssertion. + * @param samlAssertion The samlAssertion to set + */ + public void setSamlAssertion(String samlAssertion) { + this.samlAssertion = samlAssertion; + } + + /** + * Returns the timestamp. + * @return Date + */ + public Date getTimestamp() { + return timestamp; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/Cookie.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/Cookie.java new file mode 100644 index 000000000..6ed133c5a --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/Cookie.java @@ -0,0 +1,119 @@ +package at.gv.egovernment.moa.id.data; +import java.util.HashMap; +import java.util.Iterator; +import java.util.StringTokenizer; + +import at.gv.egovernment.moa.logging.Logger; + +/** + * The Cookie-class provides methods to save and return cookies for + * each single session + * + * @author Stefan Knirsch + * @version $Id$ + * + */ +public class Cookie { + /** A HahsMap containing all our cookies */ + HashMap cookies = new HashMap(); + /** A HashMap to temporarely store 'Set-Cookie' values from the OnlineApplication + * to send them back to the client/browser as soon as possible */ + HashMap cookies401 = new HashMap(); + + /** + * Adds a Cookie from a response with response-code 401 to the cookie-pool + * for sending it back to the browser / client + * @param cookieString The complete 'Set-Cookie' - String + */ + public void add401(String cookieString) + { + cookies401.put(getKey(cookieString),cookieString); + } + + /** + * Get the HashMap containing all cookies to be sent to the browser / client + * @return HashMap with all cookies + */ + public HashMap get401() + { + return cookies401; + } + + /** + * Clear the 401 cookie-pool + */ + public void clear401() + { + cookies401.clear(); + } + + /** + * Set a cookie that comes from the Online-Application + * and save it in our "normal" cookie-pool + * @param value The complete "Set-Cookie" - String from the Online-Application + */ + public void setCookie(String value) { + cookies.put(getKey(value), getValue(value)); + } + + /** + * Method saveOldCookies. + * @param value The complete "Set-Cookie" - String from the Online-Application + */ + public void saveOldCookies(String value) { + StringTokenizer st = new StringTokenizer(value,";"); + while (st.hasMoreTokens()) + { + // We have to trim because the Tokenizer returns cookies including spaces at the beginning + StringTokenizer st2 = new StringTokenizer(st.nextToken().trim(),"="); + String cookieKey = st2.nextToken().trim(); + if (st2.hasMoreTokens()) + { + String cookieValue = st2.nextToken().trim(); + if (!cookies.containsKey(cookieKey)) + cookies.put(cookieKey , cookieValue); + } + } + Logger.debug("Found these cookies: " + getCookies()); + } + + /** + * Get a String containing all cookies saved in that session seperated by '; ' + * to be sent back to the Online-Application + * @return String containing all cookies saved in that session seperated by '; ' + */ + public String getCookies() { + String result = ""; + if (cookies.size()==0) + return null; + Iterator i = cookies.keySet().iterator(); + while (i.hasNext()) { + String key = (String) i.next(); + result += key + "=" + (String)cookies.get(key) + "; "; + } + return result.substring(0, result.length() - 2); + } + + /** + * Returns the key of a key-value-pair of a cookie + * getKey("CookieA=1234") returns CookieA + * @param String the complete "Set-cookie" String containing a key-value-pair of a cookie + * @return String the key of a key-value-pair of a cookie + */ + private String getKey(String input) { + return input.substring(0, input.indexOf("=")); + } + + /** + * Returns the value of a key-value-pair of a cookie + * getKey("CookieA=1234") returns 1234 + * @param String the complete "Set-cookie" String containing a key-value-pair of a cookie + * @return String the value of a key-value-pair of a cookie + */ + private String getValue(String input) { + if (input.indexOf(";") == -1) + return input.substring(input.indexOf("=") + 1, input.getBytes().length); + return input.substring(input.indexOf("=") + 1, input.indexOf(";")); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/CookieManager.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/CookieManager.java new file mode 100644 index 000000000..1762a52b7 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/CookieManager.java @@ -0,0 +1,114 @@ +package at.gv.egovernment.moa.id.data; + +import java.util.HashMap; + +/** + * The CookieManager is a singleton to manage a Cookie-Object for + * each session + * @author Stefan Knirsch + * @version $Id$ + * + */ +public class CookieManager { + /** the singleton instance of the CookieManager */ + private static CookieManager instance; + /** a HashMap to bind a Cookie-object to every single session*/ + private static HashMap cookies = new HashMap(); + + /** + * Create a singleton of the CookieManager + * @return CookieManager + */ + public static CookieManager getInstance() + { + if(instance==null) instance=new CookieManager(); + return instance; + } + + /** + * Save a cookie to a specified session-id + * @param id The session id + * @param cookie_string The complete 'Set-Cookie' String from the OnlineApplication + */ + public void saveCookie(String id, String cookie_string) + { + getCookieWithID(id).setCookie(cookie_string); + } + + /** + * Method saveOldCookies. + * @param id + * @param cookie_string + */ + public void saveOldCookies(String id,String cookie_string) + { + getCookieWithID(id).saveOldCookies(cookie_string); + } + + /** + * Get a Cookie-Object for a specified session-id + * @param id The session id + * @return Cookie object containing all saved cookies for this session + */ + public Cookie getCookieWithID(String id) + { + Cookie c = null; + if(cookies.containsKey(id)) + c = (Cookie)cookies.get(id); + else + { + c = new Cookie(); + cookies.put(id,c); + } + return c; + } + + + /** + * Get a String containing all cookies of a specified session-id + * saved in that session seperated by '; ' to be sent back to + * the Online-Application + * @param id the session-id + * @return String containing all cookies saved in that session seperated by '; ' + */ + public String getCookie(String id) + { + Cookie result = (Cookie)cookies.get((String)id); + if (result==null) + return null; + return result.getCookies(); + + } + + /** + * Adds a Cookie for a special session from a response with + * response-code 401 to the cookie-pool for sending it back + * to the browser / client + * @param id The session-id + * @param value The complete 'Set-Cookie' - String + */ + public void add401(String id,String value) + { + getCookieWithID(id).add401(value); + } + + /** + * Clear the 401 cookie-pool of a session + * @param id the session-id + */ + public void clear401(String id) + { + getCookieWithID(id).clear401(); + } + + /** + * Get the HashMap containing all cookies of a session to be sent to the browser / client + * @param id the session-id + * @return HashMap with all cookies + */ + public HashMap get401(String id) + { + return getCookieWithID(id).get401(); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/IssuerAndSerial.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/IssuerAndSerial.java new file mode 100644 index 000000000..a47dd8b29 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/IssuerAndSerial.java @@ -0,0 +1,111 @@ +package at.gv.egovernment.moa.id.data; + +import java.math.BigInteger; +import java.security.Principal; + +import iaik.asn1.structures.Name; +import iaik.utils.RFC2253NameParser; +import iaik.utils.RFC2253NameParserException; + +/** + * A class containing the issuer and serial number of a certificate, which can + * be used to uniquely identify the certificate. + * + * @author Patrick Peck + * @version $Id$ + */ +public class IssuerAndSerial { + /** store the issuer as String*/ + private String issuerDN; + /** store the serial as BigInteger*/ + private BigInteger serial; + + /** + * Create an <code>IssuerAndSerial</code> object. + * + * The name of the issuer is converted to RFC2253. If it cannot be parsed, the + * DN contained in the <code>issuer</code> is set. + * + * @param issuer The isser of a certificate. + * @param serial The serial number of the certificate. + */ + public IssuerAndSerial(Principal issuer, BigInteger serial) { + RFC2253NameParser parser = new RFC2253NameParser(issuer.getName()); + + try { + this.issuerDN = ((Name) parser.parse()).getRFC2253String(); + } catch (RFC2253NameParserException e) { + this.issuerDN = issuer.getName(); + } + this.serial = serial; + } + + /** + * Create an <code>IssuerAndSerial</code> object. + * + * @param issuerDN The issuer distinguished name. Should be an RFC2253 name. + * @param serial The serial number of the certificate. + */ + public IssuerAndSerial(String issuerDN, BigInteger serial) { + this.issuerDN = issuerDN; + this.serial = serial; + } + + /** + * Return the issuer DN in RFC2253 format. + * + * @return The issuer part of this object. + */ + public String getIssuerDN() { + return issuerDN; + } + + /** + * Return the serial number. + * + * @return The serial number of this object. + */ + public BigInteger getSerial() { + return serial; + } + + /** + * Compare this <code>IssuerAndSerial</code> to another object. + * + * @return <code>true</code>, if <code>other</code> is an + * <code>IssuerAndSerial</code> object and the <code>issuer</code> and + * <code>serial</code> fields are both equal. <code>false</code> otherwise. + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object other) { + if (other instanceof IssuerAndSerial) { + IssuerAndSerial ias = (IssuerAndSerial) other; + return getIssuerDN().equals(ias.getIssuerDN()) + && getSerial().equals(ias.getSerial()); + } + return false; + } + + /** + * Return the hash code of this <code>IssuerAndSerial</code>. + * + * @return The hash code of this <code>IssuerAndSerial</code>. + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return issuerDN.hashCode() ^ serial.hashCode(); + } + + /** + * Return a <code>String</code> representation of this + * <code>IssuerAndSerial</code> object. + * + * @return The <code>String</code> representation. + * @see java.lang.Object#toString() + */ + public String toString() { + return ("(IssuerAndSerial - Issuer<" + getIssuerDN()) + + ("> Serial<" + serial.toString() + ">)"); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/SAMLStatus.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/SAMLStatus.java new file mode 100644 index 000000000..ed61827b6 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/data/SAMLStatus.java @@ -0,0 +1,59 @@ +package at.gv.egovernment.moa.id.data; + +/** + * Data contained in a <code><samlp:Status></code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class SAMLStatus { + + /** main status code */ + private String statusCode; + /** sub status code */ + private String subStatusCode; + /** status message */ + private String statusMessage; + + /** + * @return status code + */ + public String getStatusCode() { + return statusCode; + } + + /** + * @return status message + */ + public String getStatusMessage() { + return statusMessage; + } + + /** + * @return enclosed sub-status code + */ + public String getSubStatusCode() { + return subStatusCode; + } + + /** + * @param string the status code + */ + public void setStatusCode(String string) { + statusCode = string; + } + + /** + * @param string the status message + */ + public void setStatusMessage(String string) { + statusMessage = string; + } + + /** + * @param string the enclosed sub-status code + */ + public void setSubStatusCode(String string) { + subStatusCode = string; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/CertStoreConfigurationImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/CertStoreConfigurationImpl.java new file mode 100644 index 000000000..c338e96fc --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/CertStoreConfigurationImpl.java @@ -0,0 +1,96 @@ +package at.gv.egovernment.moa.id.iaik.config; + +import java.io.File; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConfigurationProvider; +import at.gv.egovernment.moa.id.iaik.servertools.observer.ObservableImpl; +import at.gv.egovernment.moa.util.FileUtils; +import iaik.pki.store.certstore.CertStoreConfiguration; +import iaik.pki.store.certstore.CertStoreParameters; +import iaik.pki.store.certstore.CertStoreTypes; +import iaik.pki.store.certstore.directory.DirectoryCertStoreParameters; + +/** + * Implementation of interface needed to initialize an IAIK JSSE <code>TrustManager</code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class CertStoreConfigurationImpl extends ObservableImpl + implements CertStoreConfiguration, DirectoryCertStoreParameters { + /** identifies the rootDirectory */ + private String rootDirectory; + /** ConfigurationProvider */ + private ConfigurationProvider conf; + /** Array for storing all CertStoreParameters */ + private CertStoreParameters[] parameters; + + /** + * Create a new <code>CertStoreConfigurationImpl</code>. + * + * @param conf The MOA configuration from which the configuration data is + * @throws ConfigurationException an any config-error + * being read. + */ + public CertStoreConfigurationImpl(ConfigurationProvider conf) throws ConfigurationException { + this.conf=conf; + String paramName = ConfigurationProvider.DIRECTORY_CERTSTORE_PARAMETER_PROPERTY; + String certStoreRootDirParam = conf.getGenericConfigurationParameter(paramName); + if (certStoreRootDirParam == null) + throw new ConfigurationException( + "config.08", new Object[] {paramName}); + + rootDirectory = FileUtils.makeAbsoluteURL(certStoreRootDirParam, conf.getRootConfigFileDir()); + if(rootDirectory.startsWith("file:")) rootDirectory = rootDirectory.substring(6); + File f = new File(rootDirectory); + if (!f.isDirectory()) + throw new ConfigurationException( + "config.05", new Object[] {paramName}); + + parameters = new CertStoreParameters[] { this }; + } + + /** + * @see iaik.pki.store.certstore.CertStoreConfiguration#getParameters() + */ + public CertStoreParameters[] getParameters() { + return parameters; + } + + /** + * @see iaik.pki.store.certstore.directory.DirectoryCertStoreParameters#getRootDirectory() + */ + public String getRootDirectory() { + return rootDirectory; + } + + /** + * @see iaik.pki.store.certstore.directory.DirectoryCertStoreParameters#createNew() + */ + public boolean createNew() { + return false; + } + + /** + * @see iaik.pki.store.certstore.CertStoreParameters#getId() + */ + public String getId() { + return "MOA ID Directory CertStore"; + } + + /** + * @see iaik.pki.store.certstore.CertStoreParameters#isReadOnly() + */ + public boolean isReadOnly() { + return false; + } + + /** + * @return <code>CertStoreTypes.DIRECTORY</code> + * @see iaik.pki.store.certstore.CertStoreParameters#getType() + */ + public String getType() { + return CertStoreTypes.DIRECTORY; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/LoggerConfigImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/LoggerConfigImpl.java new file mode 100644 index 000000000..3cd02a2b5 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/LoggerConfigImpl.java @@ -0,0 +1,51 @@ +package at.gv.egovernment.moa.id.iaik.config; + +import iaik.logging.LogConfigurationException; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +/** + * Implementation of interface <needed to initialize an IAIK JSSE <code>TrustManager</code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class LoggerConfigImpl implements iaik.logging.LoggerConfig { + + /** logging properties **/ + private Properties loggingProperties; + + /** + * Constructor + */ + public LoggerConfigImpl(String propertyFileURL) throws IOException { + InputStream in = new URL(propertyFileURL).openStream(); + loggingProperties = new Properties(); + loggingProperties.load(in); + in.close(); + } + + /** + * @see iaik.logging.LoggerConfig#getFactory() + */ + public String getFactory() { + return "iaik.logging.impl.Log4jFactory"; + } + + /** + * @see iaik.logging.LoggerConfig#getProperties() + */ + public Properties getProperties() throws LogConfigurationException { + return loggingProperties; + } + + /** + * @see iaik.logging.LoggerConfig#getNodeId() + */ + public String getNodeId() { + return "iaik"; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/PKIConfigurationImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/PKIConfigurationImpl.java new file mode 100644 index 000000000..b41de3c44 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/PKIConfigurationImpl.java @@ -0,0 +1,66 @@ +package at.gv.egovernment.moa.id.iaik.config; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConfigurationProvider; +import iaik.pki.PKIConfiguration; +import iaik.pki.pathvalidation.ValidationConfiguration; +import iaik.pki.revocation.RevocationConfiguration; +import iaik.pki.store.certstore.CertStoreConfiguration; +import iaik.pki.store.revocation.archive.ArchiveConfiguration; + +/** + * Implementation of interface <code>PKIConfiguration</code> needed to + * initialize an IAIK JSSE <code>TrustManager</code> + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class PKIConfigurationImpl implements PKIConfiguration { + /** The configuration for the CertStore */ + private CertStoreConfiguration certStoreConfiguration; + /** The configuration for the RevocationChecks */ + private RevocationConfiguration revocationConfiguration; + /** The configuration for the Validation */ + private ValidationConfiguration validationConfiguration; + + /** + * Constructor + * @param conf the Configuration for the PKIConfig + * @throws ConfigurationException for any config error + */ + public PKIConfigurationImpl(ConfigurationProvider conf) throws ConfigurationException { + + certStoreConfiguration = new CertStoreConfigurationImpl(conf); + revocationConfiguration = new RevocationConfigurationImpl(); + validationConfiguration = new ValidationConfigurationImpl(conf); + } + + /** + * @see iaik.pki.PKIConfiguration#getCertStoreConfiguration() + */ + public CertStoreConfiguration getCertStoreConfiguration() { + return certStoreConfiguration; + } + + /** + * @see iaik.pki.PKIConfiguration#getRevocationConfiguration() + */ + public RevocationConfiguration getRevocationConfiguration() { + return revocationConfiguration; + } + + /** + * @see iaik.pki.PKIConfiguration#getArchiveConfiguration() + */ + public ArchiveConfiguration getArchiveConfiguration() { + return null; + } + + /** + * @see iaik.pki.PKIConfiguration#getValidationConfiguration() + */ + public ValidationConfiguration getValidationConfiguration() { + return validationConfiguration; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/RevocationConfigurationImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/RevocationConfigurationImpl.java new file mode 100644 index 000000000..fff75775d --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/RevocationConfigurationImpl.java @@ -0,0 +1,36 @@ +package at.gv.egovernment.moa.id.iaik.config; + +import iaik.pki.revocation.RevocationConfiguration; + +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Date; +import java.util.Set; + +import at.gv.egovernment.moa.id.iaik.servertools.observer.ObservableImpl; + +/** + * Implementation of interface needed to initialize an IAIK JSSE <code>TrustManager</code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class RevocationConfigurationImpl extends ObservableImpl implements RevocationConfiguration { + + /** + * @see iaik.pki.revocation.RevocationConfiguration#getAlternativeDistributionPoints(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.util.Date) + */ + public Set getAlternativeDistributionPoints( + X509Certificate arg0, + X509Certificate arg1, + Date arg2) { + return Collections.EMPTY_SET; + } + + /** + * @see iaik.pki.revocation.RevocationConfiguration#archiveRevocationInfo(java.lang.String, java.lang.String) + */ + public boolean archiveRevocationInfo(String arg0, String arg1) { + return false; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/ValidationConfigurationImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/ValidationConfigurationImpl.java new file mode 100644 index 000000000..c500e2e8e --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/config/ValidationConfigurationImpl.java @@ -0,0 +1,51 @@ +package at.gv.egovernment.moa.id.iaik.config; + +import iaik.pki.pathvalidation.ValidationConfiguration; + +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; + +import at.gv.egovernment.moa.id.config.ConfigurationProvider; +import at.gv.egovernment.moa.id.iaik.servertools.observer.ObservableImpl; + +/** + * Implementation of interface needed to initialize an IAIK JSSE <code>TrustManager</code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class ValidationConfigurationImpl extends ObservableImpl + implements ValidationConfiguration { + /** The ConfigurationProvider for the validation*/ + private ConfigurationProvider conf; + + /** + * Constructor + * @param conf with the configuration + */ + public ValidationConfigurationImpl(ConfigurationProvider conf) { + this.conf = conf; + } + + /** + * @see iaik.pki.pathvalidation.ValidationConfiguration#getChainingMode(java.security.cert.X509Certificate) + */ + public String getChainingMode(X509Certificate trustAnchor) { + String chainingMode = conf.getChainingMode(trustAnchor); + return chainingMode; + } + + /** + * @see iaik.pki.pathvalidation.ValidationConfiguration#getPublicKeyParamsAsSpec(java.security.cert.X509Certificate) + */ + public AlgorithmParameterSpec getPublicKeyParamsAsSpec(X509Certificate arg0) { + return null; + } + + /** + * @see iaik.pki.pathvalidation.ValidationConfiguration#getPublicKeyParamsAsCert(java.security.cert.X509Certificate) + */ + public X509Certificate getPublicKeyParamsAsCert(X509Certificate arg0) { + return null; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/pki/PKIProfileImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/pki/PKIProfileImpl.java new file mode 100644 index 000000000..3c37706e1 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/pki/PKIProfileImpl.java @@ -0,0 +1,186 @@ +package at.gv.egovernment.moa.id.iaik.pki; + +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Set; + +import iaik.pki.PKIProfile; +import iaik.pki.pathvalidation.ValidationProfile; +import iaik.pki.revocation.RevocationProfile; +import iaik.pki.revocation.RevocationSourceTypes; +import iaik.pki.store.truststore.TrustStoreProfile; +import iaik.pki.store.truststore.TrustStoreTypes; + +import at.gv.egovernment.moa.id.iaik.servertools.observer.ObservableImpl; + +/** + * Implementation of the <code>PKIProfile</code> interface and subinterfaces + * providing information needed for certificate path validation. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class PKIProfileImpl extends ObservableImpl + implements PKIProfile, RevocationProfile, TrustStoreProfile, ValidationProfile { + + /** + * URI to the truststore + */ + private String trustStoreURI; + + /** + * revocation checking; + */ + private boolean revocationChecking; + + /** + * The trust profile identifier. + */ + private String id; + + + /** + * Create a new <code>PKIProfileImpl</code>. + * + * @param trustStoreURI trust store URI + */ + public PKIProfileImpl(String trustStoreURI, boolean revocationChecking) { + this.trustStoreURI = trustStoreURI; + this.revocationChecking = revocationChecking; + String id = String.valueOf(System.currentTimeMillis()); + setId("id-" + id); + } + + /** + * @see iaik.pki.PKIProfile#autoAddCertificates() + */ + public boolean autoAddCertificates() { + return true; + } + + /** + * @see iaik.pki.PKIProfile#getRevocationProfile() + */ + public RevocationProfile getRevocationProfile() { + return this; + } + + /** + * @see iaik.pki.PKIProfile#getTrustStoreProfile() + */ + public TrustStoreProfile getTrustStoreProfile() { + return this; + } + + /** + * @see iaik.pki.PKIProfile#getValidationProfile() + */ + public ValidationProfile getValidationProfile() { + return this; + } + + /** + * @see iaik.pki.PKIProfile#useAuthorityInfoAccess() + */ + public boolean useAuthorityInfoAccess() { + return true; + } + + /** + * @see iaik.pki.revocation.RevocationProfile#getMaxRevocationAge(java.lang.String) + */ + public long getMaxRevocationAge(String arg0) { + return 0; + } + + /** + * @see iaik.pki.revocation.RevocationProfile#getOCSPRequestHashAlgorithm() + */ + public String getOCSPRequestHashAlgorithm() { + return null; + } + + /** + * @see iaik.pki.revocation.RevocationProfile#getPreferredServiceOrder(java.security.cert.X509Certificate) + */ + public String[] getPreferredServiceOrder(X509Certificate arg0) { + return new String[] {RevocationSourceTypes.CRL}; + } + + /** + * @see iaik.pki.store.truststore.TrustStoreProfile#getType() + */ + public String getType() { + return TrustStoreTypes.DIRECTORY; + } + + /** + * @see iaik.pki.store.truststore.TrustStoreProfile#getURI() + */ + public String getURI() { + return trustStoreURI; + } + + /** + * @see iaik.pki.pathvalidation.ValidationProfile#getInitialAnyPolicyInhibit() + */ + public boolean getInitialAnyPolicyInhibit() { + return false; + } + + /** + * @see iaik.pki.pathvalidation.ValidationProfile#getInitialExplicitPolicy() + */ + public boolean getInitialExplicitPolicy() { + return false; + } + + /** + * @see iaik.pki.pathvalidation.ValidationProfile#getInitialPolicyMappingInhibit() + */ + public boolean getInitialPolicyMappingInhibit() { + return false; + } + + /** + * @see iaik.pki.pathvalidation.ValidationProfile#getInitialPolicySet() + */ + public Set getInitialPolicySet() { + return Collections.EMPTY_SET; + } + + /** + * @see iaik.pki.pathvalidation.ValidationProfile#getNameConstraintsProcessing() + */ + public boolean getNameConstraintsProcessing() { + return false; + } + + /** + * @see iaik.pki.pathvalidation.ValidationProfile#getPolicyProcessing() + */ + public boolean getPolicyProcessing() { + return false; + } + + /** + * @see iaik.pki.pathvalidation.ValidationProfile#getRevocationChecking() + */ + public boolean getRevocationChecking() { + return this.revocationChecking; + } + + /** + * @see iaik.pki.store.truststore.TrustStoreProfile#getId() + */ + public String getId() { + return id; + } + /** + * Sets the trust profile identifier. + * @param id The id to set. + */ + public void setId(String id) { + this.id = id; + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/pki/jsse/MOAIDTrustManager.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/pki/jsse/MOAIDTrustManager.java new file mode 100644 index 000000000..9da006d35 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/pki/jsse/MOAIDTrustManager.java @@ -0,0 +1,119 @@ +package at.gv.egovernment.moa.id.iaik.pki.jsse; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.logging.LoggingContext; +import at.gv.egovernment.moa.logging.LoggingContextManager; + +import iaik.pki.jsse.IAIKX509TrustManager; + +/** + * <code>TrustManager</code> implementation featuring CRL checking (inherited from + * <code>IAIKX509TrustManager</code>), plus server-end-SSL-certificate checking. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class MOAIDTrustManager extends IAIKX509TrustManager { + + /** an x509Certificate array containing all accepted server certificates*/ + private X509Certificate[] acceptedServerCertificates; + + /** + * Constructor + * @param acceptedServerCertificateStoreURL the url leading to the acceptedServer cert store + * @throws GeneralSecurityException occurs on security errors + * @throws IOException occurs on IO errors + */ + public MOAIDTrustManager(String acceptedServerCertificateStoreURL) + throws IOException, GeneralSecurityException { + + if (acceptedServerCertificateStoreURL != null) + buildAcceptedServerCertificates(acceptedServerCertificateStoreURL); + else + acceptedServerCertificates = null; + } + + + /** + * Initializes the LoggingContextManager logging context. + * Fixes a bug occuring in the case MOA-SP is called by API. + * In this case, IAIKX509TrustManager uses the LogginConfig of MOA-SP. + * This method must be called before a MOAIDTrustManager is constructed, + * from every thread. + */ + public static void initializeLoggingContext() { + if (LoggingContextManager.getInstance().getLoggingContext() == null) + LoggingContextManager.getInstance().setLoggingContext( + new LoggingContext(Thread.currentThread().getName())); + } + + + /** + * Builds an Array of accepted server certificates from an URL, + * and stores it in <code>acceptedServerCertificates</code>. + * @param acceptedServerCertificateStoreURL file URL pointing to the directory + * containing accepted server X509 certificates + * @throws GeneralSecurityException on security errors + * @throws IOException on any IO errors + */ + private void buildAcceptedServerCertificates(String acceptedServerCertificateStoreURL) + throws IOException, GeneralSecurityException { + + List certList = new ArrayList(); + URL storeURL = new URL(acceptedServerCertificateStoreURL); + File storeDir = new File(storeURL.getFile()); + // list certificate files in directory + File[] certFiles = storeDir.listFiles(); + for (int i = 0; i < certFiles.length; i++) { + // for each: create an X509Certificate and store it in list + File certFile = certFiles[i]; + FileInputStream fis = new FileInputStream(certFile.getPath()); + CertificateFactory certFact = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate)certFact.generateCertificate(fis); + fis.close(); + certList.add(cert); + } + // store acceptedServerCertificates + acceptedServerCertificates = (X509Certificate[]) certList.toArray(new X509Certificate[0]); + } + + /** + * Does additional server-end-SSL-certificate checking. + * @see com.sun.net.ssl.X509TrustManager#isServerTrusted(java.security.cert.X509Certificate[]) + */ + public boolean isServerTrusted(X509Certificate[] certChain) { + boolean trusted = super.isServerTrusted(certChain); + if (! trusted || acceptedServerCertificates == null) + return trusted; + else { + // check server-end-SSL-certificate with acceptedServerCertificates + X509Certificate serverCert = certChain[0]; + for (int i = 0; i < acceptedServerCertificates.length; i++) { + X509Certificate acceptedServerCert = acceptedServerCertificates[i]; + if (serverCert.equals(acceptedServerCert)) + return true; + } + Logger.warn(MOAIDMessageProvider.getInstance().getMessage("ssl.01", null)); + return false; + } + } + /** + * In rare cases, this method is being called although it should not be. + * @see com.sun.net.ssl.X509TrustManager#isClientTrusted(X509Certificate[]) + */ + public boolean isClientTrusted(java.security.cert.X509Certificate arg0[]) + { + return true; + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/servertools/observer/ObservableImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/servertools/observer/ObservableImpl.java new file mode 100644 index 000000000..8f36ac5c0 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/iaik/servertools/observer/ObservableImpl.java @@ -0,0 +1,46 @@ +package at.gv.egovernment.moa.id.iaik.servertools.observer; + +import iaik.pki.store.observer.NotificationData; +import iaik.pki.store.observer.Observable; +import iaik.pki.store.observer.Observer; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + +/** + * Implementation of interface <needed to initialize an IAIK JSSE <code>TrustManager</code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class ObservableImpl implements Observable { + /** a List for all observers */ + private List observers = new ArrayList(); + + /** + * @see iaik.pki.store.observer.Observable#addObserver(iaik.pki.store.observer.Observer) + */ + public void addObserver(Observer observer) { + observers.add(observer); + } + + /** + * @see iaik.pki.store.observer.Observable#removeObserver(iaik.pki.store.observer.Observer) + */ + public boolean removeObserver(Observer observer) { + return observers.remove(observer); + } + + /** + * @see iaik.pki.store.observer.Observable#notify(iaik.pki.store.observer.NotificationData) + */ + public void notify(NotificationData data) { + Iterator iter = observers.iterator(); + for (iter = observers.iterator(); iter.hasNext();) { + Observer observer = (Observer) iter.next(); + observer.notify(data); + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ConnectionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ConnectionBuilder.java new file mode 100644 index 000000000..731212ef8 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ConnectionBuilder.java @@ -0,0 +1,64 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.Vector; + +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.http.HttpServletRequest; + +/** + * Builder for {@link java.net.URLConnection} objects used to forward requests + * to the remote online application. + * + * @author Paul Ivancsics + * @version $Id$ + */ + +public interface ConnectionBuilder { + + /** + * Builds an HttpURLConnection to a {@link java.net.URL} which is derived + * from an {@link HttpServletRequest} URL, by substitution of a + * public URL prefix for the real URL prefix.<br> + * The HttpURLConnection has been created by {@link java.net.URL#openConnection}, but + * it has not yet been connected to by {@link java.net.URLConnection#connect}.<br> + * The field settings of the HttpURLConnection are: + * <ul> + * <li><code>allowUserInteraction = false</code></li> + * <li><code>doInput = true</code></li> + * <li><code>doOutput = true</code></li> + * <li><code>requestMethod = request.getMethod()</code></li> + * <li><code>useCaches = false</code></li> + * </ul> + * + * @param request the incoming request which shall be forwarded + * @param publicURLPrefix the public URL prefix to be substituted by the real URL prefix + * @param realURLPrefix the URL prefix to substitute the public URL prefix + * @param sslSocketFactory factory to be used for creating an SSL socket in case + * of a URL for scheme <code>"https:"</code>; + * <br>if <code>null</code>, the default SSL socket factory would be used + * @param parameters parameters to be forwarded + * @return a URLConnection created by {@link java.net.URL#openConnection}, connecting to + * the requested URL with <code>publicURLPrefix</code> substituted by <code>realURLPrefix</code> + * @throws IOException if an I/O exception occurs during opening the connection + * @see java.net.URL#openConnection() + * @see com.sun.net.ssl.HttpsURLConnection#getDefaultSSLSocketFactory() + */ + public HttpURLConnection buildConnection( + HttpServletRequest request, + String publicURLPrefix, + String realURLPrefix, + SSLSocketFactory sslSocketFactory, + Vector parameters) throws IOException; + + + /** + * Disconnects the HttpURLConnection if necessary. + * The implementation of the Connectionbuilder decides wether + * if this should be happen or not. + * + * @param con the HttpURLConnection which is normaly to be closed + */ + public void disconnect(HttpURLConnection con); +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ConnectionBuilderFactory.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ConnectionBuilderFactory.java new file mode 100644 index 000000000..ff7787839 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ConnectionBuilderFactory.java @@ -0,0 +1,68 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.util.HashMap; +import java.util.Map; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.proxy.OAProxyParameter; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; + +/** + * Factory delivering a {@link ConnectionBuilder} implementation for + * an online application, initialized from configuration data. + * @author Paul Ivancsics + * @version $Id$ + */ +public class ConnectionBuilderFactory { + + /** default connection builder to be used for online application + * where no special implementation of the <code>ConnectionBuilder</code> + * interface is configured + */ + private static ConnectionBuilder defaultConnectionBuilder; + /** mapping from online application public URL prefix to an implementation + * of the <code>ConnectionBuilder</code> interface to be used; + * if no mapping is given for an online application, the + * <code>DefaultConnectionBuilder</code> will be used */ + private static Map connectionBuilderMap; + + /** + * Initializes the <code>ConnectionBuilder</code> map from the configuration data. + * @throws ConfigurationException when the configuration cannot be read, + * or when a class name configured cannot be instantiated + */ + public static void initialize() throws ConfigurationException { + defaultConnectionBuilder = new DefaultConnectionBuilder(); + connectionBuilderMap = new HashMap(); + ProxyConfigurationProvider proxyConf = ProxyConfigurationProvider.getInstance(); + for (int i = 0; i < proxyConf.getOnlineApplicationParameters().length; i++) { + OAProxyParameter oaParam = proxyConf.getOnlineApplicationParameters()[i]; + String publicURLPrefix = oaParam.getPublicURLPrefix(); + String className = oaParam.getConnectionBuilderImpl(); + if (className != null) { + try { + ConnectionBuilder cb = (ConnectionBuilder)Class.forName(className).newInstance(); + connectionBuilderMap.put(publicURLPrefix, cb); + } + catch (Throwable ex) { + throw new ConfigurationException("config.07", new Object[] {publicURLPrefix}, ex); + } + } + } + } + + /** + * Gets the <code>ConnectionBuilder</code> implementation to be used for the given + * online application. + * @param publicURLPrefix public URL prefix of the online application + * @return <code>ConnectionBuilder</code> implementation + */ + public static ConnectionBuilder getConnectionBuilder(String publicURLPrefix) { + ConnectionBuilder cb = (ConnectionBuilder) connectionBuilderMap.get(publicURLPrefix); + if (cb == null) + return defaultConnectionBuilder; + else + return cb; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/DefaultConnectionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/DefaultConnectionBuilder.java new file mode 100644 index 000000000..151b1cec3 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/DefaultConnectionBuilder.java @@ -0,0 +1,149 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import at.gv.egovernment.moa.util.URLEncoder; //java.net.URLEncoder; +import java.util.Iterator; +import java.util.Vector; + +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.http.HttpServletRequest; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.BoolUtils; + +import com.sun.net.ssl.HostnameVerifier; +import com.sun.net.ssl.HttpsURLConnection; + +/** + * Defaultimplementierung von <code>ConnectionBuilder</code>. + * @author Paul Ivancsics + * @version $Id$ + */ +public class DefaultConnectionBuilder implements ConnectionBuilder { + + /** a boolean to disable the HostnameVerification (default = false)*/ + private static boolean cbDisableHostnameVerification = false; + + /** + * Constructor for DefaultConnectionBuilder. + * @throws ConfigurationException on any config error + */ + public DefaultConnectionBuilder() throws ConfigurationException { + cbDisableHostnameVerification = BoolUtils.valueOf( + ProxyConfigurationProvider.getInstance().getGenericConfigurationParameter( + "ProxyComponent.DisableHostnameVerification")); + //TODO MOA-ID BRZ undocumented feature + if (cbDisableHostnameVerification) + Logger.warn("ProxyComponent.DisableHostnameVerification: " + cbDisableHostnameVerification); + } + + /** + * @see at.gv.egovernment.moa.id.proxy.ConnectionBuilder#buildConnection + */ + public HttpURLConnection buildConnection( + HttpServletRequest req, + String publicURLPrefix, + String realURLPrefix, + SSLSocketFactory sslSocketFactory, + Vector parameters) + throws IOException { + + String requestedURL = req.getRequestURL().toString(); + // check whether requested URL starts with publicURLPrefix + + //Temporary allow http:// urls instead of the https:// in publicURLPrefix + //if (req.getSession().getAttribute("authorizationkey")==null) { + // if (! requestedURL.startsWith(publicURLPrefix)) + // throw new IOException(MOAIDMessageProvider.getInstance().getMessage( + // "proxy.01", new Object[] {requestedURL, publicURLPrefix})); + //} + + // in case of GET request, append query string to requested URL; + // otherwise, HttpURLConnection would perform a POST request + if ("get".equalsIgnoreCase(req.getMethod()) && ! parameters.isEmpty()) { + requestedURL = appendQueryString(requestedURL, parameters); + } + // build real URL in online application + String realURLString = realURLPrefix + requestedURL.substring(publicURLPrefix.length()); + URL url = new URL(realURLString); + Logger.debug("OA Request: " + req.getMethod() + " " + url.toString()); + + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestMethod(req.getMethod()); + conn.setDoInput(true); + conn.setDoOutput(true); + //conn.setUseCaches(false); + //conn.setAllowUserInteraction(true); + conn.setInstanceFollowRedirects(false); + if (conn instanceof HttpsURLConnection && sslSocketFactory != null) { + HttpsURLConnection httpsConn = (HttpsURLConnection) conn; + httpsConn.setSSLSocketFactory(sslSocketFactory); + if (cbDisableHostnameVerification) + httpsConn.setHostnameVerifier(new HostnameNonVerifier()); + } + return conn; + } + + + /** + * Disconnects the HttpURLConnection if necessary. + * The implementation of the Connectionbuilder decides wether + * if this should be happen or not. + * + * @param conn the HttpURLConnection which is normaly to be closed + */ + public void disconnect(HttpURLConnection conn) { + conn.disconnect(); + } + + + /** + * @param requestedURL + * @param parameters + * @return + */ + private String appendQueryString(String requestedURL, Vector parameters) { + String newURL = requestedURL; + String parameter[] = new String[2]; + String paramValue =""; + String paramName =""; + String paramString =""; + for (Iterator iter = parameters.iterator(); iter.hasNext();) { + try { + parameter = (String[]) iter.next(); + //next two lines work not with OWA-SSL-Login-form + paramName = URLEncoder.encode((String) parameter[0], "UTF-8"); + paramValue = URLEncoder.encode((String) parameter[1], "UTF-8"); + + } catch (UnsupportedEncodingException e) { + //UTF-8 should be supported + } + paramString = "&" + paramName + "=" + paramValue + paramString; + } + if (paramString.length()>0) newURL = newURL + "?" + paramString.substring(1); + return newURL; + } + + /** + * @author Stefan Knirsch + * @version $Id$ + * A private class to change the standard HostName verifier to disable the + * Hostname Verification Check + */ + private class HostnameNonVerifier implements HostnameVerifier { + + /** + * @see com.sun.net.ssl.HostnameVerifier#verify(String, String) + */ + public boolean verify(String arg0, String arg1) { + return true; + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/DefaultLoginParameterResolver.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/DefaultLoginParameterResolver.java new file mode 100644 index 000000000..3df76656b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/DefaultLoginParameterResolver.java @@ -0,0 +1,142 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import at.gv.egovernment.moa.id.config.proxy.OAConfiguration; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.URLEncoder; + +/** + * Implementation of interface <code>LoginParameterResolver</code> + * @author Paul Ivancsics + * @version $Id$ + */ +public class DefaultLoginParameterResolver implements LoginParameterResolver { + + /** + * Constructor + */ + public DefaultLoginParameterResolver() { + } + + /** + * Configuration mehtod (not used) + */ + public void configure(String configuration, Boolean businessService) throws LoginParameterResolverException { + } + + + /** + * @see at.gv.egovernment.moa.id.proxy.LoginParameterResolver#getAuthenticationHeaders(OAConfiguration, AuthenticationData, String, boolean, String) + */ + public Map getAuthenticationHeaders( + OAConfiguration oaConf, + AuthenticationData authData, + String clientIPAddress, + boolean businessService, + String publicURLPrefix) { + + Map result = new HashMap(); + + if (oaConf.getAuthType().equals(OAConfiguration.BASIC_AUTH)) { + String useridPredicate = oaConf.getBasicAuthUserIDMapping(); + String userid = resolveValue(useridPredicate, authData, clientIPAddress); + String passwordPredicate = oaConf.getBasicAuthPasswordMapping(); + String password = resolveValue(passwordPredicate, authData, clientIPAddress); + + try { + String userIDPassword = userid + ":" + password; + String credentials = Base64Utils.encode(userIDPassword.getBytes()); + result.put("Authorization", "Basic " + credentials); + } + catch (IOException ignore) { + } + } + else if (oaConf.getAuthType().equals(OAConfiguration.HEADER_AUTH)) { + for (Iterator iter = oaConf.getHeaderAuthMapping().keySet().iterator(); iter.hasNext();) { + String key = (String) iter.next(); + String predicate = (String) oaConf.getHeaderAuthMapping().get(key); + String resolvedValue = resolveValue(predicate, authData, clientIPAddress); + result.put(key, resolvedValue); + } + } + + return result; + } + + /** + * @see at.gv.egovernment.moa.id.proxy.LoginParameterResolver#getAuthenticationParameters(OAConfiguration, AuthenticationData, String, boolean, String) + */ + public Map getAuthenticationParameters( + OAConfiguration oaConf, + AuthenticationData authData, + String clientIPAddress, + boolean businessService, + String publicURLPrefix) { + + Map result = new HashMap(); + + if (oaConf.getAuthType().equals(OAConfiguration.PARAM_AUTH)) { + for (Iterator iter = oaConf.getParamAuthMapping().keySet().iterator(); iter.hasNext();) { + String key = (String) iter.next(); + String predicate = (String) oaConf.getParamAuthMapping().get(key); + String resolvedValue; + try { + resolvedValue = + URLEncoder.encode(resolveValue(predicate, authData, clientIPAddress), "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + //ISO-8859-1 is supported + resolvedValue = null; + } + result.put(key, resolvedValue); + } + } + + return result; + } + + /** + * Resolves a login header or parameter value. + * @param predicate header or parameter predicate name from online application configuration + * @param authData authentication data for current login + * @param clientIPAddress client IP address + * @return header or parameter value resolved; <code>null</code> if unknown name is given + */ + private static String resolveValue(String predicate, AuthenticationData authData, String clientIPAddress) { + if (predicate.equals(MOAGivenName)) + return authData.getGivenName(); + if (predicate.equals(MOAFamilyName)) + return authData.getFamilyName(); + if (predicate.equals(MOADateOfBirth)) + return authData.getDateOfBirth(); + if (predicate.equals(MOABPK)) + return authData.getBPK(); + if (predicate.equals(MOAWBPK)) + return authData.getWBPK(); + if (predicate.equals(MOAPublicAuthority)) + if (authData.isPublicAuthority()) + return "true"; + else + return "false"; + if (predicate.equals(MOABKZ)) + return authData.getPublicAuthorityCode(); + if (predicate.equals(MOAQualifiedCertificate)) + if (authData.isQualifiedCertificate()) + return "true"; + else + return "false"; + if (predicate.equals(MOAStammzahl)) + return authData.getIdentificationValue(); + if (predicate.equals(MOAIdentificationValueType)) + return authData.getIdentificationType(); + if (predicate.equals(MOAIPAddress)) + return clientIPAddress; + else return null; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ElakConnectionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ElakConnectionBuilder.java new file mode 100644 index 000000000..909e2d51e --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/ElakConnectionBuilder.java @@ -0,0 +1,249 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; + +import com.ibm.webdav.HTTPHeaders; +import com.ibm.webdav.protocol.URLStreamHandlerFactory; +import com.ibm.webdav.protocol.http.WebDAVURLConnection; +import java.util.StringTokenizer; +import java.net.URL; +import at.gv.egovernment.moa.util.URLEncoder; //java.net.URLEncoder; +import java.net.URLStreamHandler; +import java.util.Iterator; +import java.util.Vector; + +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.http.HttpServletRequest; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.BoolUtils; + +import com.sun.net.ssl.HostnameVerifier; +import com.sun.net.ssl.HttpsURLConnection; + +/** + * Defaultimplementierung von <code>ConnectionBuilder</code>. + * @author Paul Ivancsics + * @version $Id$ + */ +public class ElakConnectionBuilder implements ConnectionBuilder { + + /** a boolean to disable the HostnameVerification (default = false)*/ + private static boolean cbDisableHostnameVerification = false; + + /** a boolean to indicat if webdav protocol handler was already set */ + private static boolean webdavPHSet = false; + + /** + * The system property name used to register a protocol handler. + */ + public final static String PROTOCOL_HANDLER_PROPERTY_NAME = "java.protocol.handler.pkgs"; + + /** + * The package providing the ldap protocol handler. + */ + public final static String WEBDAV_PROTOCOL_HANDLER = "com.ibm.webdav.protocol"; + + /** + * The pipe character used to sepearte different protocol handlers. + */ + public final static char PIPE_CHAR = '|'; + + + + + + /** + * Constructor for ElakConnectionBuilder. + * @throws ConfigurationException on any config error + */ + public ElakConnectionBuilder() throws ConfigurationException { + cbDisableHostnameVerification = BoolUtils.valueOf( + ProxyConfigurationProvider.getInstance().getGenericConfigurationParameter( + "ProxyComponent.DisableHostnameVerification")); + //TODO MOA-ID BRZ undocumented feature + if (cbDisableHostnameVerification) + Logger.warn("ProxyComponent.DisableHostnameVerification: " + cbDisableHostnameVerification); + } + + /** + * @see at.gv.egovernment.moa.id.proxy.ConnectionBuilder#buildConnection + */ + public HttpURLConnection buildConnection( + HttpServletRequest req, + String publicURLPrefix, + String realURLPrefix, + SSLSocketFactory sslSocketFactory, + Vector parameters) + throws IOException { + + String requestedURL = req.getRequestURL().toString(); + // check whether requested URL starts with publicURLPrefix + if (! requestedURL.startsWith(publicURLPrefix)) + throw new IOException(MOAIDMessageProvider.getInstance().getMessage( + "proxy.01", new Object[] {requestedURL, publicURLPrefix})); + + + + // in case of GET request, append query string to requested URL; + // otherwise, HttpURLConnection would perform a POST request + //FIXME right parameters + /* + if ("get".equalsIgnoreCase(req.getMethod()) && ! parameters.isEmpty()) { + requestedURL = appendQueryString(requestedURL, parameters); + } + */ + //TODO RSCH check functionality + if (null != req.getQueryString() && 0 != req.getQueryString().length() ) { + String query = req.getQueryString(); + requestedURL = requestedURL + "?" + query; + + String parameter[] = new String[2]; + for (Iterator iter = parameters.iterator(); iter.hasNext();) { + parameter = (String[]) iter.next(); + if(query.indexOf(parameter[0]) >= 0) iter.remove(); + } + } + + // build real URL in online application + String realURLString = realURLPrefix + requestedURL.substring(publicURLPrefix.length()); + + + Logger.info("Registering WebDAV protocol handler"); + String protocolHandlers = System.getProperty(ElakConnectionBuilder.PROTOCOL_HANDLER_PROPERTY_NAME); + if (protocolHandlers == null) { + protocolHandlers = ElakConnectionBuilder.WEBDAV_PROTOCOL_HANDLER; + System.setProperty(ElakConnectionBuilder.PROTOCOL_HANDLER_PROPERTY_NAME, protocolHandlers); + } else { + // check, if WEBDAV protocol handler is already configured + boolean isConfigured = false; + StringTokenizer tokenizer = new StringTokenizer(protocolHandlers, "| "); + while (tokenizer.hasMoreTokens()) { + String protocolHandler = tokenizer.nextToken(); + if (protocolHandler.equals(ElakConnectionBuilder.WEBDAV_PROTOCOL_HANDLER)) { + isConfigured = true; + break; + } + } + // if it has not been configured yet, configure it + if (!isConfigured) { + protocolHandlers = ElakConnectionBuilder.WEBDAV_PROTOCOL_HANDLER + ElakConnectionBuilder.PIPE_CHAR + protocolHandlers; + System.setProperty(ElakConnectionBuilder.PROTOCOL_HANDLER_PROPERTY_NAME, protocolHandlers); + } + } + Logger.info("Registered protocol handlers: " + protocolHandlers); + Class webdavSH = null; + try + { + webdavSH = Class.forName(ElakConnectionBuilder.WEBDAV_PROTOCOL_HANDLER + ".http.Handler"); + } + catch (ClassNotFoundException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + URLStreamHandler urlStreamHandler = null; + try + { + urlStreamHandler = (URLStreamHandler) webdavSH.newInstance(); + } + catch (InstantiationException e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + catch (IllegalAccessException e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + //URL testURL = new URL("http", realURLString.substring("http://localhost:82".length()), 82, "", urlStreamHandler); + //WebDAVURLConnection webDavTest = (WebDAVURLConnection) testURL.openConnection(); + + + URL testURL = new URL(realURLString); + Logger.debug("TEST URL ist von der Klasse: " + testURL.getClass().getName()); + + //URL url = new URL(realURLString); + URL testURL2 = new URL(realURLString); + + URL url = new URL("http", "localhost", 82, realURLString.substring("http://localhost:82".length()), urlStreamHandler); + + Logger.debug("OA Request: " + req.getMethod() + " " + url.toString()); + WebDAVURLConnection webDavConn = (WebDAVURLConnection) url.openConnection(); + HttpURLConnection conn = (HttpURLConnection)webDavConn; + webDavConn.setRequestMethod(req.getMethod()); + webDavConn.setDoInput(true); + webDavConn.setDoOutput(true); + //conn.setUseCaches(false); + webDavConn.setAllowUserInteraction(true); + webDavConn.setInstanceFollowRedirects(false); + if (conn instanceof HttpsURLConnection && sslSocketFactory != null) { + HttpsURLConnection httpsConn = (HttpsURLConnection) conn; + httpsConn.setSSLSocketFactory(sslSocketFactory); + if (cbDisableHostnameVerification) + httpsConn.setHostnameVerifier(new HostnameNonVerifier()); + } + return conn; + } + + /** + * Disconnects the HttpURLConnection if necessary. + * The implementation of the Connectionbuilder decides wether + * if this should be happen or not. + * + * @param conn the HttpURLConnection which is normaly to be closed + */ + public void disconnect(HttpURLConnection conn) { + conn.disconnect(); + } + + /** + * @param requestedURL + * @param parameters + * @return + */ + private String appendQueryString(String requestedURL, Vector parameters) { + String newURL = requestedURL; + String parameter[] = new String[2]; + String paramValue =""; + String paramName =""; + String paramString =""; + for (Iterator iter = parameters.iterator(); iter.hasNext();) { + try { + parameter = (String[]) iter.next(); + //Following two lines do not work with OWA-SSL-Login-form + paramName = URLEncoder.encode((String) parameter[0], "UTF-8"); + paramValue = URLEncoder.encode((String) parameter[1], "UTF-8"); + + } catch (UnsupportedEncodingException e) { + //UTF-8 should be supported + } + paramString = "&" + paramName + "=" + paramValue + paramString; + } + if (paramString.length()>0) newURL = newURL + "?" + paramString.substring(1); + return newURL; + } + + /** + * @author Stefan Knirsch + * @version $Id$ + * A private class to change the standard HostName verifier to disable the + * Hostname Verification Check + */ + private class HostnameNonVerifier implements HostnameVerifier { + + /** + * @see com.sun.net.ssl.HostnameVerifier#verify(String, String) + */ + public boolean verify(String arg0, String arg1) { + return true; + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/EnhancedConnectionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/EnhancedConnectionBuilder.java new file mode 100644 index 000000000..f934ab80b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/EnhancedConnectionBuilder.java @@ -0,0 +1,211 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.io.IOException; +import java.net.URL; +import java.net.URLStreamHandler; +import java.util.Iterator; +import java.util.Vector; + +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; +import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.BoolUtils; + +import com.sun.net.ssl.HostnameVerifier; +import HTTPClient.HttpURLConnection; +import HTTPClient.HTTPConnection; + + +/** + * Outlook Web Access (OWA) Implementierung von <code>ConnectionBuilder</code>. + * uses the HTTP(s)Client from Ronald Tschalär. + * origin version (without https support) is available at http://www.innovation.ch/java/HTTPClient/ + * + * @author pdanner + */ +public class EnhancedConnectionBuilder implements ConnectionBuilder { + + /** a boolean to disable the HostnameVerification (default = false)*/ + private static boolean cbDisableHostnameVerification = false; + /** Name of the Parameter for the Target */ + private static final String PARAM_TARGET = "Target"; + /** Name of the Parameter for the SAMLArtifact */ + private static final String PARAM_SAMLARTIFACT = "SAMLArtifact"; + /** Name of the Attribute for marking the session as authenticated*/ + private static final String ATT_AUTHDATAFETCHED = "AuthDataFetched"; + + static { + HTTPConnection.setDefaultTimeout(0); + try { + HTTPConnection.removeDefaultModule(Class.forName("HTTPClient.AuthorizationModule")); + HTTPConnection.removeDefaultModule(Class.forName("HTTPClient.RedirectionModule")); + HTTPConnection.removeDefaultModule(Class.forName("HTTPClient.CookieModule")); + //HTTPConnection.removeDefaultModule(Class.forName("HTTPClient.RetryModule")); + } catch (ClassNotFoundException e) { + + } + } + + /** + * Constructor for OWAConnectionBuilder. + * @throws ConfigurationException on any config error + */ + public EnhancedConnectionBuilder() throws ConfigurationException { + cbDisableHostnameVerification = BoolUtils.valueOf( + ProxyConfigurationProvider.getInstance().getGenericConfigurationParameter( + "ProxyComponent.DisableHostnameVerification")); + //TODO MOA-ID BRZ undocumented feature + if (cbDisableHostnameVerification) + Logger.warn("ProxyComponent.DisableHostnameVerification: " + cbDisableHostnameVerification); + } + + /** + * @see at.gv.egovernment.moa.id.proxy.ConnectionBuilder#buildConnection + */ + public java.net.HttpURLConnection buildConnection(HttpServletRequest req, String publicURLPrefix, String realURLPrefix, SSLSocketFactory sslSocketFactory, Vector parameters) throws IOException { + + String requestedURL = req.getRequestURL().toString(); + // check whether requested URL starts with publicURLPrefix + + if (! requestedURL.startsWith(publicURLPrefix.substring(0,5))) + throw new IOException(MOAIDMessageProvider.getInstance().getMessage( + "proxy.01", new Object[] {requestedURL, publicURLPrefix})); + + String query = req.getQueryString(); + if (req.getSession().getAttribute(ATT_AUTHDATAFETCHED)!=null) { + query = removeParameter(query, PARAM_SAMLARTIFACT); + query = removeParameter(query, PARAM_TARGET); + req.getSession().removeAttribute(ATT_AUTHDATAFETCHED); + } + if (null != query && 0 != query.length() ) { + requestedURL = requestedURL + "?" + query; + + String parameter[] = new String[2]; + for (Iterator iter = parameters.iterator(); iter.hasNext();) { + parameter = (String[]) iter.next(); + if(query.indexOf(parameter[0]) >= 0) iter.remove(); + } + } + + // build real URL in online application + String realURLString = realURLPrefix + requestedURL.substring(publicURLPrefix.length()); + + // build real URL in online application + URLStreamHandler urlStreamHandler = null; + + //URL url = new URL(realURLString); + if (realURLString.startsWith("https")) { + urlStreamHandler = new HTTPClient.https.Handler(); + } else{ + urlStreamHandler = new HTTPClient.http.Handler(); + } + URL url = new URL(null, realURLString, urlStreamHandler); + Logger.debug("OA Request: " + req.getMethod() + " " + url.toString()); + + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + + conn.setRequestMethod(req.getMethod()); + conn.setDoInput(true); + conn.setDoOutput(true); + //conn.setUseCaches(false); + //conn.setAllowUserInteraction(true); + conn.setInstanceFollowRedirects(false); + + if (realURLString.startsWith("https") && sslSocketFactory != null) { + conn.setSSLSocketFactory(sslSocketFactory); + //Not available in HTTPClient + //if (cbDisableHostnameVerification) + // conn.setHostnameVerifier(new HostnameNonVerifier()); + } + + return conn; + + } + + /** + * Disconnects the HttpURLConnection if necessary. + * The implementation of the Connectionbuilder decides wether + * if this should be happen or not. + * + * @param conn the HttpURLConnection which is normaly to be closed + */ + public void disconnect(java.net.HttpURLConnection conn) { + // In HTTPClient there must not be an diconnect! + // conn.disconnect(); + } + + /** + * @author Stefan Knirsch + * @version $Id$ + * A private class to change the standard HostName verifier to disable the + * Hostname Verification Check + */ + private class HostnameNonVerifier implements HostnameVerifier { + + /** + * @see com.sun.net.ssl.HostnameVerifier#verify(String, String) + */ + public boolean verify(String arg0, String arg1) { + return true; + } + } + + /** + * Removes parameters from the query-URL recursively + * + * @param query the query from which the parameter is to be removed + * @param parameter the parameter to be removed + * @return the parameterclean query + */ + private String removeParameter(String query, String parameter) { + return removeParameter(query, parameter, true); + } + + /** + * Removes one parameter from the query-URL recursively + * + * @param query the query from which the parameter is to be removed + * @param parameter the parameter to be removed + * @param remove. Boolean value wether a parameter was removed in last call or not. In initial call set to true to check for new occurrences + * @return the parameterclean query + */ + private String removeParameter(String query, String parameter, boolean remove) { + String result = query; + if (remove && query!=null && !query.equals("") && parameter!=null && !parameter.equals("")) { + String param = parameter; + int capEnd=0; + if (!param.endsWith("=")) param=param+"="; + if (query.startsWith(param)) { + //remove leading + result=""; + } else { + if (!param.startsWith("&")) param="&"+param; + capEnd = query.indexOf(param); + if (capEnd!=-1) { + //leading part + result=query.substring(0, capEnd); + } + } + if (capEnd!=-1) { + //trailing part + capEnd += param.length(); + int capBegin = -1; + if (capEnd <query.length()) capBegin = query.indexOf("&", capEnd); + if (capBegin!=-1) { + if (capBegin<query.length()) { + result=result + query.substring(capBegin); + if (result.startsWith("&")) result = result.substring(1); //if now is leading part + } + } + } + result = removeParameter(result, parameter, !query.equals(result)); + } + return result; + } + + } diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolver.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolver.java new file mode 100644 index 000000000..b47389950 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolver.java @@ -0,0 +1,86 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.util.Map; + +import at.gv.egovernment.moa.id.config.proxy.OAConfiguration; +import at.gv.egovernment.moa.id.data.AuthenticationData; + +/** + * Determines authentication parameters and headers to be added to a {@link java.net.URLConnection} + * to the remote online application. + * Utilizes {@link OAConfiguration} and {@link AuthenticationData}. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public interface LoginParameterResolver { + + /** Constants used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code>, + * naming predicates used by the <code>LoginParameterResolver</code>. */ + public static final String MOAGivenName = "MOAGivenName"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOAFamilyName = "MOAFamilyName"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOADateOfBirth = "MOADateOfBirth"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOABPK = "MOABPK"; + /** Constant used in <code>MOAIDConfiguration-1.3.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOAWBPK = "MOAWBPK"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOAPublicAuthority = "MOAPublicAuthority"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOABKZ = "MOABKZ"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOAQualifiedCertificate = "MOAQualifiedCertificate"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOAStammzahl = "MOAStammzahl"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOAIdentificationValueType = "MOAIdentificationValueType"; + /** Constant used in <code>MOAIDConfiguration-1.2.xsd</code>, type <code>MOAAuthDataType</code> */ + public static final String MOAIPAddress = "MOAIPAddress"; + + /** + * Returns authentication headers to be added to a URLConnection. + * + * @param oaConf configuration data + * @param authData authentication data + * @param clientIPAddress client IP address + * @param businessService boolean value for recognizing (w)bPK-mode + * @param publicURLPrefix to distinguish different online applications + * @return A map, the keys being header names and values being corresponding header values. + * <br>In case of authentication type <code>"basic-auth"</code>, header fields + * <code>username</code> and <code>password</code>. + * <br>In case of authentication type <code>"header-auth"</code>, header fields + * derived from parameter mapping and authentication data provided. + * <br>Otherwise, an empty map. + */ + public Map getAuthenticationHeaders( + OAConfiguration oaConf, + AuthenticationData authData, + String clientIPAddress, + boolean businessService, + String publicURLPrefix) throws LoginParameterResolverException, NotAllowedException; + + /** + * Returns request parameters to be added to a URLConnection. + * + * @param oaConf configuration data + * @param authData authentication data + * @param clientIPAddress client IP address + * @param businessService boolean value for recognizing (w)bPK-mode + * @param publicURLPrefix to distinguish different online applications + * @return A map, the keys being parameter names and values being corresponding parameter values. + * <br>In case of authentication type <code>"param-auth"</code>, parameters + * derived from parameter mapping and authentication data provided. + * <br>Otherwise, an empty map. + */ + public Map getAuthenticationParameters( + OAConfiguration oaConf, + AuthenticationData authData, + String clientIPAddress, + boolean businessService, + String publicURLPrefix) throws LoginParameterResolverException, NotAllowedException; + + public void configure(String configuration, Boolean businessService) throws LoginParameterResolverException; + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolverException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolverException.java new file mode 100644 index 000000000..3aa2368bf --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolverException.java @@ -0,0 +1,37 @@ +package at.gv.egovernment.moa.id.proxy; + +import at.gv.egovernment.moa.id.MOAIDException; + +/** + * Exception thrown while proxying a request to the online application + * + * @author Rudolf Schamberger + * @version $Id$ + */ +public class LoginParameterResolverException extends MOAIDException { + + /** + * Constructor for LoginParameterResolverException. + * @param messageId + * @param parameters + */ + public LoginParameterResolverException( + String messageId, + Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for LoginParameterResolverException. + * @param messageId + * @param parameters + * @param wrapped + */ + public LoginParameterResolverException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolverFactory.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolverFactory.java new file mode 100644 index 000000000..8edd23438 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/LoginParameterResolverFactory.java @@ -0,0 +1,82 @@ +package at.gv.egovernment.moa.id.proxy; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.proxy.OAProxyParameter; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; + +/** + * Factory delivering a {@link LoginParameterResolver} implementation for + * an online application, initialized from configuration data. + * @author Paul Ivancsics + * @version $Id$ + */ +public class LoginParameterResolverFactory { + + /** default login parameter resolver to be used for online application + * where no special implementation of the <code>LoginParameterResolver</code> + * interface is configured + */ + private static LoginParameterResolver defaultLoginParameterResolver; + /** mapping from online application public URL prefix to an implementation + * of the <code>LoginParameterResolver</code> interface to be used; + * if no mapping is given for an online application, the + * <code>DefaultLoginParameterResolver</code> will be used */ + private static Map loginParameterResolverMap; + + /** + * Initializes the <code>LoginParameterResolver</code> map from the configuration data. + * @throws ConfigurationException when the configuration cannot be read, + * or when a class name configured cannot be instantiated + */ + public static void initialize() throws ConfigurationException { + defaultLoginParameterResolver = new DefaultLoginParameterResolver(); + loginParameterResolverMap = new HashMap(); + ProxyConfigurationProvider proxyConf = ProxyConfigurationProvider.getInstance(); + for (int i = 0; i < proxyConf.getOnlineApplicationParameters().length; i++) { + OAProxyParameter oaParam = proxyConf.getOnlineApplicationParameters()[i]; + String publicURLPrefix = oaParam.getPublicURLPrefix(); + String className = oaParam.getLoginParameterResolverImpl(); + String configuration = oaParam.getLoginParameterResolverConfiguration(); + if (className != null) { + try { + Class lprClass = Class.forName(className); + LoginParameterResolver lpr = (LoginParameterResolver)Class.forName(className).newInstance(); + + Class[] argumentTypes = { String.class, Boolean.class }; + Method confMethod = lprClass.getMethod( "configure", argumentTypes ); + + Object[] arguments = { new String(configuration), new Boolean(oaParam.getBusinessService()) }; + confMethod.invoke( lpr, arguments ); + + loginParameterResolverMap.put(publicURLPrefix, lpr); + } + catch (InvocationTargetException lpex) { + throw new ConfigurationException("config.11", new Object[] {className}, lpex); + } + catch (Throwable ex) { + throw new ConfigurationException("config.07", new Object[] {publicURLPrefix}, ex); + } + } + } + } + + /** + * Gets the <code>LoginParameterResolver</code> implementation to be used for the given + * online application. + * @param publicURLPrefix public URL prefix of the online application + * @return <code>LoginParameterResolver</code> implementation + */ + public static LoginParameterResolver getLoginParameterResolver(String publicURLPrefix) { + LoginParameterResolver lpr = (LoginParameterResolver) loginParameterResolverMap.get(publicURLPrefix); + if (lpr == null) + return defaultLoginParameterResolver; + else + return lpr; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/MOAIDProxyInitializer.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/MOAIDProxyInitializer.java new file mode 100644 index 000000000..3f615b01c --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/MOAIDProxyInitializer.java @@ -0,0 +1,95 @@ +package at.gv.egovernment.moa.id.proxy; + +import iaik.pki.PKIException; +import iaik.pki.jsse.IAIKX509TrustManager; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +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.proxy.OAProxyParameter; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; +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; + +/** + * Web application initializer + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class MOAIDProxyInitializer { + + /** + * Initializes the web application components which need initialization: + * logging, JSSE, MOA-ID Auth configuration, Axis, session cleaner. + */ + public static void initialize() + throws ConfigurationException, IOException, GeneralSecurityException, PKIException { + + Logger.setHierarchy("moa.id.proxy"); + + // 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 = MOAIDProxyInitializer.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 the SSLSocketFactory store + SSLUtils.initialize(); + + // Initializes IAIKX509TrustManager logging + String log4jConfigURL = System.getProperty("log4j.configuration"); + if (log4jConfigURL != null) { + IAIKX509TrustManager.initLog(new LoggerConfigImpl(log4jConfigURL)); + } + + // Loads the configuration + ProxyConfigurationProvider proxyConf = ProxyConfigurationProvider.reload(); + + // Initializes the Axis secure socket factory for use in calling the MOA-Auth web service, + // using configuration data + ConnectionParameter connParamAuth = proxyConf.getAuthComponentConnectionParameter(); + if (connParamAuth!=null) { + if (connParamAuth.isHTTPSURL()) { + SSLSocketFactory ssf = SSLUtils.getSSLSocketFactory(proxyConf, connParamAuth); + AxisSecureSocketFactory.initialize(ssf); + } + } else { + throw new ConfigurationException("config.16", null); + } + + // Initializes the Axis secure socket factories for use in calling the online applications, + // using configuration data + OAProxyParameter[] oaParams = proxyConf.getOnlineApplicationParameters(); + for (int i = 0; i < oaParams.length; i++) { + OAProxyParameter oaParam = oaParams[i]; + ConnectionParameter oaConnParam = oaParam.getConnectionParameter(); + if (oaConnParam.isHTTPSURL()) + SSLUtils.getSSLSocketFactory(proxyConf, oaConnParam); + } + + // Initializes the ConnectionBuilderFactory from configuration data + ConnectionBuilderFactory.initialize(); + + // Initializes the LoginParameterResolverFactory from configuration data + LoginParameterResolverFactory.initialize(); + + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/NotAllowedException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/NotAllowedException.java new file mode 100644 index 000000000..849160a7b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/NotAllowedException.java @@ -0,0 +1,39 @@ +package at.gv.egovernment.moa.id.proxy; + +import at.gv.egovernment.moa.id.MOAIDException; + +/** + * Exception thrown while proxying a request to the online application + * Reason for this exception: the dedicated LoginParameterResolver does + * not allow access to the desired ressource. + * + * @author Rudolf Schamberger + * @version $Id$ + */ +public class NotAllowedException extends MOAIDException { + + /** + * Constructor for NotAllowedException. + * @param messageId + * @param parameters + */ + public NotAllowedException( + String messageId, + Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for NotAllowedException. + * @param messageId + * @param parameters + * @param wrapped + */ + public NotAllowedException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/XMLLoginParameterResolverEncryptedData.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/XMLLoginParameterResolverEncryptedData.java new file mode 100644 index 000000000..9bfd9de9b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/XMLLoginParameterResolverEncryptedData.java @@ -0,0 +1,678 @@ +package at.gv.egovernment.moa.id.proxy; + +import iaik.security.provider.IAIK; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import at.gv.egovernment.moa.id.config.ConfigurationProvider; +import at.gv.egovernment.moa.id.config.proxy.OAConfiguration; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.logging.Logger; +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.FileUtils; +import at.gv.egovernment.moa.util.URLEncoder; + +/** + * XMLLoginParameterResolver an implementation of implementation of interface + * <code>LoginParameterResolver</code> + * This implementation used to map identities stored in an XML file to parameters + * which are given to OAs. + * + * @author Rudolf Schamberger + * @version $Id$ + */ +public class XMLLoginParameterResolverEncryptedData implements LoginParameterResolver { + + //file which is parsed and interpreted for paremeter resolving. + private String identityFile; + + private Cipher blowfishCipher; + private Key key; + /** + * inner class used to store mapped parameters + */ + class LPRParams { + + /** + * getter method for parameter Enabled. + * Parameter Enabled decides if mapped parameters should be used by XMLLoginParameterResolver + */ + public boolean getEnabled() { + return enabled.booleanValue(); + } + + /** + * getter method for parameter UN (username) + * @return Parameter UN or <code>null</code> not set. + */ + public String getUN() { + return UN; + } + + /** + * getter method for parameter UN (username) + * @return Parameter UN or <code>null</code> not set. + */ + //TODO XMLLPR decrypt + public String getPlainUN() { + //Security.addProvider(); + + + return UN; + } + + + /** + * getter method for parameter PW (password) + * @return Parameter PW or <code>null</code> not set. + */ + public String getPW() { + return PW; + } + + /** + * getter method for generic parameter Param1 + * @return Parameter Param1 or <code>null</code> not set. + */ + public String getParam1() { + return Param1; + } + + /** + * getter method for generic parameter Param2 + * @return Parameter Param2 or <code>null</code> not set. + */ + public String getParam2() { + return Param2; + } + + /** + * getter method for generic parameter Param3 + * @return Parameter Param3 or <code>null</code> not set. + */ + public String getParam3() { + return Param3; + } + + /** + * Returns a string representation of LPRParams + * + * @return a <code>String</code> representation of this object. + * @see XMLLoginParameterResolver.LPRParams + */ + public String toString() { + return "Enabled: " + + enabled.toString() + + "UN: '" + + UN + + "' PW: '" + + PW + + "' Param1: '" + + Param1 + + "' Param2: '" + + Param2 + + "' Param3: '" + + Param3 + + "'\n"; + } + + //private member variables used to store the parameters + private Boolean enabled = null; + private String UN = null; + private String PW = null; + private String Param1 = null; + private String Param2 = null; + private String Param3 = null; + + /** + * Constructs a newly allocated <code>XMLLoginParameterResolver.LPRParams</code> object. + * + * @param enabled enable user mapping to parameter set for the parameter set. + * @param UN username used in HTTP 401 - BasicAuthentication + * @param PW password used in HTTP 401 - BasicAuthentication + * @param Param1 generic parameter1 used in HeaderAuthentication and ParameterAuthentication + * @param Param2 generic parameter2 used in HeaderAuthentication and ParameterAuthentication + * @param Param3 generic parameter3 used in HeaderAuthentication and ParameterAuthentication + **/ + LPRParams(boolean enabled, String UN, String PW, String Param1, String Param2, String Param3) { + this.enabled = new Boolean(enabled); + this.UN = UN; + this.PW = PW; + this.Param1 = Param1; + this.Param2 = Param2; + this.Param3 = Param3; + } + + /** + * Constructs a newly allocated <code>XMLLoginParameterResolver.LPRParams</code> object. + * + * @param enabled enable user mapping to parameter set for the parameter set. + * @param UN username used in HTTP 401 - BasicAuthentication + * @param PW password used in HTTP 401 - BasicAuthentication + **/ + LPRParams(boolean enabled, String UN, String PW) { + this(enabled, UN, PW, null, null, null); + } + } + + /** + * Constructs a newly allocated <code>XMLLoginParameterResolver</code> object. + **/ + public XMLLoginParameterResolverEncryptedData() { + bPKMap = new HashMap(); + namedMap = new HashMap(); + } + + /** + * configuration method + * @param configuration enabled enable user mapping to parameter set for the parameter set. + */ + public void configure(String configuration, Boolean businessService) throws LoginParameterResolverException { + File idFile; + Element rootElement; + + Security.addProvider(new IAIK()); + try { + blowfishCipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding", "IAIK"); + + } catch (NoSuchPaddingException e) { + throw new LoginParameterResolverException("config.11", + new Object[] { "XMLLoginParameterResolver: NoSuchPaddingException \n" + e.toString()}); + } catch (NoSuchProviderException e) { + throw new LoginParameterResolverException("config.11", + new Object[] { "XMLLoginParameterResolver: NoSuchProviderException \n" + e.toString()}); + } catch (NoSuchAlgorithmException e) { + throw new LoginParameterResolverException("config.11", + new Object[] { "XMLLoginParameterResolver: NoSuchAlgorithmException \n" + e.toString()}); + } + + String plaintext = "start"; + String encrypted = encryptData(plaintext, "1234567890123456", "123hochgeheim"); + String decrypted = decryptData(encrypted, "1234567890123456", "123hochgeheim"); + Logger.debug("plaintext: " + plaintext); + Logger.debug("encrypted: " + encrypted); + Logger.debug("decrypted: " + decrypted); + + //make file name absolut (if it is relative to main config file) + //TODO MOAID XMLLPR check + String moaIDConfigFileName = System.getProperty(ConfigurationProvider.CONFIG_PROPERTY_NAME); + String rootConfigFileDir = new File(moaIDConfigFileName).getParent(); + this.identityFile = FileUtils.makeAbsoluteURL(configuration, rootConfigFileDir); + + if (null == identityFile || false == (idFile = new File(identityFile)).canRead()) { + throw new LoginParameterResolverException("config.11", + new Object[] { "XMLLoginParameterResolver: could not read '" + identityFile + "' " }); + } + try { + rootElement = readXMLFile(identityFile); + } catch (IOException lex) { + Logger.error(lex.toString()); + throw new LoginParameterResolverException("config.11", + new Object[] { "XMLLoginParameterResolver: could not read '" + identityFile + "' " }); + + } catch (SAXException sex) { + Logger.error(sex.toString()); + throw new LoginParameterResolverException("config.11", + new Object[] { "XMLLoginParameterResolver: parsing problem in file:'" + identityFile + "' ", sex.toString() }); + } catch (ParserConfigurationException e) { + // TODO XMLPR Auto-generated catch block + Logger.error(e.toString()); + throw new LoginParameterResolverException("config.11", + new Object[] { "XMLLoginParameterResolver: parsing problem in file:'" + identityFile + "' ", e.toString() }); + } + buildInfo(rootElement, businessService.booleanValue()); + isConfigured = true; + } + + /** + * encryptData method uses parameters masterSecret and bPK as key information to encrypt plaintext + * @param plaintext + * @param bPK + * @param masterSecret + * @return encrypted data (blowfish encrypted, base64 encoded) + * @throws LoginParameterResolverException + */ + public String encryptData(String plaintext, String bPK, String masterSecret) throws LoginParameterResolverException + { + try { + String keyString = bPK + masterSecret; + key = new iaik.security.cipher.SecretKey(keyString.getBytes("UTF-8"), "Blowfish"); + IvParameterSpec param = new IvParameterSpec(new byte [] {0,0,0,0,0,0,0,0}); + + blowfishCipher.init(Cipher.ENCRYPT_MODE, key, param); + byte [] cipherText = blowfishCipher.doFinal(plaintext.getBytes("UTF-8")); + return Base64Utils.encode(cipherText); + } catch (UnsupportedEncodingException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (InvalidKeyException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (BadPaddingException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (IllegalBlockSizeException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (IllegalStateException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (InvalidAlgorithmParameterException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (IOException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } + } + + + /** + * encryptData method uses parameters masterSecret and bPK as key information to decrypt ciphertext + * @param ciphertext (blowfish encrypted, base64encoded) + * @param bPK + * @param masterSecret + * @return decrypted Data (plaintext) + * @throws LoginParameterResolverException + */ + public String decryptData(String ciphertext, String bPK, String masterSecret) throws LoginParameterResolverException + { + try { + String keyString = bPK + masterSecret; + key = new iaik.security.cipher.SecretKey(keyString.getBytes("UTF-8"), "Blowfish"); + IvParameterSpec param = new IvParameterSpec(new byte [] {0,0,0,0,0,0,0,0}); + blowfishCipher.init(Cipher.DECRYPT_MODE, key, param); + byte [] plaintext = blowfishCipher.doFinal(Base64Utils.decode(ciphertext, true)); + return new String(plaintext); + } catch (UnsupportedEncodingException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (InvalidKeyException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (BadPaddingException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (IllegalBlockSizeException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (IllegalStateException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (InvalidAlgorithmParameterException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } catch (IOException e) { + throw new LoginParameterResolverException("config.14", new Object [] {"Blowfish: " + e.toString()}); + } + } + + + + /** + * @see at.gv.egovernment.moa.id.proxy.LoginParameterResolver#getAuthenticationHeaders(OAConfiguration, AuthenticationData, String, boolean, String) + */ + public Map getAuthenticationHeaders( + OAConfiguration oaConf, + AuthenticationData authData, + String clientIPAddress, + boolean businessService, + String publicURLPrefix) throws LoginParameterResolverException, NotAllowedException { + Map result = new HashMap(); + + if (!isConfigured) { + //TODO XMLLPR + throw new LoginParameterResolverException("XMLLoginParameterResolver with configuration '" + + identityFile + "' is not configured!", null); + } + + //get the Identity of the user + String famName = resolveValue("MOAFamilyName", authData, clientIPAddress); + String givenName = resolveValue("MOAGivenName", authData, clientIPAddress); + String dateOfBirth = resolveValue("MOADateOfBirth", authData, clientIPAddress); + String bPK =""; + String wType= ""; + if (businessService) { + bPK = resolveValue(MOAWBPK, authData, clientIPAddress); + wType = "w"; + } else { + bPK = resolveValue(MOABPK, authData, clientIPAddress); + } + String userid = ""; + String password = ""; + LPRParams params = null; + boolean userFound = false; + + //try (w)bPK and named search + params = bPKIdentitySearch(bPK, wType); + + if (null == params) + params = namedIdentitySearch(famName, givenName, dateOfBirth); + + //if both searches failed, report error. + if(null == params) + throw new NotAllowedException("User:_" + wType + "bPK:'" +bPK+ ", " + famName + ", " + givenName + "' not authorized.", null); + + //HTTP 401 - Basic Authentication + if (oaConf.getAuthType().equals("basic")) { + userid = (null != params.getUN()) ? params.getUN() : ""; + password = (null != params.getPW()) ? params.getPW() : ""; + + try { + String userIDPassword = userid + ":" + password; + String credentials = Base64Utils.encode(userIDPassword.getBytes("UTF-8")); + Logger.debug("XMLLoginParameterResolver: calculated credentials: " + credentials); + result.put("Authorization", "Basic " + credentials); + } catch (IOException ignore) { + throw new LoginParameterResolverException("config.14", new Object[] {"internal error while encoding in Base64"}); + } + } else if (oaConf.getAuthType().equals("header")) { //HTTP Authentication + String key; + String resolvedValue; + //TODO MOAID XMLLPR select value through OA-ConfigFile; + if(null != params.getUN()) result.put("UN", params.getUN()); + if(null != params.getPW()) result.put("UN", params.getPW()); + if(null != params.getParam1()) result.put("UN", params.getParam1()); + if(null != params.getParam2()) result.put("UN", params.getParam2()); + if(null != params.getParam3()) result.put("UN", params.getParam3()); + + } else { + throw new LoginParameterResolverException("config.14", new Object[] {"AuthType not supported"}); + } + + return result; + } + + /** + * @see at.gv.egovernment.moa.id.proxy.LoginParameterResolver#getAuthenticationParameters(OAConfiguration, AuthenticationData, String, boolean, String) + */ + public Map getAuthenticationParameters( + OAConfiguration oaConf, + AuthenticationData authData, + String clientIPAddress, + boolean businessService, + String publicURLPrefix) throws LoginParameterResolverException, NotAllowedException { + + Map result = new HashMap(); + + if (!isConfigured) { + Logger.warn("XMLLoginParameterResolver with configuration '" + identityFile + " is not configured"); + return result; + } + + String famName = resolveValue("MOAFamilyName", authData, clientIPAddress); + String givenName = resolveValue("MOAGivenName", authData, clientIPAddress); + String dateOfBirth = resolveValue("MOADateOfBirth", authData, clientIPAddress); + String bPK =""; + String wType= ""; + if (businessService) { + bPK = resolveValue(MOAWBPK, authData, clientIPAddress); + wType = "w"; + } else { + bPK = resolveValue(MOABPK, authData, clientIPAddress); + } + String userid = ""; + String password = ""; + LPRParams params = null; + + //try (w)bPK and named search + params = bPKIdentitySearch(bPK, wType); + + if (null == params) + params = namedIdentitySearch(famName, givenName, dateOfBirth); + + //if both searches failed, report error. + if(null == params) + throw new NotAllowedException("User:_" + wType + "bPK:'" +bPK+ ", " + famName + ", " + givenName + "' not authorized.", null); + + //TODO MOAID XMLLPR URLEncoder.encode + if (oaConf.getAuthType().equals("param")) { + try { + if(null != params.getUN()) result.put(XSD_UNATTR, URLEncoder.encode(params.getUN(),"ISO-8859-1")); + if(null != params.getPW()) result.put(XSD_PWATTR, URLEncoder.encode(params.getPW(),"ISO-8859-1")); + if(null != params.getParam1()) result.put(XSD_PARAM1ATTR, URLEncoder.encode(params.getParam1(),"ISO-8859-1")); + if(null != params.getParam2()) result.put(XSD_PARAM2ATTR, URLEncoder.encode(params.getParam2(),"ISO-8859-1")); + if(null != params.getParam3()) result.put(XSD_PARAM3ATTR, URLEncoder.encode(params.getParam3(),"ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + // ISO-8859-1 is supported + throw new LoginParameterResolverException("URLEncoder error", null); + } + } else { + throw new LoginParameterResolverException("AuthType not supported", null); + } + return result; + } + + /** + * Resolves a login header or parameter value. + * @param predicate header or parameter predicate name from online application configuration + * @param authData authentication data for current login + * @param clientIPAddress client IP address + * @return header or parameter value resolved; <code>null</code> if unknown name is given + */ + private static String resolveValue( + String predicate, + AuthenticationData authData, + String clientIPAddress) { + if (predicate.equals("MOAGivenName")) + return authData.getGivenName(); + if (predicate.equals("MOAFamilyName")) + return authData.getFamilyName(); + if (predicate.equals("MOADateOfBirth")) + return authData.getDateOfBirth(); + if (predicate.equals("MOABPK")) + return authData.getBPK(); + if (predicate.equals("MOAWBPK")) + return authData.getWBPK(); + if (predicate.equals("MOAPublicAuthority")) + if (authData.isPublicAuthority()) + return "true"; + else + return "false"; + if (predicate.equals("MOABKZ")) + return authData.getPublicAuthorityCode(); + if (predicate.equals("MOAQualifiedCertificate")) + if (authData.isQualifiedCertificate()) + return "true"; + else + return "false"; + if (predicate.equals("MOAStammzahl")) + return authData.getIdentificationValue(); + if (predicate.equals(MOAIdentificationValueType)) + return authData.getIdentificationType(); + if (predicate.equals("MOAIPAddress")) + return clientIPAddress; + else + return null; + } + + /** + * reads, parses the configuration file of XMLLoginParameterResolver and returns the document element. + * @param fileName of the configuration file. + */ + private Element readXMLFile(String fileName) throws ParserConfigurationException, SAXException, IOException { + Logger.info("XMLLoginParameterResolver: Loading and parsing XMLPLoginParameterConfiguration configuration: " + fileName); + + InputStream stream = null; + Element configElem; + + stream = new BufferedInputStream(new FileInputStream(fileName)); + configElem = DOMUtils.parseDocument(stream, true, Constants.ALL_SCHEMA_LOCATIONS, null).getDocumentElement(); + return configElem; + } + + /** + * buildInfo builds up the internal data mapping between the "Identities" and the "Parameters" from the parsed XML file. + * @param root document root element. + */ + private void buildInfo(Element root, boolean businessService) { + NodeList idList = root.getElementsByTagName(XSD_IDELEM); + NodeList paramList = root.getElementsByTagName(XSD_PARAMELEM); + String wType =""; + if (businessService) wType = "w"; + for (int i = 0; i < idList.getLength(); i++) + Logger.debug("XMLLoginParameterResolver: LocalName idList: " + idList.item(i).getLocalName()); + + for (int i = 0; i < paramList.getLength(); i++) + Logger.debug("XMLLoginParameterResolver: LocalName paramList: " + paramList.item(i).getLocalName()); + + for (int i = 0; i < idList.getLength(); i++) { + Element tmpElem = (Element) idList.item(i); + NodeList tmpList = tmpElem.getElementsByTagName(XSD_NAMEDIDELEM); + for (int j = 0; j < tmpList.getLength(); j++) + Logger.debug("XMLLoginParameterResolver: LocalName tmp: " + tmpList.item(j).getLocalName()); + + //Search for NamedIdentity Elements + if (1 == tmpList.getLength()) { + tmpElem = (Element) tmpList.item(0); + String tmpStr = tmpElem.getAttribute(XSD_SURNAMEATTR) + "," + + tmpElem.getAttribute(XSD_GIVENNAMEATTR) + "," + + tmpElem.getAttribute(XSD_BIRTHDATEATTR); + boolean tmpBool = false; + if (tmpElem.getFirstChild() != null + && "1".compareTo(tmpElem.getFirstChild().getNodeValue()) == 0) + tmpBool = true; + //TODO XMLLPR remove + Logger.debug("XMLLoginParameterResolver: tmpStr: " + tmpStr + " value: " + (new Boolean(tmpBool)).toString()); + tmpElem = (Element) paramList.item(i); + Logger.debug("XMLLoginParameterResolver: attribute UN: " + tmpElem.getAttribute(XSD_UNATTR) + + " attribute PW: " + tmpElem.getAttribute(XSD_PWATTR) + + " attribute Param1: " + tmpElem.getAttribute(XSD_PARAM1ATTR) + + " attribute Param2: " + tmpElem.getAttribute(XSD_PARAM2ATTR) + + " attribute Param3: " + tmpElem.getAttribute(XSD_PARAM3ATTR) ); + namedMap.put(tmpStr, new LPRParams(tmpBool, tmpElem.getAttribute(XSD_UNATTR), tmpElem.getAttribute(XSD_PWATTR), + tmpElem.getAttribute(XSD_PARAM1ATTR), tmpElem.getAttribute(XSD_PARAM2ATTR), + tmpElem.getAttribute(XSD_PARAM3ATTR)) ); + } else { + + //(w)bPKIdentity Elements + if (businessService) { + tmpList = tmpElem.getElementsByTagName(XSD_WBPKIDELEM); + } else { + tmpList = tmpElem.getElementsByTagName(XSD_BPKIDELEM); + } + if (1 == tmpList.getLength()) { + tmpElem = (Element) tmpList.item(0); + String tmpStr = ""; + if (businessService) { + tmpStr = tmpElem.getAttribute(XSD_WBPKATTR); + } else { + tmpStr = tmpElem.getAttribute(XSD_BPKATTR); + } + boolean tmpBool = false; + if (tmpElem.getFirstChild() != null + && "1".compareTo(tmpElem.getFirstChild().getNodeValue()) == 0) + tmpBool = true; + Logger.debug("XMLLoginParameterResolver: tmpStr: " + tmpStr + " value: " + (new Boolean(tmpBool)).toString()); + tmpElem = (Element) paramList.item(i); + Logger.debug("XMLLoginParameterResolver: attribute UN: " + tmpElem.getAttribute(XSD_UNATTR) + + " attribute PW: " + tmpElem.getAttribute(XSD_PWATTR) + + " attribute Param1: " + tmpElem.getAttribute(XSD_PARAM1ATTR) + + " attribute Param2: " + tmpElem.getAttribute(XSD_PARAM2ATTR) + + " attribute Param3: " + tmpElem.getAttribute(XSD_PARAM3ATTR) ); + namedMap.put(tmpStr, new LPRParams(tmpBool, tmpElem.getAttribute(XSD_UNATTR), tmpElem.getAttribute(XSD_PWATTR), + tmpElem.getAttribute(XSD_PARAM1ATTR), tmpElem.getAttribute(XSD_PARAM2ATTR), + tmpElem.getAttribute(XSD_PARAM3ATTR)) ); + } else { + if (businessService) { + Logger.warn("XMLLoginParameterResolver: wrong format no Elements " + XSD_NAMEDIDELEM + " or " + XSD_WBPKIDELEM + " found"); + } else { + Logger.warn("XMLLoginParameterResolver: wrong format no Elements " + XSD_NAMEDIDELEM + " or " + XSD_BPKIDELEM + " found"); + } + } + } + } + + Logger.debug("namedMap:" + namedMap.toString()); + Logger.debug(wType + "bPKMap:" + bPKMap.toString()); + } + + + + + /** + * searches for a given bPK and returns the appropriate LPRParams structure + * @param bPK search argument + * @return LPRParams if bPK could be found in internal mappings or null otherwise. + */ + LPRParams bPKIdentitySearch(String bPK, String wType) { + //search for mapping with (w)bPK of the user + Logger.info("XMLLoginParameterResolver: search for login data mapped to " + wType + "bPK:" + bPK); + LPRParams params = (LPRParams) bPKMap.get(bPK); + if (null == params) { + Logger.info("XMLLoginParameterResolver: params for " + wType + "bPK: " + bPK + " not found!"); + return null; + } else if (params.getEnabled()) { + Logger.info("XMLLoginParameterResolver: " + wType + "bPK: " + bPK + "found in list; user is enabled"); + Logger.debug("XMLLoginParameterResolver: using: " + params.toString()); + return params; + } + Logger.info("XMLLoginParameterResolver: " + wType + "bPK: " + bPK + "found in list but user is NOT enabled"); + return null; + } + + /** + * searches for a given namedIdentity and returns the appropriate LPRParams structure + * @param surName surname search argument + * @param givenName givenname search argument + * @param dateOfBirth dateofbirth search argument + * @return LPRParams if (w)bPK could be found in internal mappings or null otherwise. + */ + LPRParams namedIdentitySearch(String surName, String givenName, String dateOfBirth) { + Logger.info("XMLLoginParameterResolver: search for login data for SurName:" + surName + " GivenName: " + givenName + " DateOfBirth" + dateOfBirth); + //try first a search with surname, givenname and birthdate + LPRParams params = (LPRParams) namedMap.get(surName + "," + givenName + "," + dateOfBirth); + if (null == params) { + Logger.debug("XMLLoginParameterResolver: params for Surname: " + surName + " GivenName: " + givenName + "BirthDate: " + dateOfBirth + " not found!"); + //try a search with surname, givenname only + params = (LPRParams) namedMap.get(surName + "," + givenName + "," + XSD_BIRTHDATEBLANKATTR); + if(null == params) { + Logger.debug("XMLLoginParameterResolver: params for Surname: " + surName + " GivenName: " + givenName + " not found!"); + return null; + } + } + + if (params.getEnabled()) { + Logger.info("XMLLoginParameterResolver: Surname:" + surName + " GivenName: " + givenName + " found in list; user is enabled"); + Logger.debug("XMLLoginParameterResolver: using: " + params.toString()); + return params; + } + Logger.info("XMLLoginParameterResolver: SurName:" + surName + " GivenName: " + givenName + "found in list; user is NOT enabled"); + return null; + } + + //public static final String XSD_MAPPING = "Mapping"; + //public static final String XSD_DOCELEM = "MOAIdentities"; + public static final String XSD_IDELEM = "Identity"; + public static final String XSD_NAMEDIDELEM = "NamedIdentity"; + public static final String XSD_BPKIDELEM = "bPKIdentity"; + public static final String XSD_WBPKIDELEM = "wbPKIdentity"; + public static final String XSD_PARAMELEM = "Parameters"; + public static final String XSD_SURNAMEATTR = "SurName"; + public static final String XSD_GIVENNAMEATTR = "GivenName"; + public static final String XSD_BIRTHDATEATTR = "BirthDate"; + public static final String XSD_BIRTHDATEBLANKATTR = "any"; + public static final String XSD_BPKATTR = "bPK"; + public static final String XSD_WBPKATTR = "wbPK"; + public static final String XSD_UNATTR = "UN"; + public static final String XSD_PWATTR = "PW"; + public static final String XSD_PARAM1ATTR = "Param1"; + public static final String XSD_PARAM2ATTR = "Param2"; + public static final String XSD_PARAM3ATTR = "Param3"; + private Map bPKMap; + private Map namedMap; + private boolean isConfigured = false; +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/XMLLoginParameterResolverPlainData.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/XMLLoginParameterResolverPlainData.java new file mode 100644 index 000000000..aedafdf85 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/XMLLoginParameterResolverPlainData.java @@ -0,0 +1,422 @@ +package at.gv.egovernment.moa.id.proxy; + +import at.gv.egovernment.moa.id.config.proxy.OAConfiguration; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.Base64Utils; +import java.io.IOException; +import java.util.*; +import org.apache.xerces.parsers.DOMParser; +import org.w3c.dom.*; + +// Referenced classes of package at.gv.egovernment.moa.id.proxy: +// +// TODO MOA-ID test full functionality + +public class XMLLoginParameterResolverPlainData + implements LoginParameterResolver +{ + private String configuration; + + /** + * inner class used to store mapped parameters + */ + class LPRParams { + + /** + * getter method for parameter Enabled. + * Parameter Enabled decides if mapped parameters should be used by XMLLoginParameterResolver + */ + public boolean getEnabled() { + return enabled.booleanValue(); + } + + /** + * getter method for parameter UN (username) + * @return Parameter UN or <code>null</code> not set. + */ + public String getUN() { + return UN; + } + + /** + * getter method for parameter UN (username) + * @return Parameter UN or <code>null</code> not set. + */ + public String getPlainUN() { + return UN; + } + + + /** + * getter method for parameter PW (password) + * @return Parameter PW or <code>null</code> not set. + */ + public String getPW() { + return PW; + } + + /** + * getter method for generic parameter Param1 + * @return Parameter Param1 or <code>null</code> not set. + */ + public String getParam1() { + return Param1; + } + + /** + * getter method for generic parameter Param2 + * @return Parameter Param2 or <code>null</code> not set. + */ + public String getParam2() { + return Param2; + } + + /** + * getter method for generic parameter Param3 + * @return Parameter Param3 or <code>null</code> not set. + */ + public String getParam3() { + return Param3; + } + + /** + * Returns a string representation of LPRParams + * + * @return a <code>String</code> representation of this object. + * @see XMLLoginParameterResolver.LPRParams + */ + public String toString() { + return "Enabled: " + + enabled.toString() + + "UN: '" + + UN + + "' PW: '" + + PW + + "' Param1: '" + + Param1 + + "' Param2: '" + + Param2 + + "' Param3: '" + + Param3 + + "'\n"; + } + + //private member variables used to store the parameters + private Boolean enabled = null; + private String UN = null; + private String PW = null; + private String Param1 = null; + private String Param2 = null; + private String Param3 = null; + + /** + * Constructs a newly allocated <code>XMLLoginParameterResolver.LPRParams</code> object. + * + * @param enabled enable user mapping to parameter set for the parameter set. + * @param UN username used in HTTP 401 - BasicAuthentication + * @param PW password used in HTTP 401 - BasicAuthentication + * @param Param1 generic parameter1 used in HeaderAuthentication and ParameterAuthentication + * @param Param2 generic parameter2 used in HeaderAuthentication and ParameterAuthentication + * @param Param3 generic parameter3 used in HeaderAuthentication and ParameterAuthentication + **/ + LPRParams(boolean enabled, String UN, String PW, String Param1, String Param2, String Param3) { + this.enabled = new Boolean(enabled); + this.UN = UN; + this.PW = PW; + this.Param1 = Param1; + this.Param2 = Param2; + this.Param3 = Param3; + } + + /** + * Constructs a newly allocated <code>XMLLoginParameterResolver.LPRParams</code> object. + * + * @param enabled enable user mapping to parameter set for the parameter set. + * @param UN username used in HTTP 401 - BasicAuthentication + * @param PW password used in HTTP 401 - BasicAuthentication + **/ + LPRParams(boolean enabled, String UN, String PW) { + this(enabled, UN, PW, null, null, null); + } + } + + //TODO document + public XMLLoginParameterResolverPlainData() + { + bPKMap = new HashMap(); + namedMap = new HashMap(); + + } + + //TODO document + public Map getAuthenticationHeaders(OAConfiguration oaConf, AuthenticationData authData, String clientIPAddress, boolean businessService, String publicURLPrefix) throws NotAllowedException + { + Map result = new HashMap(); + if(oaConf.getAuthType().equals("basic")) + { + String famName = resolveValue(MOAFamilyName, authData, clientIPAddress); + String givenName = resolveValue(MOAGivenName, authData, clientIPAddress); + String dateOfBirth = resolveValue(MOADateOfBirth, authData, clientIPAddress); + String bPK =""; + String wType= ""; + if (businessService) { + bPK = resolveValue(MOAWBPK, authData, clientIPAddress); + wType = "w"; + } else { + bPK = resolveValue(MOABPK, authData, clientIPAddress); + } + String userid = ""; + String password = ""; + String param1 = ""; + String param2 = ""; + String param3 = ""; + + LPRParams params = null; + boolean userFound = false; + + //first step: search for (w)bPK entry in user list + Logger.debug("XMLLoginParameterResolverPlainData: search for automatic login data for "+ wType + "bPK:" + bPK); + params = (LPRParams)bPKMap.get(bPK); + if(params == null) + Logger.debug("XMLLoginParameterResolverPlainData: params for "+ wType + "bPK: " + bPK + " not found in file!"); + else + if(params.getEnabled()) + { //if user is enabled: get related parameters + Logger.debug("XMLLoginParameterResolverPlainData: "+ wType + "bPK: " + bPK + " found in list; user is enabled"); + Logger.debug("XMLLoginParameterResolverPlainData: using: " + params.toString()); + userid = params.getUN(); + password = params.getPW(); + param1 = params.getParam1(); + param2 = params.getParam2(); + param3 = params.getParam3(); + userFound = true; + } else + { + Logger.info("XMLLoginParameterResolverPlainData: "+ wType + "bPK: " + bPK + " found in list; user is NOT enabled"); + } + if(!userFound) //secound step: search for name entry in user list + { + Logger.debug("XMLLoginParameterResolverPlainData: search for automatic login data for SurName:" + famName + " GivenName: " + givenName + " DateOfBirth: " + dateOfBirth); + params = (LPRParams)namedMap.get(famName + "," + givenName + "," + dateOfBirth); + if(params == null) { + Logger.debug("XMLLoginParameterResolverPlainData: params for Surname: " + famName + " GivenName: " + givenName + " DateOfBirth: " + dateOfBirth + " not found in file!"); + //try also with wildcard ("*") birthdate + params = (LPRParams)namedMap.get(famName + "," + givenName + "," + "*"); + if(params != null) Logger.debug("XMLLoginParameterResolverPlainData: params for Surname: " + famName + " GivenName: " + givenName + " DateOfBirth: " + "*" + " found!"); + } + + if(null != params && params.getEnabled()) + { + Logger.debug("XMLLoginParameterResolverPlainData: SurName:" + famName + " GivenName: " + givenName + " DateOfBirth: " + dateOfBirth + " found in file; user is enabled"); + Logger.debug("XMLLoginParameterResolverPlainData: using: " + params.toString()); + userid = params.getUN(); + password = params.getPW(); + param1 = params.getParam1(); + param2 = params.getParam2(); + param3 = params.getParam3(); + userFound = true; + } + } + if(!userFound) //third step: search for default user in user list + { + //third step: search for (w)bPK for the default user entry in user list + Logger.debug("XMLLoginParameterResolverPlainData: search for automatic login data for default user"); + params = (LPRParams)bPKMap.get("default"); + if(params == null) + Logger.debug("XMLLoginParameterResolverPlainData: params for default user not found in file!"); + else + if(params.getEnabled()) + { //if user is enabled: get related parameters + Logger.debug("XMLLoginParameterResolverPlainData: default user found in list; user is enabled"); + Logger.debug("XMLLoginParameterResolverPlainData: using: " + params.toString()); + userid = params.getUN(); + password = params.getPW(); + param1 = params.getParam1(); + param2 = params.getParam2(); + param3 = params.getParam3(); + userFound = true; + } else + { + Logger.info("XMLLoginParameterResolverPlainData: default user found in list; user is NOT enabled"); + } + } + + if(!userFound) //if user is not found then throw NotAllowedException exception + { + //TODO MOA-ID proove this with testcases! + Logger.info("XMLLoginParameterResolverPlainData: Person is not allowed No automatic login"); + throw new NotAllowedException("XMLLoginParameterResolverPlainData: Person is not allowed No automatic login ", new Object[] { }); + } + try //if user was found: generate Authorization header entry with associated credemtials + { + String userIDPassword = userid + ":" + password; + String credentials = Base64Utils.encode(userIDPassword.getBytes()); + Logger.debug("XMLLoginParameterResolverPlainData: calculated credentials: " + credentials); + result.put("Authorization", "Basic " + credentials); + } + catch(IOException ignore) { } + } else + if(oaConf.getAuthType().equals("header")) + { + String key; + String resolvedValue; + for(Iterator iter = oaConf.getHeaderAuthMapping().keySet().iterator(); iter.hasNext(); result.put(key, resolvedValue)) + { + key = (String)iter.next(); + String predicate = (String)oaConf.getHeaderAuthMapping().get(key); + resolvedValue = resolveValue(predicate, authData, clientIPAddress); + } + + } + return result; + } + + public Map getAuthenticationParameters(OAConfiguration oaConf, AuthenticationData authData, String clientIPAddress, boolean businessService, String publicURLPrefix) + { + Map result = new HashMap(); + if(oaConf.getAuthType().equals("param")) + { + String key; + String resolvedValue; + for(Iterator iter = oaConf.getParamAuthMapping().keySet().iterator(); iter.hasNext(); result.put(key, resolvedValue)) + { + key = (String)iter.next(); + String predicate = (String)oaConf.getParamAuthMapping().get(key); + resolvedValue = resolveValue(predicate, authData, clientIPAddress); + } + + } + return result; + } + + private static String resolveValue(String predicate, AuthenticationData authData, String clientIPAddress) + { + if(predicate.equals(MOAGivenName)) + return authData.getGivenName(); + if(predicate.equals(MOAFamilyName)) + return authData.getFamilyName(); + if(predicate.equals(MOADateOfBirth)) + return authData.getDateOfBirth(); + if(predicate.equals(MOABPK)) + return authData.getBPK(); + if(predicate.equals(MOAWBPK)) + return authData.getWBPK(); + if(predicate.equals(MOAPublicAuthority)) + if(authData.isPublicAuthority()) + return "true"; + else + return "false"; + if(predicate.equals(MOABKZ)) + return authData.getPublicAuthorityCode(); + if(predicate.equals(MOAQualifiedCertificate)) + if(authData.isQualifiedCertificate()) + return "true"; + else + return "false"; + if(predicate.equals(MOAStammzahl)) + return authData.getIdentificationValue(); + if (predicate.equals(MOAIdentificationValueType)) + return authData.getIdentificationType(); + if(predicate.equals(MOAIPAddress)) + return clientIPAddress; + else + return null; + } + + private Document readXMLFile(String fileName) throws LoginParameterResolverException + { + Logger.info("XMLLoginParameterResolverPlainData: Loading MOA-OA configuration " + fileName); + DOMParser parser = new DOMParser(); + try + { + parser.setFeature("http://xml.org/sax/features/validation", true); + parser.setFeature("http://apache.org/xml/features/validation/schema", true); + parser.parse(fileName); + return parser.getDocument(); + } + catch(Exception e) + { + String msg = e.toString(); + throw new LoginParameterResolverException("proxy.13", new Object[] {"<noURL>: XMLLoginParameterResolverPlainData: Error parsing file " + fileName, "detail problem: " + msg}); + } + } + + private void buildInfo(Document doc, boolean businessService) + { + Element root = doc.getDocumentElement(); + NodeList idList = root.getElementsByTagName("Identity"); + NodeList paramList = root.getElementsByTagName("Parameters"); + String wType =""; + if (businessService) wType = "w"; + for(int i = 0; i < idList.getLength(); i++) + Logger.debug("XMLLoginParameterResolverPlainData: LocalName idList: " + idList.item(i).getLocalName()); + + for(int i = 0; i < paramList.getLength(); i++) + Logger.debug("XMLLoginParameterResolverPlainData: LocalName paramList: " + paramList.item(i).getLocalName()); + + for(int i = 0; i < idList.getLength(); i++) + { + Element tmpElem = (Element)idList.item(i); + NodeList tmpList = tmpElem.getElementsByTagName("NamedIdentity"); + for(int j = 0; j < tmpList.getLength(); j++) + Logger.debug("XMLLoginParameterResolverPlainData: LocalName tmp: " + tmpList.item(j).getLocalName()); + + if(1 == tmpList.getLength()) + { + tmpElem = (Element)tmpList.item(0); + String tmpStr = tmpElem.getAttribute("SurName") + "," + tmpElem.getAttribute("GivenName") + "," + tmpElem.getAttribute("BirthDate"); + boolean tmpBool = false; + if(tmpElem.getFirstChild() != null && "1".compareTo(tmpElem.getFirstChild().getNodeValue()) == 0) + tmpBool = true; + Logger.debug("XMLLoginParameterResolverPlainData: tmpStr: " + tmpStr + " value: " + (new Boolean(tmpBool)).toString()); + tmpElem = (Element)paramList.item(i); + Logger.debug("XMLLoginParameterResolverPlainData: attribute UN: " + tmpElem.getAttribute("UN") + " attribute PW: " + tmpElem.getAttribute("PW")); + namedMap.put(tmpStr, new LPRParams(tmpBool, tmpElem.getAttribute("UN"), tmpElem.getAttribute("PW"))); + } else + { + tmpList = tmpElem.getElementsByTagName(wType + "bPKIdentity"); + if(1 == tmpList.getLength()) + { + tmpElem = (Element)tmpList.item(0); + String tmpStr = tmpElem.getAttribute(wType + "bPK"); + boolean tmpBool = false; + if(tmpElem.getFirstChild() != null && "1".compareTo(tmpElem.getFirstChild().getNodeValue()) == 0) + tmpBool = true; + Logger.debug("XMLLoginParameterResolverPlainData: tmpStr: " + tmpStr + " value: " + (new Boolean(tmpBool)).toString()); + tmpElem = (Element)paramList.item(i); + Logger.debug("XMLLoginParameterResolverPlainData: attribute UN: " + tmpElem.getAttribute("UN") + " attribute PW: " + tmpElem.getAttribute("PW") + " attribute Param1: " + tmpElem.getAttribute("Param1")); + bPKMap.put(tmpStr, new LPRParams(tmpBool, tmpElem.getAttribute("UN"), tmpElem.getAttribute("PW"))); + } else + { + Logger.warn("XMLLoginParameterResolverPlainData: wrong format or incorrect mode; no NamedIdentity or " + wType + "bPKIdentity found"); + } + } + } + + Logger.debug("namedMap:" + namedMap.toString()); + Logger.debug(wType + "bPKMap:" + bPKMap.toString()); + } + + //public static final String XSD_DOCELEM = "MOAIdentities"; + //public static final String XSD_IDELEM = "Identity"; + //public static final String XSD_NAMEDIDELEM = "NamedIdentity"; + //public static final String XSD_BPKIDELEM = "bPKIdentity"; + //public static final String XSD_PARAMELEM = "Parameters"; + //public static final String XML_LPR_CONFIG_PROPERTY_NAME1 = "moa.id.xmllpr1.configuration"; + private Map bPKMap; + private Map namedMap; + + + public void configure(String configuration, Boolean businessService) throws LoginParameterResolverException { + Logger.info("XMLLoginParameterResolverPlainData: initialization string: " + configuration); + this.configuration = configuration; + String fileName = configuration; + if(fileName == null) { + fileName = "file:conf/moa-id/Identities.xml"; + Logger.info("XMLLoginParameterResolverPlainData: used file name string: " + fileName); + } + Document doc = readXMLFile(fileName); + buildInfo(doc, businessService.booleanValue() ); + } +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/builder/SAMLRequestBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/builder/SAMLRequestBuilder.java new file mode 100644 index 000000000..e0e1fde4a --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/builder/SAMLRequestBuilder.java @@ -0,0 +1,55 @@ +package at.gv.egovernment.moa.id.proxy.builder; + +import java.text.MessageFormat; +import java.util.Calendar; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.DateTimeUtils; + +/** + * Builder for the <code><samlp:Request></code> used for querying + * the authentication data <code><saml:Assertion></code>. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class SAMLRequestBuilder implements Constants { + /** samlp-Request template */ + private static final String REQUEST = + "<samlp:Request xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\" RequestID=\"{0}\" MajorVersion=\"1\" MinorVersion=\"0\" IssueInstant=\"{1}\">" + + "<samlp:AssertionArtifact>{2}</samlp:AssertionArtifact>" + + "</samlp:Request>"; + + /** + * Constructor for SAMLRequestBuilder. + */ + public SAMLRequestBuilder() { + super(); + } + + /** + * Builds the <code><samlp:Request></code>. + * @param requestID request ID + * @param samlArtifactBase64 SAML artifact, encoded BASE64 + * @return the DOM element + */ + public Element build(String requestID, String samlArtifactBase64) throws BuildException { + try { + String issueInstant = DateTimeUtils.buildDateTime(Calendar.getInstance()); + String request = MessageFormat.format(REQUEST, new Object[] {requestID, issueInstant, samlArtifactBase64}); + Element requestElem = DOMUtils.parseDocument(request, false, ALL_SCHEMA_LOCATIONS, null).getDocumentElement(); + return requestElem; + } + catch (Throwable ex) { + throw new BuildException( + "builder.00", + new Object[] {"samlp:Request", ex.toString()}, + ex); + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/invoke/GetAuthenticationDataInvoker.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/invoke/GetAuthenticationDataInvoker.java new file mode 100644 index 000000000..f7e5299ab --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/invoke/GetAuthenticationDataInvoker.java @@ -0,0 +1,143 @@ +package at.gv.egovernment.moa.id.proxy.invoke; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +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.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.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.id.data.SAMLStatus; +import at.gv.egovernment.moa.id.proxy.builder.SAMLRequestBuilder; +import at.gv.egovernment.moa.id.proxy.parser.SAMLResponseParser; +import at.gv.egovernment.moa.id.proxy.servlet.ProxyException; +import at.gv.egovernment.moa.id.util.Random; + +/** + * Invoker of + * <ul> + * <li>either the GetAuthenticationData web service of MOA-ID Auth</li> + * <li>or the API call {@link at.gv.egovernment.moa.id.auth.AuthenticationServer#getAuthenticationData},</li> + * </ul> + * depending of the configuration. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class GetAuthenticationDataInvoker { + /** Create a new QName object for the webservice endpoint */ + private static final QName SERVICE_QNAME = new QName("GetAuthenticationData"); + + /** invoked object for API call of MOA-ID Auth */ + private static Object apiServer = null; + /** invoked method for API call of MOA-ID Auth */ + private static Method apiMethod = null; + + /** + * Invokes the service passing domain model objects. + * @param samlArtifact SAML artifact + * @return AuthenticationData object + * @throws ServiceException on any exception thrown + */ + /** + * Get authentication data from the MOA-ID Auth component, + * either via API call or via web service call. + * @param samlArtifact SAML artifact to be used as a parameter + * @return AuthenticationData + */ + public AuthenticationData getAuthenticationData(String samlArtifact) + throws ConfigurationException, ProxyException, BuildException, ServiceException, ParseException, AuthenticationException { + + ConnectionParameter authConnParam = + ProxyConfigurationProvider.getInstance().getAuthComponentConnectionParameter(); + if (authConnParam == null) { + try { + if (apiServer == null) { + Class serverClass = Class.forName("at.gv.egovernment.moa.id.auth.AuthenticationServer"); + Method getInstanceMethod = serverClass.getMethod("getInstance", (Class[]) null); + apiServer = getInstanceMethod.invoke(null, (Object[]) null); + apiMethod = serverClass.getMethod( + "getAuthenticationData", new Class[] {String.class}); + } + AuthenticationData authData = (AuthenticationData)apiMethod.invoke(apiServer, new Object[] {samlArtifact}); + return authData; + } + catch (InvocationTargetException ex) { + Throwable targetEx = ex.getTargetException(); + if (targetEx instanceof AuthenticationException) + throw (AuthenticationException) targetEx; + else + throw new ProxyException("proxy.09", new Object[] {targetEx.toString()}); + } + catch (Throwable ex) { + throw new ProxyException("proxy.09", new Object[] {ex.toString()}); + } + } + else { + Element samlpRequest = new SAMLRequestBuilder().build(Random.nextRandom(), samlArtifact); + Element samlpResponse = getAuthenticationData(samlpRequest); + SAMLResponseParser srp = new SAMLResponseParser(samlpResponse); + SAMLStatus status = srp.parseStatusCode(); + if (! "samlp:Success".equals(status.getStatusCode())) { + // on error status throw exception + String code = status.getStatusCode(); + if (status.getSubStatusCode() != null && status.getSubStatusCode().length() > 0) + code += "(" + status.getSubStatusCode() + ")"; + throw new ServiceException("service.02", new Object[] {code, status.getStatusMessage()}); + } + return srp.parseAuthenticationData(); + } + } + + /** + * Invokes the service passing DOM elements. + * @param request request DOM element + * @return response DOM element + * @throws ServiceException on any exception thrown + */ + public Element getAuthenticationData(Element request) throws ServiceException { + try { + Service service = ServiceFactory.newInstance().createService(SERVICE_QNAME); + Call call = service.createCall(); + SOAPBodyElement body = + new SOAPBodyElement(request); + SOAPBodyElement[] params = new SOAPBodyElement[] {body}; + Vector responses; + SOAPBodyElement response; + + String endPoint; + ConnectionParameter authConnParam = + ProxyConfigurationProvider.getInstance().getAuthComponentConnectionParameter(); + + //If the ConnectionParameter do NOT exist, we throw an exception .... + if (authConnParam!=null) { + endPoint = authConnParam.getUrl(); + call.setTargetEndpointAddress(endPoint); + responses = (Vector) call.invoke(SERVICE_QNAME, params); + response = (SOAPBodyElement) responses.get(0); + return response.getAsDOM(); + } + else + { + throw new ServiceException("service.01", null); + } + } + catch (Exception ex) { + throw new ServiceException("service.00", new Object[] {ex.toString()}, ex); + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/parser/AuthenticationDataAssertionParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/parser/AuthenticationDataAssertionParser.java new file mode 100644 index 000000000..6bf5da02b --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/parser/AuthenticationDataAssertionParser.java @@ -0,0 +1,158 @@ +package at.gv.egovernment.moa.id.proxy.parser; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.auth.builder.BPKBuilder; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.util.BoolUtils; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Parser for the <code><saml:Assertion></code> returned by the + * <code>GetAuthenticationData</code> web service. + * @author Paul Ivancsics + * @version $Id$ + */ +public class AuthenticationDataAssertionParser implements Constants { + + /** Prefix for SAML-Xpath-expressions */ + private static String SAML = SAML_PREFIX + ":"; + /** Prefix for PersonData-Xpath-expressions */ + private static String PR = PD_PREFIX + ":"; + /** Prefix for Attribute MajorVersion in an Xpath-expression */ + private static String MAJOR_VERSION_XPATH = + "@MajorVersion"; + /** Prefix for Attribute MinorVersion in an Xpath-expression */ + private static String MINOR_VERSION_XPATH = + "@MinorVersion"; + /** Prefix for Attribute AssertionID in an Xpath-expression */ + private static String ASSERTION_ID_XPATH = + "@AssertionID"; + /** Prefix for Attribute Issuer in an Xpath-expression */ + private static String ISSUER_XPATH = + "@Issuer"; + /** Prefix for Attribute IssueInstant in an Xpath-expression */ + private static String ISSUE_INSTANT_XPATH = + "@IssueInstant"; + /** Prefix for Element AttributeStatement in an Xpath-expression */ + private static String ATTRIBUTESTATEMENT_XPATH = + SAML + "AttributeStatement/"; + /** Prefix for Element NameIdentifier in an Xpath-expression */ + private static String PK_XPATH = + ATTRIBUTESTATEMENT_XPATH + + SAML + "Subject/" + + SAML + "NameIdentifier"; + private static String NAME_QUALIFIER_XPATH = + PK_XPATH + "/@NameQualifier"; + /** Prefix for Element Person in an Xpath-expression */ + private static String PERSONDATA_XPATH = + ATTRIBUTESTATEMENT_XPATH + + SAML + "Attribute[@AttributeName=\"PersonData\"]/" + + SAML + "AttributeValue/" + + PR + "Person/"; + /** Prefix for Element Value in an Xpath-expression */ + private static String IDENTIFICATION_VALUE_XPATH = + PERSONDATA_XPATH + + PR + "Identification/" + + PR + "Value"; + private static String IDENTIFICATION_TYPE_XPATH = + PERSONDATA_XPATH + + PR + "Identification/" + + PR + "Type"; + /** Prefix for Element GivenName in an Xpath-expression */ + private static String GIVEN_NAME_XPATH = + PERSONDATA_XPATH + + PR + "Name/" + + PR + "GivenName"; + /** Prefix for Element FamilyName in an Xpath-expression */ + private static String FAMILY_NAME_XPATH = + PERSONDATA_XPATH + + PR + "Name/" + + PR + "FamilyName"; + /** Prefix for Element DateOfBirth in an Xpath-expression */ + private static String DATE_OF_BIRTH_XPATH = + PERSONDATA_XPATH + + PR + "DateOfBirth"; + /** Prefix for Element AttributeValue in an Xpath-expression */ + private static String IS_QUALIFIED_CERT_XPATH = + ATTRIBUTESTATEMENT_XPATH + + SAML + "Attribute[@AttributeName=\"isQualifiedCertificate\"]/" + + SAML + "AttributeValue"; + /** Prefix for Element AttributeValue in an Xpath-expression */ + private static String PUBLIC_AUTHORITY_XPATH = + ATTRIBUTESTATEMENT_XPATH + + SAML + "Attribute[@AttributeName=\"isPublicAuthority\"]/" + + SAML + "AttributeValue"; + /** Element samlAssertion represents the SAML:Assertion */ + private Element samlAssertion; + + /** + * Constructor + * @param samlAssertion samlpResponse the <code><samlp:Response></code> as a DOM element + */ + public AuthenticationDataAssertionParser(Element samlAssertion) { + this.samlAssertion = samlAssertion; + } + + /** + * Parses the <code><saml:Assertion></code>. + * @return <code>AuthenticationData</code> object + * @throws ParseException on any error + */ + public AuthenticationData parseAuthenticationData() + throws ParseException { + + try { + AuthenticationData authData = new AuthenticationData(); + //ÄNDERN: NUR der Identification-Teil + authData.setSamlAssertion(DOMUtils.serializeNode(samlAssertion)); + authData.setMajorVersion(new Integer( + XPathUtils.getAttributeValue(samlAssertion, MAJOR_VERSION_XPATH, "-1")).intValue()); + authData.setMinorVersion(new Integer( + XPathUtils.getAttributeValue(samlAssertion, MINOR_VERSION_XPATH, "-1")).intValue()); + authData.setAssertionID( + XPathUtils.getAttributeValue(samlAssertion, ASSERTION_ID_XPATH, "")); + authData.setIssuer( + XPathUtils.getAttributeValue(samlAssertion, ISSUER_XPATH, "")); + authData.setIssueInstant( + XPathUtils.getAttributeValue(samlAssertion, ISSUE_INSTANT_XPATH, "")); + String pkValue = XPathUtils.getElementValue(samlAssertion, PK_XPATH, ""); + if (XPathUtils.getAttributeValue(samlAssertion, NAME_QUALIFIER_XPATH, "").equalsIgnoreCase(URN_PREFIX_BPK)) { + authData.setBPK(pkValue); + } else { + authData.setWBPK(pkValue); + } + authData.setIdentificationValue( + XPathUtils.getElementValue(samlAssertion, IDENTIFICATION_VALUE_XPATH, "")); + authData.setIdentificationType( + XPathUtils.getElementValue(samlAssertion, IDENTIFICATION_TYPE_XPATH, "")); + authData.setGivenName( + XPathUtils.getElementValue(samlAssertion, GIVEN_NAME_XPATH, "")); + authData.setFamilyName( + XPathUtils.getElementValue(samlAssertion, FAMILY_NAME_XPATH, "")); + authData.setDateOfBirth( + XPathUtils.getElementValue(samlAssertion, DATE_OF_BIRTH_XPATH, "")); + authData.setQualifiedCertificate(BoolUtils.valueOf( + XPathUtils.getElementValue(samlAssertion, IS_QUALIFIED_CERT_XPATH, ""))); + String publicAuthority = + XPathUtils.getElementValue(samlAssertion, PUBLIC_AUTHORITY_XPATH, null); + if (publicAuthority == null) { + authData.setPublicAuthority(false); + authData.setPublicAuthorityCode(""); + } + else { + authData.setPublicAuthority(true); + if (! publicAuthority.equalsIgnoreCase("true")) + authData.setPublicAuthorityCode(publicAuthority); + } + return authData; + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString() }, t); + } + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/parser/SAMLResponseParser.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/parser/SAMLResponseParser.java new file mode 100644 index 000000000..9f77578fd --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/parser/SAMLResponseParser.java @@ -0,0 +1,100 @@ +package at.gv.egovernment.moa.id.proxy.parser; + +import org.w3c.dom.Element; + +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.id.data.SAMLStatus; +import at.gv.egovernment.moa.util.Constants; +import at.gv.egovernment.moa.util.XPathUtils; + +/** + * Parser for the <code><samlp:Response></code> returned by the + * <code>GetAuthenticationData</code> web service. + * @author Paul Ivancsics + * @version $Id$ + */ +public class SAMLResponseParser implements Constants { + /** Element containing the samlResponse */ + private Element samlResponse; + /** Xpath prefix for reaching SAMLP Namespaces */ + private static String SAMLP = SAMLP_PREFIX + ":"; + /** Xpath prefix for reaching SAML Namespaces */ + private static String SAML = SAML_PREFIX + ":"; + /** Xpath prefix for reaching PersonData Namespaces */ + private static String PR = PD_PREFIX + ":"; + /** Xpath expression for reaching the SAMLP:Response element */ + private static final String ROOT = + "/" + SAMLP + "Response/"; + /** Xpath expression for reaching the SAMLP:Status element */ + private static final String STATUS_XPATH = + ROOT + + SAMLP + "Status/"; + /** Xpath expression for reaching the SAMLP:StatusCode_Value attribute */ + private static final String STATUSCODE_XPATH = + STATUS_XPATH + + SAMLP + "StatusCode/@Value"; + /** Xpath expression for reaching the SAMLP:SubStatusCode_Value attribute */ + private static final String SUBSTATUSCODE_XPATH = + STATUS_XPATH + + SAMLP + "StatusCode/" + + SAMLP + "StatusCode/@Value"; + /** Xpath expression for reaching the SAMLP:StatusMessage element */ + private static final String STATUSMESSAGE_XPATH = + STATUS_XPATH + + SAMLP + "StatusMessage"; + /** Xpath expression for reaching the SAML:Assertion element */ + private static String ASSERTION_XPATH = + ROOT + + SAML + "Assertion"; + + /** + * Constructor + * @param samlResponse the <code><samlp:Response></code> as a DOM element + */ + public SAMLResponseParser(Element samlResponse) { + this.samlResponse = samlResponse; + } + + /** + * Parses the <code><samlp:StatusCode></code> from the <code><samlp:Response></code>. + * @return <code>AuthenticationData</code> object + * @throws ParseException on any parsing error + */ + public SAMLStatus parseStatusCode() + throws ParseException { + + SAMLStatus status = new SAMLStatus(); + try { + status.setStatusCode( + XPathUtils.getAttributeValue(samlResponse, STATUSCODE_XPATH, "")); + status.setSubStatusCode( + XPathUtils.getAttributeValue(samlResponse, SUBSTATUSCODE_XPATH, "")); + status.setStatusMessage( + XPathUtils.getElementValue(samlResponse, STATUSMESSAGE_XPATH, "")); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString() }, t); + } + return status; + } + + /** + * Parses the <code><saml:Assertion></code> from the <code><samlp:Response></code>. + * @return <code>AuthenticationData</code> object + * @throws ParseException on any parsing error + */ + public AuthenticationData parseAuthenticationData() + throws ParseException { + + Element samlAssertion; + try { + samlAssertion = (Element)XPathUtils.selectSingleNode(samlResponse, ASSERTION_XPATH); + } + catch (Throwable t) { + throw new ParseException("parser.01", new Object[] { t.toString() }, t); + } + return new AuthenticationDataAssertionParser(samlAssertion).parseAuthenticationData(); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ConfigurationServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ConfigurationServlet.java new file mode 100644 index 000000000..4ab2e2cf7 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ConfigurationServlet.java @@ -0,0 +1,71 @@ +package at.gv.egovernment.moa.id.proxy.servlet; + +import java.io.IOException; +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; + +import javax.servlet.ServletConfig; +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.proxy.MOAIDProxyInitializer; +import at.gv.egovernment.moa.id.util.HTTPRequestJSPForwarder; +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 { + + /** + * 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(); + try { + MOAIDProxyInitializer.initialize(); + + String message = msg.getMessage("config.00", new Object[] + { DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.GERMAN).format(new Date())} ); + Logger.info(message); + + HTTPRequestJSPForwarder.forwardNamed(message, "/message-proxy.jsp", getServletContext(), request, response); + } catch (Throwable t) { + String errorMessage = msg.getMessage("config.04", null); + Logger.error(errorMessage, t); + HTTPRequestJSPForwarder.forwardNamed(errorMessage, "/message-proxy.jsp", getServletContext(), request, response); + } + } + + /** + * 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); + } + +/** + * Calls the web application initializer. + * + * @see javax.servlet.Servlet#init(ServletConfig) + */ +public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); +} + +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ProxyException.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ProxyException.java new file mode 100644 index 000000000..0080c010e --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ProxyException.java @@ -0,0 +1,35 @@ +package at.gv.egovernment.moa.id.proxy.servlet; + +import at.gv.egovernment.moa.id.MOAIDException; + +/** + * Exception thrown while proxying a request to the online application + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class ProxyException extends MOAIDException { + + /** + * Constructor for ProxyException. + * @param messageId + * @param parameters + */ + public ProxyException(String messageId, Object[] parameters) { + super(messageId, parameters); + } + + /** + * Constructor for ProxyException. + * @param messageId + * @param parameters + * @param wrapped + */ + public ProxyException( + String messageId, + Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ProxyServlet.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ProxyServlet.java new file mode 100644 index 000000000..6d215c35e --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/proxy/servlet/ProxyServlet.java @@ -0,0 +1,879 @@ +package at.gv.egovernment.moa.id.proxy.servlet; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Vector; + +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import at.gv.egovernment.moa.id.AuthenticationException; +import at.gv.egovernment.moa.id.BuildException; +import at.gv.egovernment.moa.id.MOAIDException; +import at.gv.egovernment.moa.id.ParseException; +import at.gv.egovernment.moa.id.ServiceException; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.ConnectionParameter; +import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider; +import at.gv.egovernment.moa.id.config.proxy.OAConfiguration; +import at.gv.egovernment.moa.id.config.proxy.OAProxyParameter; +import at.gv.egovernment.moa.id.data.AuthenticationData; +import at.gv.egovernment.moa.id.data.CookieManager; +import at.gv.egovernment.moa.id.proxy.ConnectionBuilder; +import at.gv.egovernment.moa.id.proxy.ConnectionBuilderFactory; +import at.gv.egovernment.moa.id.proxy.LoginParameterResolver; +import at.gv.egovernment.moa.id.proxy.LoginParameterResolverException; +import at.gv.egovernment.moa.id.proxy.LoginParameterResolverFactory; +import at.gv.egovernment.moa.id.proxy.MOAIDProxyInitializer; +import at.gv.egovernment.moa.id.proxy.NotAllowedException; +import at.gv.egovernment.moa.id.proxy.invoke.GetAuthenticationDataInvoker; +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.util.Base64Utils; + +/** + * Servlet requested for logging in at an online application, + * and then for proxying requests to the online application. + * @author Paul Ivancsics + * @version $Id$ + */ +public class ProxyServlet extends HttpServlet { + /** Name of the Parameter for the Target */ + private static final String PARAM_TARGET = "Target"; + /** Name of the Parameter for the SAMLArtifact */ + private static final String PARAM_SAMLARTIFACT = "SAMLArtifact"; + + /** Name of the Attribute for marking the session as authenticated*/ + private static final String ATT_AUTHDATAFETCHED = "AuthDataFetched"; + /** Name of the Attribute for the PublicURLPrefix */ + private static final String ATT_PUBLIC_URLPREFIX = "PublicURLPrefix"; + /** Name of the Attribute for the RealURLPrefix */ + private static final String ATT_REAL_URLPREFIX = "RealURLPrefix"; + /** Name of the Attribute for the SSLSocketFactory */ + private static final String ATT_SSL_SOCKET_FACTORY = "SSLSocketFactory"; + /** Name of the Attribute for the LoginHeaders */ + private static final String ATT_LOGIN_HEADERS = "LoginHeaders"; + /** Name of the Attribute for the LoginParameters */ + private static final String ATT_LOGIN_PARAMETERS = "LoginParameters"; + /** Name of the Attribute for the SAMLARTIFACT */ + private static final String ATT_SAML_ARTIFACT = "SamlArtifact"; + /** Name of the Attribute for the state of the browser request for login dialog*/ + private static final String ATT_BROWSERREQU = "BrowserLoginRequest"; + /** Name of the Attribute for the state of the browser request for login dialog*/ + private static final String ATT_OA_CONF = "oaConf"; + /** Name of the Attribute for the Logintype of the OnlineApplication*/ + private static final String ATT_OA_LOGINTYPE = "LoginType"; + /** Name of the Attribute for the number of the try to login into the OnlineApplication*/ + private static final String ATT_OA_LOGINTRY = "LoginTry"; + /** Maximum permitted login tries */ + private static final int MAX_OA_LOGINTRY = 3; + /** Name of the Attribute for authorization value for further connections*/ + private static final String ATT_OA_AUTHORIZATION_HEADER = "authorizationkey"; + /** Name of the Attribute for user binding */ + private static final String ATT_OA_USER_BINDING = "UserBinding"; + /** For extended internal debug messages */ + private static final boolean INTERNAL_DEBUG = false; + /** Message to be given if browser login failed */ + private static final String RET_401_MSG = "<html><head><title>Ein Fehler ist aufgetreten</title></head><body><h1>Fehler bei der Anmeldung</h1><p>Bei der Anmeldung ist ein Fehler aufgetreten.</p><p>Fehler bei der Anmeldung. <br>Prüfen Sie bitte ihre Berechtigung.<br><b>Abbruch durch den Benutzer.</b><br></p></body></html>"; + + /** + * @see javax.servlet.http.HttpServlet#service(HttpServletRequest, HttpServletResponse) + */ + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + Logger.debug("getRequestURL:" + req.getRequestURL().toString()); + try { + if (req.getParameter(PARAM_SAMLARTIFACT) != null) { + // check if SAML Artifact was already used in this session (in case of page reload) + HttpSession session = req.getSession(); + if (null != session && req.getParameter(PARAM_SAMLARTIFACT).equals(session.getAttribute(ATT_SAML_ARTIFACT))) { + if (session.getAttribute(ATT_BROWSERREQU)==null) { + tunnelRequest(req, resp); + }else{ + login(req, resp); //login after browser login dialog + } + } else + // it is the first time that the SAML Artifact was used + login(req, resp); + } + else + tunnelRequest(req, resp); + } + catch (MOAIDException ex) { + handleError(ex.getMessage(), ex, req, resp); + } + catch (Throwable ex) { + handleError(ex.getMessage(), ex, req, resp); + } + } + + /** + * Login to online application at first call of servlet for a user session.<br/> + * <ul> + * <li>Acquires authentication data from the MOA-ID Auth component.</li> + * <li>Reads configuration data for the online application.</li> + * <li>Resolves login parameters.</li> + * <li>Sets up an SSLSocketFactory in case of a secure connection to the online application.</li> + * <li>For a stateless online application, stores data in the HttpSession.</li> + * <li>Tunnels the request to the online application.</li> + * </ul> + * @param req + * @param resp + * @throws ConfigurationException when wrong configuration is encountered + * @throws ProxyException when wrong configuration is encountered + * @throws BuildException while building the request for MOA-ID Auth + * @throws ServiceException while invoking MOA-ID Auth + * @throws ParseException while parsing the response from MOA-ID Auth + */ + private void login(HttpServletRequest req, HttpServletResponse resp) throws ConfigurationException, ProxyException, BuildException, ServiceException, ParseException, AuthenticationException { + + HttpSession session = req.getSession(); + String samlArtifact = ""; + Map loginHeaders = null; + Map loginParameters = null; + String publicURLPrefix = ""; + String realURLPrefix = ""; + SSLSocketFactory ssf = null; + String urlRequested = req.getRequestURL().toString(); + OAConfiguration oaConf = null; + String loginType = ""; + String binding = ""; + + if (session.getAttribute(ATT_BROWSERREQU)==null) { + + samlArtifact = req.getParameter(PARAM_SAMLARTIFACT); + Logger.debug("moa-id-proxy login " + PARAM_SAMLARTIFACT + ": " + samlArtifact); + // String target = req.getParameter(PARAM_TARGET); parameter given but not processed + // boolean targetprovided = req.getParameter(PARAM_TARGET) != null; + + // get authentication data from the MOA-ID Auth component + AuthenticationData authData; + try { + authData = new GetAuthenticationDataInvoker().getAuthenticationData(samlArtifact); + } catch (ServiceException ex) { + throw new ProxyException("proxy.14", new Object[] {ex.getMessage()}, ex); + } catch (ProxyException ex) { + throw new ProxyException("proxy.14", new Object[] {ex.getMessage()}, ex); + } + session.setAttribute(ATT_AUTHDATAFETCHED, "true"); + + // read configuration data + ProxyConfigurationProvider proxyConf = ProxyConfigurationProvider.getInstance(); + OAProxyParameter oaParam = proxyConf.getOnlineApplicationParameter(urlRequested); + if (oaParam == null) { + throw new ProxyException("proxy.02", new Object[] { urlRequested }); + } + publicURLPrefix = oaParam.getPublicURLPrefix(); + Logger.debug("OA: " + publicURLPrefix); + oaConf = oaParam.getOaConfiguration(); + ConnectionParameter oaConnParam = oaParam.getConnectionParameter(); + realURLPrefix = oaConnParam.getUrl(); + + // resolve login parameters to be forwarded to online application + LoginParameterResolver lpr = LoginParameterResolverFactory.getLoginParameterResolver(publicURLPrefix); + String clientIPAddress = req.getRemoteAddr(); + boolean businessService = oaParam.getBusinessService(); + try { + if (oaConf.getAuthType().equals(OAConfiguration.PARAM_AUTH)) { + loginParameters = lpr.getAuthenticationParameters(oaConf, authData, clientIPAddress, businessService, publicURLPrefix); + } else { + loginHeaders = lpr.getAuthenticationHeaders(oaConf, authData, clientIPAddress, businessService, publicURLPrefix); + for (Iterator iter = loginHeaders.keySet().iterator(); iter.hasNext();) { + //extract user-defined bindingValue + String headerKey = (String) iter.next(); + String headerKeyValue = (String) loginHeaders.get(headerKey); + if (headerKey.equalsIgnoreCase("binding")) { + binding = (String) loginHeaders.get(headerKey); + } + for (int i = 1; i <= 3; i++) { + if (headerKey.equalsIgnoreCase("param" + i)) { + int sep = headerKeyValue.indexOf("="); + if (sep>-1) { + if (sep>0) { + String value = ""; + if (headerKeyValue.length()>sep+1) value = headerKeyValue.substring(sep+1); + if (loginParameters == null) loginParameters = new HashMap(); + loginParameters.put(headerKeyValue.substring(0,sep) , value); + } + } else { + loginParameters.put(headerKey, ""); + } + } + } + } + loginHeaders.remove("binding"); + loginHeaders.remove("param1"); + loginHeaders.remove("param2"); + loginHeaders.remove("param3"); + } + } catch (LoginParameterResolverException ex) { + throw new ProxyException("proxy.13", new Object[] { publicURLPrefix }); + } catch (NotAllowedException e) { + throw new ProxyException("proxy.15", new Object[] { }); + } + + // setup SSLSocketFactory for communication with the online application + if (oaConnParam.isHTTPSURL()) { + try { + ssf = SSLUtils.getSSLSocketFactory(proxyConf, oaConnParam); + } catch (Throwable ex) { + throw new ProxyException( + "proxy.05", + new Object[] { oaConnParam.getUrl(), ex.toString()}, + ex); + } + } + + // for stateless online application, store data in HttpSession + loginType = oaConf.getLoginType(); + if ("".equalsIgnoreCase(binding)) { + binding = oaConf.getBinding(); + if ("".equalsIgnoreCase(binding)) binding = "full"; + } + Logger.debug("Login type: " + loginType); + if (loginType.equals(OAConfiguration.LOGINTYPE_STATELESS)) { + int sessionTimeOut = oaParam.getSessionTimeOut(); + if (sessionTimeOut == 0) + sessionTimeOut = 60 * 60; // default 1 h + + session.setMaxInactiveInterval(sessionTimeOut); + session.setAttribute(ATT_PUBLIC_URLPREFIX, publicURLPrefix); + session.setAttribute(ATT_REAL_URLPREFIX, realURLPrefix); + session.setAttribute(ATT_SSL_SOCKET_FACTORY, ssf); + session.setAttribute(ATT_LOGIN_HEADERS, loginHeaders); + session.setAttribute(ATT_LOGIN_PARAMETERS, loginParameters); + session.setAttribute(ATT_SAML_ARTIFACT, samlArtifact); + session.setAttribute(ATT_OA_CONF, oaConf); + session.setAttribute(ATT_OA_LOGINTYPE, loginType); + session.setAttribute(ATT_OA_USER_BINDING, binding); + session.removeAttribute(ATT_BROWSERREQU); + session.removeAttribute(ATT_OA_AUTHORIZATION_HEADER); + session.removeAttribute(ATT_OA_LOGINTRY); + Logger.debug("moa-id-proxy: HTTPSession " + session.getId() + " angelegt"); + } + + } else { + loginHeaders = (Map) session.getAttribute(ATT_LOGIN_HEADERS); + publicURLPrefix = (String) session.getAttribute(ATT_PUBLIC_URLPREFIX); + realURLPrefix = (String) session.getAttribute(ATT_REAL_URLPREFIX); + ssf = (SSLSocketFactory) session.getAttribute(ATT_SSL_SOCKET_FACTORY); + loginHeaders = (Map) session.getAttribute(ATT_LOGIN_HEADERS); + loginParameters = (Map) session.getAttribute(ATT_LOGIN_PARAMETERS); + samlArtifact = (String) session.getAttribute(ATT_SAML_ARTIFACT); + oaConf = (OAConfiguration) session.getAttribute(ATT_OA_CONF); + loginType = (String) session.getAttribute(ATT_OA_LOGINTYPE); + binding = (String) session.getAttribute(ATT_OA_USER_BINDING); + session.removeAttribute(ATT_BROWSERREQU); + Logger.debug("moa-id-proxy: HTTPSession " + session.getId() + " aufgenommen"); + } + + try { + int respcode = 0; + + // tunnel request to the online application + respcode = tunnelRequest(req, resp, loginHeaders, loginParameters, publicURLPrefix, realURLPrefix, ssf, binding); + if (respcode == 401) { + if (OAConfiguration.BINDUNG_FULL.equals(binding) && oaConf.getLoginType().equals(OAConfiguration.LOGINTYPE_STATELESS)) { + throw new ProxyException("proxy.12", new Object[] { realURLPrefix }); + } + } + } catch (ProxyException ex) { + throw new ProxyException("proxy.12", new Object[] { realURLPrefix }); + } catch (Throwable ex) { + throw new ProxyException("proxy.04", new Object[] { urlRequested, ex.toString()}, ex); + } + } + + /** + * Tunnels a request to the stateless online application using data stored in the HTTP session. + * @param req HTTP request + * @param resp HTTP response + * @throws IOException if an I/O error occurs + */ + private void tunnelRequest(HttpServletRequest req, HttpServletResponse resp) throws ProxyException, IOException { + + //Logger.debug("Tunnel request (stateless)"); + HttpSession session = req.getSession(false); + + if (session == null) + throw new ProxyException("proxy.07", null); + String publicURLPrefix = (String) session.getAttribute(ATT_PUBLIC_URLPREFIX); + //A session is automatically created when forwarded 1st time to errorpage-proxy.jsp (with the handleError method) + //additional check if publicURLPrefix is OK, if not throw an Exception + if (publicURLPrefix == null) + throw new ProxyException("proxy.07", null); + + String realURLPrefix = (String) session.getAttribute(ATT_REAL_URLPREFIX); + SSLSocketFactory ssf = (SSLSocketFactory) session.getAttribute(ATT_SSL_SOCKET_FACTORY); + Map loginHeaders = (Map) session.getAttribute(ATT_LOGIN_HEADERS); + Map loginParameters = (Map) session.getAttribute(ATT_LOGIN_PARAMETERS); + String binding = (String) session.getAttribute(ATT_OA_USER_BINDING); + if (publicURLPrefix == null || realURLPrefix == null) + throw new ProxyException("proxy.08", new Object[] { req.getRequestURL().toString()}); + + int respcode = tunnelRequest(req, resp, loginHeaders, loginParameters, publicURLPrefix, realURLPrefix, ssf, binding); + if (respcode == -401) // #tries to login exceeded + throw new ProxyException("proxy.16", new Object[] {realURLPrefix, Integer.toString(MAX_OA_LOGINTRY)}); + } + +/** + * Tunnels a request to the online application using given URL mapping and SSLSocketFactory. + * This method returns the ResponseCode of the request to the online application. + * @param req HTTP request + * @param resp HTTP response + * @param loginHeaders header field/values to be inserted for purposes of authentication; + * may be <code>null</code> + * @param loginParameters parameter name/values to be inserted for purposes of authentication; + * may be <code>null</code> + * @param publicURLPrefix prefix of request URL to be substituted for the <code>realURLPrefix</code> + * @param realURLPrefix prefix of online application URL to substitute the <code>publicURLPrefix</code> + * @param ssf SSLSocketFactory to use + * @throws IOException if an I/O error occurs + */ +private int tunnelRequest(HttpServletRequest req, HttpServletResponse resp, Map loginHeaders, Map loginParameters, String publicURLPrefix, String realURLPrefix, SSLSocketFactory ssf, String binding) + throws IOException { + + String originBinding = binding; + String browserUserID = ""; + String browserPassword = ""; + //URL url = new URL(realURLPrefix); + //String realURLHost = url.getHost(); + if (INTERNAL_DEBUG && !binding.equals("")) Logger.debug("Binding: " + binding); + + // collect headers from request + Map headers = new HashMap(); + for (Enumeration enu = req.getHeaderNames(); enu.hasMoreElements();) { + String headerKey = (String) enu.nextElement(); + String headerKeyValue = req.getHeader(headerKey); + if (INTERNAL_DEBUG) Logger.debug("Incoming:" + headerKey + "=" + headerKeyValue); + //Analyze Basic-Auth-Headers from the client + if (headerKey.equalsIgnoreCase("Authorization")) { + if (headerKeyValue.substring(0,6).equalsIgnoreCase("Basic ")) { + String credentials = headerKeyValue.substring(6); + byte [] bplaintextcredentials = Base64Utils. decode(credentials, true); + String plaintextcredentials = new String(bplaintextcredentials); + browserUserID = plaintextcredentials.substring(0,plaintextcredentials.indexOf(":")); + browserPassword = plaintextcredentials.substring(plaintextcredentials.indexOf(":")+1); + //deactivate following line for security + //if (INTERNAL_DEBUG) Logger.debug("Analyzing authorization-header from browser: " + headerKeyValue + "gives UN:PW=" + browserUserID + ":" + browserPassword ); + } + if (headerKeyValue.substring(0,9).equalsIgnoreCase("Negotiate")) { + //deactivate following line for security + //if (INTERNAL_DEBUG) Logger.debug("Analyzing authorization-header from browser: Found NTLM Aut.: " + headerKeyValue + "gives UN:PW=" + browserUserID + ":" + browserPassword ); + } + } + else + { + /* Headers MUST NOT be repaced according to our Spec. + if (headerKey.equalsIgnoreCase("Host")) { + headerKeyValue = realURLHost; + //headerKeyValue= realURLPrefix.substring(hoststartpos); + if (INTERNAL_DEBUG) Logger.debug("replaced:" + headerKey + "=" + headerKeyValue); + } + */ + headers.put(headerKey, headerKeyValue); + } + } + + + // collect login headers, possibly overwriting headers from request + String authorizationvalue=""; + if (req.getSession().getAttribute(ATT_OA_AUTHORIZATION_HEADER)==null) { + + if (OAConfiguration.BINDUNG_NOMATCH.equals(binding)) { + int loginTry = getLoginTry(req); + Logger.debug("Binding: mode = " + OAConfiguration.BINDUNG_NOMATCH + "(try #" + Integer.toString(loginTry) + ")"); + if (loginTry==1) { + binding = OAConfiguration.BINDUNG_FULL; + } else { + binding = OAConfiguration.BINDUNG_USERNAME; + } + } + + /* Soll auch bei anderen bindings zuerst ein passwort probiert werden können: + //if we have the first Login-Try and we have Binding to Username and a predefined Password we try this one first + // full binding will be covered by next block + if (loginTry==1 && !OAConfiguration.BINDUNG_FULL.equals(binding)) { + //1st try: if we have a password, try this one first + for (Iterator iter = loginHeaders.keySet().iterator(); iter.hasNext();) { + String headerKey = (String) iter.next(); + String headerKeyValue = (String) loginHeaders.get(headerKey); + if (isBasicAuthenticationHeader(headerKey, headerKeyValue)) { + String credentials = headerKeyValue.substring(6); + byte [] bplaintextcredentials = Base64Utils.decode(credentials, true); + String plaintextcredentials = new String(bplaintextcredentials); + String password = plaintextcredentials.substring(plaintextcredentials.indexOf(":")+1); + if (password!=null && !password.equals("")) { + Logger.debug("Binding: found predefined password. Trying full binding first"); + binding = OAConfiguration.BINDUNG_FULL; + break; + } + } + } + } + */ + + + + //we have a connection with not having logged on + if (loginHeaders != null && (browserPassword.length()!=0 || browserUserID.length()!=0 || OAConfiguration.BINDUNG_FULL.equals(binding))) { + for (Iterator iter = loginHeaders.keySet().iterator(); iter.hasNext();) { + String headerKey = (String) iter.next(); + String headerKeyValue = (String) loginHeaders.get(headerKey); + //customize loginheaders if necessary + if (isBasicAuthenticationHeader(headerKey, headerKeyValue)) + { + if (OAConfiguration.BINDUNG_FULL.equals(binding)) { + authorizationvalue = headerKeyValue; + Logger.debug("Binding: full binding to user established"); + } else { + String credentials = headerKeyValue.substring(6); + byte [] bplaintextcredentials = Base64Utils.decode(credentials, true); + String plaintextcredentials = new String(bplaintextcredentials); + String userID = plaintextcredentials.substring(0,plaintextcredentials.indexOf(":")); + String password = plaintextcredentials.substring(plaintextcredentials.indexOf(":")+1); + String userIDPassword = ":"; + if (OAConfiguration.BINDUNG_USERNAME.equals(binding)) { + Logger.debug("Binding: Access with necessary binding to user"); + userIDPassword = userID + ":" + browserPassword; + } else if (OAConfiguration.BINDUNG_NONE.equals(binding)) { + Logger.debug("Binding: Access without binding to user"); + //If first time + if (browserUserID.length()==0) browserUserID = userID; + if (browserPassword.length()==0) browserPassword = password; + userIDPassword = browserUserID + ":" + browserPassword; + } else { + userIDPassword = userID + ":" + password; + } + credentials = Base64Utils.encode(userIDPassword.getBytes()); + authorizationvalue = "Basic " + credentials; + headerKeyValue = authorizationvalue; + } + } + headers.put(headerKey, headerKeyValue); + } + } + }else{ + //if OA needs Authorization header in each further request + authorizationvalue = (String) req.getSession().getAttribute(ATT_OA_AUTHORIZATION_HEADER); + if (loginHeaders != null) headers.put("Authorization", authorizationvalue); + } + + + Vector parameters = new Vector(); + + for (Enumeration enu = req.getParameterNames(); enu.hasMoreElements();) { + String paramName = (String) enu.nextElement(); + if (!(paramName.equals(PARAM_SAMLARTIFACT) || paramName.equals(PARAM_TARGET))) { + if (INTERNAL_DEBUG) Logger.debug("Req Parameter-put: " + paramName + ":" + req.getParameter(paramName)); + String parameter[] = new String[2]; + parameter[0]= paramName; + parameter[1]= req.getParameter(paramName); + parameters.add(parameter); + } + } + // collect login parameters, possibly overwriting parameters from request + if (loginParameters != null) { + for (Iterator iter = loginParameters.keySet().iterator(); iter.hasNext();) { + String paramName = (String) iter.next(); + if (!(paramName.equals(PARAM_SAMLARTIFACT) || paramName.equals(PARAM_TARGET))) { + if (INTERNAL_DEBUG) Logger.debug("Req Login-Parameter-put: " + paramName + ":" + loginParameters.get(paramName)); + String parameter[] = new String[2]; + parameter[0]= paramName; + parameter[1]= (String) loginParameters.get(paramName); + parameters.add(parameter); + } + } + } + + ConnectionBuilder cb = ConnectionBuilderFactory.getConnectionBuilder(publicURLPrefix); + HttpURLConnection conn = cb.buildConnection(req, publicURLPrefix, realURLPrefix, ssf, parameters); + + // set headers as request properties of URLConnection + for (Iterator iter = headers.keySet().iterator(); iter.hasNext();) { + String headerKey = (String) iter.next(); + String headerValue = (String) headers.get(headerKey); + String LogStr = "Req header " + headerKey + ": " + headers.get(headerKey); + if (isBasicAuthenticationHeader(headerKey, headerValue)) { + String credentials = headerValue.substring(6); + byte [] bplaintextcredentials = Base64Utils. decode(credentials, true); + String plaintextcredentials = new String(bplaintextcredentials); + String uid = plaintextcredentials.substring(0,plaintextcredentials.indexOf(":")); + String pwd = plaintextcredentials.substring(plaintextcredentials.indexOf(":")+1); + //Sollte AuthorizationInfo vom HTTPClient benutzt werden: cb.addBasicAuthorization(publicURLPrefix, uid, pwd); + //deactivate following line for security + //if (INTERNAL_DEBUG && Logger.isDebugEnabled()) LogStr = LogStr + " >UserID:Password< >" + uid + ":" + pwd + "<"; + } + conn.setRequestProperty(headerKey, headerValue); + if (INTERNAL_DEBUG) Logger.debug(LogStr); + } + + StringWriter sb = new StringWriter(); + + // Write out parameters into output stream of URLConnection. + // On GET request, do not send parameters in any case, + // otherwise HttpURLConnection would send a POST. + if (!"get".equalsIgnoreCase(req.getMethod()) && !parameters.isEmpty()) { + boolean firstParam = true; + String parameter[] = new String[2]; + for (Iterator iter = parameters.iterator(); iter.hasNext();) { + parameter = (String[]) iter.next(); + String paramName = parameter[0]; + String paramValue = parameter[1]; + if (firstParam) + firstParam = false; + else + sb.write("&"); + sb.write(paramName); + sb.write("="); + sb.write(paramValue); + if (INTERNAL_DEBUG) Logger.debug("Req param " + paramName + ": " + paramValue); + } + } + + // For WebDAV and POST: copy content + if (!"get".equalsIgnoreCase(req.getMethod())) { + if (INTERNAL_DEBUG && !"post".equalsIgnoreCase(req.getMethod())) Logger.debug("---- WEBDAV ---- copying content"); + try { + OutputStream out = conn.getOutputStream(); + InputStream in = req.getInputStream(); + if (!parameters.isEmpty()) out.write(sb.toString().getBytes()); //Parameter nicht mehr mittels Printwriter schreiben + copyStream(in, out, null, req.getMethod()); + out.flush(); + out.close(); + } catch (IOException e) { + if (!"post".equalsIgnoreCase(req.getMethod())) + Logger.debug("---- WEBDAV ---- streamcopy problem"); + else + Logger.debug("---- POST ---- streamcopy problem"); + } + } + + // connect + if (INTERNAL_DEBUG) Logger.debug("Connect Request"); + conn.connect(); + if (INTERNAL_DEBUG) Logger.debug("Connect Response"); + + // check login tries + if (conn.getResponseCode()==HttpURLConnection.HTTP_UNAUTHORIZED) { + int loginTry = getLoginTry(req); + req.getSession().setAttribute(ATT_OA_LOGINTRY, Integer.toString(loginTry)); + if (loginTry > MAX_OA_LOGINTRY) { + Logger.debug("Found 401 UNAUTHORIZED, maximum tries exceeded; leaving..."); + cb.disconnect(conn); + return -401; + } + } + + + + if (conn.getResponseCode()==HttpURLConnection.HTTP_UNAUTHORIZED && OAConfiguration.BINDUNG_FULL.equals(originBinding)) { + Logger.debug("Found 401 UNAUTHORIZED, leaving..."); + cb.disconnect(conn); + return conn.getResponseCode(); + } + + + resp.setStatus(conn.getResponseCode()); + resp.setContentType(conn.getContentType()); + + if (loginHeaders != null && (conn.getResponseCode()==HttpURLConnection.HTTP_OK || conn.getResponseCode()==HttpURLConnection.HTTP_MOVED_TEMP) && req.getSession().getAttribute(ATT_OA_AUTHORIZATION_HEADER)==null) { + req.getSession().setAttribute(ATT_OA_AUTHORIZATION_HEADER, authorizationvalue); + Logger.debug("Login OK. Saving authorization header to remember in further requests"); + } + + // Read response headers + // Omit response header "content-length" if response header "Transfer-encoding: chunked" is set. + // Otherwise, the connection will not be kept alive, resulting in subsequent missing requests. + // See JavaDoc of javax.servlet.http.HttpServlet: + // When using HTTP 1.1 chunked encoding (which means that the response has a Transfer-Encoding header), do not set the Content-Length header. + Vector respHeaders = new Vector(); + + boolean chunked = false; + String contentLengthKey = null; + String transferEncodingKey = null; + int i = 1; + String headerKey; + String loginType = (String) req.getSession().getAttribute(ATT_OA_LOGINTYPE); + while ((headerKey = conn.getHeaderFieldKey(i)) != null) { + String headerValue = conn.getHeaderField(i); + // Überschrift im Browser-Passworteingabedialog setzen (sonst ist der reale host eingetragen) + if (headerKey.equalsIgnoreCase("WWW-Authenticate") && headerValue.startsWith("Basic realm=\"")) { + headerValue = "Basic realm=\"" + publicURLPrefix + "\""; + if (OAConfiguration.BINDUNG_USERNAME.equals(originBinding) || OAConfiguration.BINDUNG_NOMATCH.equals(originBinding)) { + headerValue = "Basic realm=\"Bitte Passwort eingeben\""; + } else if (OAConfiguration.BINDUNG_NONE.equals(originBinding)) { + headerValue = "Basic realm=\"Bitte Benutzername und Passwort eingeben\""; + } + } + String respHeader[] = new String[2]; + if ((conn.getResponseCode()==HttpURLConnection.HTTP_UNAUTHORIZED) && headerKey.equalsIgnoreCase("content-length")) { + //alter the unauthorized message with template for login + //TODO: supply a special login form on unauthorized messages with bindings!=full + headerValue = Integer.toString(RET_401_MSG.length()); + } + respHeader[0]= headerKey; + respHeader[1]= headerValue; + + if (!(OAConfiguration.BINDUNG_FULL.equals(originBinding) && OAConfiguration.LOGINTYPE_STATELESS.equals(loginType) && headerKey.equalsIgnoreCase("WWW-Authenticate") && headerValue.startsWith("Basic realm=\""))) { + respHeaders.add(respHeader); + if (INTERNAL_DEBUG) Logger.debug("Resp header " + headerKey + ": " + headerValue); + } else { + Logger.debug("Resp header ---REMOVED--- " + headerKey + ": " + headerValue); + } + if (isTransferEncodingChunkedHeader(headerKey, headerValue)) { + chunked = true; + transferEncodingKey = headerKey; + } + if ("content-length".equalsIgnoreCase(headerKey)) + contentLengthKey = headerKey; + i++; + } + if (chunked && contentLengthKey != null) { + respHeaders.remove(transferEncodingKey); + Logger.debug("Resp header " + transferEncodingKey + " REMOVED"); + } + + String headerValue; + String respHeader[] = new String[2]; + + //write out all Responseheaders + for (Iterator iter = respHeaders.iterator(); iter.hasNext();) { + respHeader = (String[]) iter.next(); + headerKey = respHeader[0]; + headerValue = respHeader[1]; + resp.addHeader(headerKey, headerValue); + } + + //Logger.debug(">>>> Copy Content"); + //Logger.debug(" from ()" + conn.getURL()); + //Logger.debug(" to (" + req.getRemoteAddr() + ":"+ ") " +req.getRequestURL()); + + // read response stream + Logger.debug("Resp from " + conn.getURL().toString() + ": status " + conn.getResponseCode()); + // Load content unless the server lets us know that the content is NOT MODIFIED... + if (conn.getResponseCode()!=HttpURLConnection.HTTP_NOT_MODIFIED ) { + BufferedInputStream respIn = new BufferedInputStream(conn.getInputStream()); + //Logger.debug("Got Inputstream"); + BufferedOutputStream respOut = new BufferedOutputStream(resp.getOutputStream()); + //Logger.debug("Got Outputstream"); + + byte [] buffer = new byte[4096]; + if (respOut != null) { + int bytesRead; + while ((bytesRead = respIn.read(buffer)) >= 0) { + if (conn.getResponseCode()!=HttpURLConnection.HTTP_UNAUTHORIZED) respOut.write(buffer, 0, bytesRead); + } + } else { + while (respIn.read(buffer) >= 0); + } + + + /* + int ch; + StringBuffer strBuf = new StringBuffer(""); + while ((ch = respIn.read()) >= 0) { + if (conn.getResponseCode()!=HttpURLConnection.HTTP_UNAUTHORIZED) respOut.write(ch); + strBuf.append((char)ch); + } + Logger.debug("Resp Content:"); + if (strBuf.toString().length()>500) + Logger.debug(strBuf.toString().substring(0,500)); + else + Logger.debug(strBuf.toString()); + */ + + + if (conn.getResponseCode()==HttpURLConnection.HTTP_UNAUTHORIZED) { + respOut.write(RET_401_MSG.getBytes()); + } + respOut.flush(); + respOut.close(); + respIn.close(); + if (conn.getResponseCode()==HttpURLConnection.HTTP_UNAUTHORIZED) { + Logger.debug("Found 401 UNAUTHORIZED..."); + cb.disconnect(conn); + return conn.getResponseCode(); + } + } else { + //if (conn.getResponseCode()==HttpURLConnection.HTTP_NOT_MODIFIED) + Logger.debug("Found 304 NOT MODIFIED..."); + } + + cb.disconnect(conn); + Logger.debug("Request done"); + + return conn.getResponseCode(); +} + +/** + * Gets the current amount of the login try at the online application + * + * @param req the HttpServletRequest + * @return the number off the current login try + */ +private int getLoginTry(HttpServletRequest req) { + String oa_loginTry = (String) req.getSession().getAttribute(ATT_OA_LOGINTRY); + int loginTry = 1; + if (oa_loginTry!=null) loginTry = Integer.parseInt(oa_loginTry)+1; + return loginTry; +} +/** + * Determines whether a HTTP header is a basic authentication header of the kind "Authorization: Basic ..." + * + * @param headerKey header name + * @param headerValue header value + * @return true for a basic authentication header + */ +private boolean isBasicAuthenticationHeader(String headerKey, String headerValue) { + if (!"authorization".equalsIgnoreCase(headerKey)) + return false; + if (headerValue.length() < "basic".length()) + return false; + String authenticationSchema = headerValue.substring(0, "basic".length()); + return "basic".equalsIgnoreCase(authenticationSchema); +} +/** + * Determines whether a basic authentication header of the kind "Authorization: Basic ..." + * is included in a HTTP request + * @param req HTTP request + * @return true for a basic authentication header provided + */ +private boolean isBasicAuthenticationHeaderProvided(HttpServletRequest req) { + for (Enumeration enu = req.getHeaderNames(); enu.hasMoreElements();) { + String headerKey = (String) enu.nextElement(); + String headerValue = req.getHeader(headerKey); + if (isBasicAuthenticationHeader(headerKey, headerValue)) + return true; + } + return false; +} +/** + * Determines whether a HTTP header is "Transfer-encoding" header with value containing "chunked" + * + * @param headerKey header name + * @param headerValue header value + * @return true for a "Transfer-encoding: chunked" header + */ +private boolean isTransferEncodingChunkedHeader(String headerKey, String headerValue) { + if (!"transfer-encoding".equalsIgnoreCase(headerKey)) + return false; + return headerValue.indexOf("chunked") >= 0 || headerValue.indexOf("Chunked") >= 0 || headerValue.indexOf("CHUNKED") >= 0; +} + +/** + * Calls the web application initializer. + * + * @see javax.servlet.Servlet#init(ServletConfig) + */ +public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + try { + MOAIDProxyInitializer.initialize(); + Logger.info(MOAIDMessageProvider.getInstance().getMessage("proxy.00", null)); + } + catch (Exception ex) { + Logger.fatal(MOAIDMessageProvider.getInstance().getMessage("proxy.06", null), ex); + throw new ServletException(ex); + } +} + +/** + * 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-proxy.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(null != errorMessage) { + Logger.error(errorMessage); + req.setAttribute("ErrorMessage", errorMessage ); + } + + if (null != exceptionThrown) { + if(null == errorMessage) errorMessage = exceptionThrown.getMessage(); + Logger.error(errorMessage, exceptionThrown); + //req.setAttribute("ExceptionThrown", exceptionThrown); + } + + //forward this to errorpage-proxy.jsp where the HTML error page is generated + ServletContext context = getServletContext(); + RequestDispatcher dispatcher = context.getRequestDispatcher("/errorpage-proxy.jsp"); + try { + dispatcher.forward(req, resp); + } catch (ServletException e) { + Logger.error(e); + } catch (IOException e) { + Logger.error(e); + } + +} + + +// * taken from iaik.utils.util.copyStream: +/** + * Reads all data (until EOF is reached) from the given source to the + * destination stream. If the destination stream is null, all data is dropped. + * It uses the given buffer to read data and forward it. If the buffer is + * null, this method allocates a buffer. + * + * @param source The stream providing the data. + * @param destination The stream that takes the data. If this is null, all + * data from source will be read and discarded. + * @param buffer The buffer to use for forwarding. If it is null, the method + * allocates a buffer. + * @exception IOException If reading from the source or writing to the + * destination fails. + */ +private static void copyStream(InputStream source, OutputStream destination, byte[] buffer, String method) throws IOException { + if (source == null) { + throw new NullPointerException("Argument \"source\" must not be null."); + } + if (buffer == null) { + buffer = new byte[4096]; + } + + if (destination != null) { + int bytesRead; + while ((bytesRead = source.read(buffer)) >= 0) { + destination.write(buffer, 0, bytesRead); + //if (method.equalsIgnoreCase("POST")) Logger.debug(buffer.toString()); + } + } else { + while (source.read(buffer) >= 0); + } +} + + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/AxisSecureSocketFactory.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/AxisSecureSocketFactory.java new file mode 100644 index 000000000..954a87e62 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/AxisSecureSocketFactory.java @@ -0,0 +1,212 @@ +package at.gv.egovernment.moa.id.util; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; +import java.security.GeneralSecurityException; +import java.util.Hashtable; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import org.apache.axis.components.net.BooleanHolder; +import org.apache.axis.components.net.DefaultSocketFactory; +import org.apache.axis.components.net.SecureSocketFactory; +import org.apache.axis.components.net.TransportClientProperties; +import org.apache.axis.components.net.TransportClientPropertiesFactory; +import org.apache.axis.utils.Messages; +import org.apache.axis.utils.XMLUtils; + +import at.gv.egovernment.moa.logging.Logger; + +/** + * Secure socket factory for Axis webs service clients of the MOA-ID component, + * which are the MOA-SP calls from MOA-ID Auth, + * and the MOA-ID Auth calls from MOA-ID Proxy. + * <br/>Use this initialization code:<br/> + * <code> // ConnectionParameter connParam = ... get from ConfigurationProvider + * AxisSecureSocketFactory.initialize(connParam);</code> + * <br/>See the Apache Axis documentation on how to configure this class + * as the default secure socket factory to be used by Axis. + * <br/> + * This code has been copied from <code>JSSESocketFactory</code>, the + * method <code>initialize()</code> has been added. + * + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class AxisSecureSocketFactory + extends DefaultSocketFactory implements SecureSocketFactory { + + /** Field sslFactory */ + private static SSLSocketFactory sslFactory; + + /** + * Constructor for AxisSecureSocketFactory. + * @param attributes ??? + */ + public AxisSecureSocketFactory(Hashtable attributes) { + super(attributes); + } + /** + * Initializes the factory by setting the connection parameters to be used for + * setting the secure socket factory, and by setting the system property + * <code>axis.socketSecureFactory</code>. + * @param ssf <code>SSLSocketFactory</code> to initialize with + */ + public static void initialize(SSLSocketFactory ssf) + throws IOException, GeneralSecurityException { + + Logger.debug("Initialize AxisSecureSocketFactory"); + sslFactory = ssf; + } + + /** + * creates a secure socket + * + * @param host + * @param port + * @param otherHeaders + * @param useFullURL + * + * @return Socket + * @throws Exception + */ + public Socket create( + String host, + int port, + StringBuffer otherHeaders, + BooleanHolder useFullURL) + throws Exception { + if (port == -1) { + port = 443; + } + + TransportClientProperties tcp = + TransportClientPropertiesFactory.create("https"); + + boolean hostInNonProxyList = + isHostInNonProxyList(host, tcp.getNonProxyHosts()); + + Socket sslSocket = null; + if (tcp.getProxyHost().length() == 0 || hostInNonProxyList) { + // direct SSL connection + sslSocket = sslFactory.createSocket(host, port); + } + else { + + // Default proxy port is 80, even for https + int tunnelPort = + (tcp.getProxyPort().length() != 0) + ? Integer.parseInt(tcp.getProxyPort()) + : 80; + if (tunnelPort < 0) + tunnelPort = 80; + + // Create the regular socket connection to the proxy + Socket tunnel = new Socket(tcp.getProxyHost(), tunnelPort); + + // The tunnel handshake method (condensed and made reflexive) + OutputStream tunnelOutputStream = tunnel.getOutputStream(); + PrintWriter out = + new PrintWriter( + new BufferedWriter(new OutputStreamWriter(tunnelOutputStream))); + + // More secure version... engage later? + // PasswordAuthentication pa = + // Authenticator.requestPasswordAuthentication( + // InetAddress.getByName(tunnelHost), + // tunnelPort, "SOCK", "Proxy","HTTP"); + // if(pa == null){ + // printDebug("No Authenticator set."); + // }else{ + // printDebug("Using Authenticator."); + // tunnelUser = pa.getUserName(); + // tunnelPassword = new String(pa.getPassword()); + // } + out.print( + "CONNECT " + + host + + ":" + + port + + " HTTP/1.0\r\n" + + "User-Agent: AxisClient"); + if (tcp.getProxyUser().length() != 0 + && tcp.getProxyPassword().length() != 0) { + + // add basic authentication header for the proxy + String encodedPassword = + XMLUtils.base64encode( + (tcp.getProxyUser() + ":" + tcp.getProxyPassword()).getBytes()); + + out.print("\nProxy-Authorization: Basic " + encodedPassword); + } + out.print("\nContent-Length: 0"); + out.print("\nPragma: no-cache"); + out.print("\r\n\r\n"); + out.flush(); + InputStream tunnelInputStream = tunnel.getInputStream(); + + if (log.isDebugEnabled()) { + log.debug( + Messages.getMessage( + "isNull00", + "tunnelInputStream", + "" + (tunnelInputStream == null))); + } + String replyStr = ""; + + // Make sure to read all the response from the proxy to prevent SSL negotiation failure + // Response message terminated by two sequential newlines + int newlinesSeen = 0; + boolean headerDone = false; /* Done on first newline */ + + while (newlinesSeen < 2) { + int i = tunnelInputStream.read(); + + if (i < 0) { + throw new IOException("Unexpected EOF from proxy"); + } + if (i == '\n') { + headerDone = true; + ++newlinesSeen; + } + else if (i != '\r') { + newlinesSeen = 0; + if (!headerDone) { + replyStr += String.valueOf((char) i); + } + } + } + if (!replyStr.startsWith("HTTP/1.0 200") + && !replyStr.startsWith("HTTP/1.1 200")) { + throw new IOException( + Messages.getMessage( + "cantTunnel00", + new String[] { tcp.getProxyHost(), "" + tunnelPort, replyStr })); + } + + // End of condensed reflective tunnel handshake method + sslSocket = sslFactory.createSocket(tunnel, host, port, true); + if (log.isDebugEnabled()) { + log.debug( + Messages.getMessage( + "setupTunnel00", + tcp.getProxyHost(), + "" + tunnelPort)); + } + } + + ((SSLSocket) sslSocket).startHandshake(); + if (log.isDebugEnabled()) { + log.debug(Messages.getMessage("createdSSL00")); + } + return sslSocket; + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ECDSAConstants.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ECDSAConstants.java new file mode 100644 index 000000000..c5dad8bc4 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ECDSAConstants.java @@ -0,0 +1,36 @@ +/* + * Created on 20.01.2005 + * + * @author rschamberger + * $ID$ + */ +package at.gv.egovernment.moa.id.util; + + +/** + * Class used to define Constants used in Class ECDSAKeyValueConverter + * + * * @author rschamberger + */ +public class ECDSAConstants { + + /* ECDSA Namespace + */ + static String NAMESPACE_ECDSAKEYVALUE_ = "http://www.w3.org/2001/04/xmldsig-more#"; + + /* Schema instance NS + */ + static String NAMESPACE_XSI_ = "http://www.w3.org/2001/XMLSchema-instance"; + + /* ecdsa prefix value + */ + static String NS_PREFIX_ECDSAKEYVALUE_ = "ecdsa"; + + /* namespace namespace + */ + static String NAMESPACE_NAMESPACES_ = "http://www.w3.org/XML/1998/namespace"; + + /* si prefix value + */ + static String NS_PREFIX_XSI_ = "si"; +}; diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ECDSAKeyValueConverter.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ECDSAKeyValueConverter.java new file mode 100644 index 000000000..6fb78edb7 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ECDSAKeyValueConverter.java @@ -0,0 +1,572 @@ +package at.gv.egovernment.moa.id.util; + +import iaik.security.ecc.ecdsa.ECDSAParameter; +import iaik.security.ecc.ecdsa.ECPublicKey; +import iaik.security.ecc.math.ecgroup.AffineCoordinate; +import iaik.security.ecc.math.ecgroup.Coordinate; +import iaik.security.ecc.math.ecgroup.CoordinateTypes; +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.field.Field; +import iaik.security.ecc.math.field.FieldElement; +import iaik.security.ecc.math.field.PrimeField; +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 org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class ECDSAKeyValueConverter +{ + + + //TODO enhance javadoc + + /** + * converter class which can be used to convert ECDSA keys encoded in XML + * to a PublicKey data structure + * @param keyValueElem ECDSAKeyValue Element + * @return ECPublicKey encoded in PublicKey data structure + * @throws Exception + */ + public static PublicKey element2ECDSAPublicKey(Element keyValueElem) throws Exception + { + String ecdsaNS = ECDSAConstants.NAMESPACE_ECDSAKEYVALUE_; + + // 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")); + String curveNameOID = namedCurve.getAttributeNS(null, "URN").substring(8); + ECCParameterFactory eccParamFactory = ECCParameterFactory.getInstance(); + // eccParameterSpec = eccParamFactory.getParameterByOID(curveNameURN.getPath().substring(4)); + eccParameterSpec = eccParamFactory.getParameterByOID(curveNameOID); + } + 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(ECDSAConstants.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, CoordinateTypes.PROJECTIVE_COORDINATES); + ECGroupFactory ecGroupFactory = ECGroupFactory.getInstance(); + EllipticCurve eCurve = ecGroupFactory.getCurve(eccParameterSpec.getA(), + eccParameterSpec.getB(), eccParameterSpec.getR(), CoordinateTypes.PROJECTIVE_COORDINATES); + Field field = eCurve.getField(); + + // Detect type of public key field elements + String elementType = publicKeyXElem.getAttributeNS(ECDSAConstants.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); + PrimeField pf = (PrimeField) field; + publicKeyPointX = pf.newElement(new BigInteger(publicKeyXStr, 10)); +// Value yValue = FieldFactory.getInstance().getPrimeFieldValue(new BigInteger(publicKeyYStr, 10)); +// publicKeyPointY = field.newElement(yValue); + publicKeyPointY = pf.newElement(new BigInteger(publicKeyYStr, 10)); + } + else + { + publicKeyPointX = field.newElement(octetString2ByteArray(publicKeyXStr)); + publicKeyPointY = field.newElement(octetString2ByteArray(publicKeyYStr)); + } +// ProjectiveCoordinate publicKeyPointCoordinate = new ProjectiveCoordinate(publicKeyPointX, +// publicKeyPointY, field.getONEelement()); + Coordinate publicKeyPointCoordinate = new AffineCoordinate(publicKeyPointX, + publicKeyPointY).toProjective(); + ECPoint publicKeyPoint = eCurve.newPoint(publicKeyPointCoordinate); + ECPublicKey publicKey = new ECPublicKey(ecdsaParams, publicKeyPoint); + + return publicKey; + } + + /* ---------------------------------------------------------------------------------------------------- */ + + /* + public static Element publicKey2ECDSAKeyValueElement(boolean implParams, String curveOID, + ECDSAPublicKey publicKey, Document factoryDoc) + { + String ecdsaNS = ECDSAConstants.NAMESPACE_ECDSAKEYVALUE_; + String ecdsaNSP = ECDSAConstants.NS_PREFIX_ECDSAKEYVALUE_; + String nsNS = ECDSAConstants.NAMESPACE_NAMESPACES_; + String xsiNS = ECDSAConstants.NAMESPACE_XSI_; + String xsiNSP = ECDSAConstants.NS_PREFIX_XSI_; + + ECDSAParameter params = (ECDSAParameter)publicKey.getParameter(); + EllipticCurve curve = params.getG().getCurve(); + Field field = curve.getField(); + int fieldId = curve.getField().getFieldId(); + + Element eCDSAKeyValue = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":ECDSAKeyValue"); + eCDSAKeyValue.setAttributeNS(nsNS, "xmlns:" + ecdsaNSP, ecdsaNS); + eCDSAKeyValue.setAttributeNS(nsNS, "xmlns:" + xsiNSP, xsiNS); + + // Detect field type + int coeffPositions[] = new int[3]; + int fieldType = 0; + String fieldElemTypeString = null; + final int FT_PRIME = 1, FT_TNB = 2, FT_PNB = 3; + if (fieldId == Field.PRIME_FIELD) + { + fieldType = FT_PRIME; + fieldElemTypeString = ecdsaNSP + ":PrimeFieldElemType"; + } + else + { + // Get irreducible polynomal + BinaryField binaryField = (BinaryField)field; + BinaryFieldValue irreducible = binaryField.getIrreducible(); + + // Get coefficients of irreducible polynomal + int order = irreducible.getOrder(); + int coeffCount = 2; + for (int i = 1; i < order - 1; i++) + { + if (irreducible.testBit(i)) + { + coeffPositions[coeffCount - 2] = i; + coeffCount++; + if (coeffCount == 5) break; + } + } + + // Set polynomal type (TNB or + fieldType = (coeffCount == 3) ? FT_TNB : FT_PNB; + fieldElemTypeString = ecdsaNSP + ":CharTwoFieldElemType"; + } + + if (!implParams) + { + Element domainParameters = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":DomainParameters"); + eCDSAKeyValue.appendChild(factoryDoc.createTextNode("\n ")); + eCDSAKeyValue.appendChild(domainParameters); + + if (curveOID != null) + { + // Named curve + Element namedCurve = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":NamedCurve"); + namedCurve.setAttributeNS(null, "URN", "urn:oid:" + curveOID); + domainParameters.appendChild(factoryDoc.createTextNode("\n ")); + domainParameters.appendChild(namedCurve); + domainParameters.appendChild(factoryDoc.createTextNode("\n ")); + } + else + { + // Explicit parameters + Element explicitParams = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":ExplicitParams"); + + // Field parameters + Element fieldParams = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":FieldParams"); + explicitParams.appendChild(factoryDoc.createTextNode("\n ")); + explicitParams.appendChild(fieldParams); + + if (fieldType == FT_PRIME) + { + fieldParams.setAttributeNS(xsiNS, xsiNSP + ":type", ecdsaNSP + ":PrimeFieldParamsType"); + Element p = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":P"); + p.appendChild(factoryDoc.createTextNode(curve.getField().getSize().toString(10))); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + fieldParams.appendChild(p); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + } + else if (fieldType == FT_TNB) + { + fieldParams.setAttributeNS(xsiNS, xsiNSP + ":type", ecdsaNSP + ":TnBFieldParamsType"); + Element m = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":M"); + m.appendChild(factoryDoc.createTextNode(Integer.toString(curve.getField().getOrder()))); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + fieldParams.appendChild(m); + + Element k = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":K"); + k.appendChild(factoryDoc.createTextNode(Integer.toString(coeffPositions[0], 10))); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + fieldParams.appendChild(k); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + } + else + { + fieldParams.setAttributeNS(xsiNS, xsiNSP + ":type", ecdsaNSP + ":PnBFieldParamsType"); + Element m = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":M"); + m.appendChild(factoryDoc.createTextNode(Integer.toString(curve.getField().getOrder()))); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + fieldParams.appendChild(m); + + Element k1 = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":K1"); + k1.appendChild(factoryDoc.createTextNode(Integer.toString(coeffPositions[0]))); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + fieldParams.appendChild(k1); + + Element k2 = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":K2"); + k2.appendChild(factoryDoc.createTextNode(Integer.toString(coeffPositions[1]))); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + fieldParams.appendChild(k2); + + Element k3 = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":K3"); + k3.appendChild(factoryDoc.createTextNode(Integer.toString(coeffPositions[2]))); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + fieldParams.appendChild(k3); + fieldParams.appendChild(factoryDoc.createTextNode("\n ")); + } + + // Curve parameters + Element curveParams = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":CurveParams"); + explicitParams.appendChild(factoryDoc.createTextNode("\n ")); + explicitParams.appendChild(curveParams); + + Element a = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":A"); + a.setAttributeNS(xsiNS, xsiNSP + ":type", fieldElemTypeString); + a.setAttributeNS(null, "Value", + (fieldId == Field.PRIME_FIELD) + ? curve.getA().getValue().toBigInt().toString(10) + : evenStringLength(curve.getA().getValue().toBigInt().toString(16))); + curveParams.appendChild(factoryDoc.createTextNode("\n ")); + curveParams.appendChild(a); + + Element b = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":B"); + b.setAttributeNS(xsiNS, xsiNSP + ":type", fieldElemTypeString); + b.setAttributeNS(null, "Value", + (fieldId == Field.PRIME_FIELD) + ? curve.getB().getValue().toBigInt().toString(10) + : evenStringLength(curve.getB().getValue().toBigInt().toString(16))); + curveParams.appendChild(factoryDoc.createTextNode("\n ")); + curveParams.appendChild(b); + + if (params.getS() != null) + { + Element seed = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":Seed"); + seed.appendChild(factoryDoc.createTextNode(evenStringLength(params.getS().toString(16)))); + curveParams.appendChild(factoryDoc.createTextNode("\n ")); + curveParams.appendChild(seed); + } + + curveParams.appendChild(factoryDoc.createTextNode("\n ")); + + // Base point params + Element basePointParams = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":BasePointParams"); + explicitParams.appendChild(factoryDoc.createTextNode("\n ")); + explicitParams.appendChild(basePointParams); + + Element basePoint = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":BasePoint"); + basePointParams.appendChild(factoryDoc.createTextNode("\n ")); + basePointParams.appendChild(basePoint); + + Element x = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":X"); + x.setAttributeNS(xsiNS, xsiNSP + ":type", fieldElemTypeString); + x.setAttributeNS(null, "Value", + (fieldId == Field.PRIME_FIELD) + ? params.getG().getCoordinates().getX().getValue().toBigInt().toString(10) + : evenStringLength(params.getG().getCoordinates().getX().getValue().toBigInt().toString(16))); + basePoint.appendChild(factoryDoc.createTextNode("\n ")); + basePoint.appendChild(x); + + Element y = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":Y"); + y.setAttributeNS(xsiNS, xsiNSP + ":type", fieldElemTypeString); + y.setAttributeNS(null, "Value", + (fieldId == Field.PRIME_FIELD) + ? params.getG().getCoordinates().getY().getValue().toBigInt().toString(10) + : evenStringLength(params.getG().getCoordinates().getY().getValue().toBigInt().toString(16))); + basePoint.appendChild(factoryDoc.createTextNode("\n ")); + basePoint.appendChild(y); + basePoint.appendChild(factoryDoc.createTextNode("\n ")); + + Element order = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":Order"); + order.appendChild(factoryDoc.createTextNode(params.getR().toString(10))); + basePointParams.appendChild(factoryDoc.createTextNode("\n ")); + basePointParams.appendChild(order); + + if (params.getK() != null) + { + Element cofactor = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":Cofactor"); + cofactor.appendChild(factoryDoc.createTextNode(params.getK().toString(10))); + basePointParams.appendChild(factoryDoc.createTextNode("\n ")); + basePointParams.appendChild(cofactor); + } + + basePointParams.appendChild(factoryDoc.createTextNode("\n ")); + explicitParams.appendChild(factoryDoc.createTextNode("\n ")); + + domainParameters.appendChild(factoryDoc.createTextNode("\n ")); + domainParameters.appendChild(explicitParams); + domainParameters.appendChild(factoryDoc.createTextNode("\n ")); + } + } + + // Public key point + Element publicKeyPoint = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":PublicKey"); + + Element publicKeyX = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":X"); + publicKeyX.setAttributeNS(xsiNS, xsiNSP + ":type", fieldElemTypeString); + publicKeyX.setAttributeNS(null, "Value", + (fieldId == Field.PRIME_FIELD) + ? publicKey.getW().getCoordinates().getX().getValue().toBigInt().toString(10) + : evenStringLength(publicKey.getW().getCoordinates().getX().getValue().toBigInt().toString(16))); + publicKeyPoint.appendChild(factoryDoc.createTextNode("\n ")); + publicKeyPoint.appendChild(publicKeyX); + + Element publicKeyY = factoryDoc.createElementNS(ecdsaNS, ecdsaNSP + ":Y"); + publicKeyY.setAttributeNS(xsiNS, xsiNSP + ":type", fieldElemTypeString); + publicKeyY.setAttributeNS(null, "Value", + (fieldId == Field.PRIME_FIELD) + ? publicKey.getW().getCoordinates().getY().getValue().toBigInt().toString(10) + : evenStringLength(publicKey.getW().getCoordinates().getY().getValue().toBigInt().toString(16))); + publicKeyPoint.appendChild(factoryDoc.createTextNode("\n ")); + publicKeyPoint.appendChild(publicKeyY); + publicKeyPoint.appendChild(factoryDoc.createTextNode("\n ")); + + eCDSAKeyValue.appendChild(factoryDoc.createTextNode("\n ")); + eCDSAKeyValue.appendChild(publicKeyPoint); + eCDSAKeyValue.appendChild(factoryDoc.createTextNode("\n ")); + + return eCDSAKeyValue; + } + */ + /* ---------------------------------------------------------------------------------------------------- */ + + 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 (ECDSAConstants.NAMESPACE_ECDSAKEYVALUE_.equals(currentAttr.getValue())) + { + return ("xmlns".equals(currentAttr.getNodeName())) ? "" : currentAttr.getNodeName().substring(6); + } + } + return null; + } + + /* ---------------------------------------------------------------------------------------------------- */ + + // Converts an octet string representation into an int array as needed for the IAIK ECC library + // String: rightmost byte is least significant byte + // IntArray: 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 + // String: rightmost byte is least significant byte + // ByteArray: 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; + } + + /* ---------------------------------------------------------------------------------------------------- */ + + private static String evenStringLength(String hexString) + { + return (hexString.length() % 2 != 0) ? "0" + hexString : hexString; + } + + /* ---------------------------------------------------------------------------------------------------- */ + + 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); + } + + /* ---------------------------------------------------------------------------------------------------- */ + + 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; + } + + /* ---------------------------------------------------------------------------------------------------- */ + + 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/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/HTTPRequestJSPForwarder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/HTTPRequestJSPForwarder.java new file mode 100644 index 000000000..7c4731555 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/HTTPRequestJSPForwarder.java @@ -0,0 +1,76 @@ +/* + * Created on 17.02.2004 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package at.gv.egovernment.moa.id.util; + +import java.io.IOException; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import at.gv.egovernment.moa.logging.Logger; + +/** + * @author rschamberger + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class HTTPRequestJSPForwarder { + + /** + * Forwards the HttpServletRequest to a customizable JSP Page and serves the Response. <br> + * <ul> + * <li>Logs the message</li> + * </ul> + * + * @param message message text + * @param jspPageURI URI of the JSP page + * @param context the servlet context of the servlet belonging to the req, resp + * @param req servlet request + * @param resp servlet response + */ + public static void forwardNamed( + String message, + String jspPageURI, + ServletContext context, + HttpServletRequest req, + HttpServletResponse resp) { + + if (null != message) { + Logger.info(message); + req.setAttribute("Message", message); + } + + //forward this to the given jsp page where the HTML response is generated + try { + context.getRequestDispatcher(jspPageURI).forward(req, resp); + } catch (IOException e) { + Logger.error(e); + } catch (ServletException e) { + Logger.error(e); + } + } + + /** + * Forwards the HttpServletRequest to the customizable JSP Page 'message.jsp' and serves the Response. <br> + * <ul> + * <li>Logs the message</li> + * </ul> + * + * @param message message text + * @param context the servlet context of the servlet belonging to the req, resp + * @param req servlet request + * @param resp servlet response + */ + public static void forwardDefault( + String message, + ServletContext context, + HttpServletRequest req, + HttpServletResponse resp) { + forwardNamed(message, "/message.jsp", context, req, resp); + } +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/HTTPUtils.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/HTTPUtils.java new file mode 100644 index 000000000..035c47eb9 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/HTTPUtils.java @@ -0,0 +1,68 @@ +package at.gv.egovernment.moa.id.util; + +/** + * HTTP Utilities + * + * @author Rudolf Schamberger + * @version $Id$ + */ + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.apache.regexp.RE; +import org.apache.regexp.RESyntaxException; + +/** + * + * @author Rudolf Schamberger + * + */ +public class HTTPUtils { + + /** + * Utility used to obtainin correct encoded HTTP content. + * Reads a given Content adressed by HTTP-URL into String. + * Content encoding is considered by using the Content-Type HTTP header charset value. + * @param URL HTTP URL to read from. + * @return String representation of content + * @throws IOException on data-reading problems + */ + public static String readHttpURL(String URL) + throws IOException { + + URL url = new URL(URL); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestMethod("GET"); + String contentType = conn.getContentType(); + RE regExp = null; + try { + regExp = new RE("(;.*charset=)(\"*)(.*[^\"])"); + } catch (RESyntaxException e) { + //RESyntaxException is not possible = expr. is costant + } + boolean charsetSupplied = regExp.match(contentType); + String encoding = "ISO-8859-1"; //default HTTP encoding + if (charsetSupplied) { + encoding = regExp.getParen(3); + } + InputStream instream = new BufferedInputStream(conn.getInputStream()); + InputStreamReader isr = new InputStreamReader(instream, encoding); + Reader in = new BufferedReader(isr); + int ch; + StringBuffer buffer = new StringBuffer(); + while ((ch = in.read()) > -1) { + buffer.append((char)ch); + } + in.close(); + conn.disconnect(); + return buffer.toString(); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/InOrderServletRequestWrapper.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/InOrderServletRequestWrapper.java new file mode 100644 index 000000000..4330133f0 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/InOrderServletRequestWrapper.java @@ -0,0 +1,374 @@ +/* + * Created on 01.10.2004 + * + * @author rschamberger + * $ID$ + */ +package at.gv.egovernment.moa.id.util; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.Vector; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequestWrapper; + +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.URLDecoder; + +/** + * Special ServletRequestWrapper class which provides a more precise implementation of the getParameter* + * family. This implementation cares about the order of the parameters from Query String and HTTP POST + * Body. Use this as Filter class for Servlets which such needs. + * + * @author Rudolf Schamberger + * @version $Id$ + */ +public class InOrderServletRequestWrapper extends HttpServletRequestWrapper { + + /** + * standard encoding used to decode the URL string. + */ + // + public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1"; + /** + * Vector that stores the order of the query paramters + */ + private Vector queryParamOrder; + + /** + * Hashtable that stores the content of the query paramters + */ + private Hashtable queryParameters; + + /** + * Vector that stores the order of the HTTP body paramters + */ + private Vector bodyParamOrder; + + /** + * Hashtable that stores the content of the HTTP body paramters + */ + private Hashtable bodyParameters; + + /** + * ServletContext + */ + private ServletContext context; + + /** + * Identifier used to identify query parameters + */ + public static final int QUERY_PARAM = 1; + + /** + * Identifier used to identify HTTP body parameters + */ + public static final int BODY_PARAM = 2; + + /** + * @see HttpServletRequestWrapper + */ + public InOrderServletRequestWrapper(final HttpServletRequest request, final ServletContext sContext) { + super(request); + this.context = sContext; + } + + /** + * parses the Query and if availlable also HTTP POST parameters + * + * @param req a <code>HttpServletRequest</code> which should be parsed + */ + protected final void parseParameters(final HttpServletRequest req) + { + queryParamOrder = new Vector(); + queryParameters = new Hashtable(); + bodyParamOrder = new Vector(); + bodyParameters = new Hashtable(); + + //Insert code for Query string parsing + String rawQuery = req.getQueryString(); + queryParameters = tokenize(queryParameters, queryParamOrder, rawQuery, DEFAULT_CHARACTER_ENCODING, true); + + //analyze HTTP Post body parameters + if (req.getMethod().equalsIgnoreCase("POST")) + { + //get body encoding + String enc = req.getCharacterEncoding(); + if (enc == null) enc = DEFAULT_CHARACTER_ENCODING; + + if (req.getContentType().equals("application/x-www-form-urlencoded")) + { + try + { + bodyParameters = parsePostData(bodyParameters, req.getContentLength(), req.getInputStream(), enc); + } + catch (IOException e) + { + context.log("could not open input stream of reqest \n" + e.toString()); + } + } + else + { + //TODO add multipart code + context.log( + "ERROR other Content-Types than 'application/x-www-form-urlencoded' not supported!"); + } + + }// end POST + } + + /** + * parses the HTTP POST parameters + * + * @param ht parameter Hashtable to put parameters in. + * @param length of content + * @param instream the ServletInputStream of the request + * @param encoding encoding of the instream + * + * @return the Hashtable with the parsed data + */ + private Hashtable parsePostData(Hashtable ht, final int length, final ServletInputStream instream, + final String encoding) + { + int inputLen, offset; + byte[] postedBytes = null; + boolean dataRemaining = true; + String postedBody; + + StringBuffer sb = new StringBuffer(); + + if (length <= 0) + { + return null; + } + + postedBytes = new byte[length]; + try + { + offset = 0; + while (dataRemaining) + { + inputLen = instream.read(postedBytes, offset, length - offset); + if (inputLen <= 0) + { + throw new IOException("read error during reading the HTTP POST body"); + } + offset += inputLen; + if ((length - offset) == 0) + { + dataRemaining = false; + } + } + } + catch (IOException e) + { + System.out.println("Exception =" + e); + return null; + } + + postedBody = new String(postedBytes); + Hashtable ht2 = tokenize(ht, bodyParamOrder, postedBody, encoding, false); + return ht2; + } + + + /** + * tokenizes parameter strings + * + * @param ht parameter Hashtable to put parameters in. + * @param order Vector in which the order of the tokenized parameters will be stored. + * @param parameterString String to tokenize. + * @param encoding which will be used to decode the parameterString. + * + * @return the Hashtable with the parsed data + */ + private Hashtable tokenize(Hashtable ht, Vector order, final String parameterString, final String encoding, boolean decode) + { + String[] valArray = null; + + if (null == parameterString) return ht; + + StringTokenizer st = new StringTokenizer(parameterString, "&"); + + String key = null; + String val = null; + + while (st.hasMoreTokens()) + { + String pair = (String) st.nextToken(); + int pos = pair.indexOf('='); + if (pos == -1) + { + throw new IllegalArgumentException(); + } + try + { + if (decode) { + key = URLDecoder.decode(pair.substring(0, pos), encoding); + val = URLDecoder.decode(pair.substring(pos + 1, pair.length()), encoding); + } else { + key = pair.substring(0, pos); + val = pair.substring(pos + 1, pair.length()); + } + //Logger.debug("(" + Integer.toString(key.length()) + "=" + Integer.toString(pair.substring(0, pos).length()) + ")"+key+"|--|"+pair.substring(0, pos)); + //Logger.debug("(" + Integer.toString(val.length()) + "=" + Integer.toString(pair.substring(pos + 1, pair.length()).length()) + ")"+val+"|--|"+pair.substring(pos + 1, pair.length())); + } + catch (Exception e) + { + throw new IllegalArgumentException(); + } + if (ht.containsKey(key)) + { + String oldVals[] = (String[]) ht.get(key); + valArray = new String[oldVals.length + 1]; + for (int i = 0; i < oldVals.length; i++) + { + valArray[i] = oldVals[i]; + } + valArray[oldVals.length] = val; + } + else + { + valArray = new String[1]; + valArray[0] = val; + } + ht.put(key, valArray); + order.addElement(key); + } + return ht; + + } + + /** + * Returns the value of a request parameter as a <code>String</code>, or <code>null</code> if the + * parameter does not exist. Request parameters are extra information sent with the request. For HTTP + * servlets, parameters are contained in the query string or posted form data. + * + * <p> + * You should only use this method when you are sure the parameter has only one value. If the parameter + * might have more than one value, use {@link #getParameterValues(String, int)}. + * + * <p> + * If you use this method with a multivalued parameter, the value returned is equal to the first value in + * the array returned by <code>getParameterValues</code>. + * + * <p> + * If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then + * reading the body directly via {@link #getInputStream} or {@link #getReader}can interfere with the + * execution of this method. + * + * @param name a <code>String</code> containing the name of the parameter whose value is requested + * + * @return a <code>String</code> representing the single value of the parameter + * + * @see #getParameterValues(String, int) + * + */ + public final String getParameter(final String name) { + String val = getParameter(name, QUERY_PARAM); + return (null != val) ? val : getParameter(name, BODY_PARAM); + } + + /** + * Returns the value of a request parameter as a <code>String</code>, or <code>null</code> if the + * parameter does not exist. + * + * @param name a <code>String</code> containing the name of the parameter whose value is requested + * @param parameterType type of parameter + * @see at.gv.egovernment.moa.id.util.InOrderServletRequestWrapper#QUERY_PARAM + * and @see at.gv.egovernment.moa.id.util.InOrderServletRequestWrapper#BODY_PARAM + * @see #getParameterValues(String) + * @return value of the (single) parameter or null if not availlable + **/ + public final String getParameter(final String name, final int parameterType) + { + + Hashtable parameters = (parameterType == QUERY_PARAM) ? queryParameters : bodyParameters; + String[] vals = (String[]) parameters.get(name); + if (vals == null) + { + return null; + } + return vals[0]; + } + + + /** + * Returns an array of <code>String</code> objects containing all of the values the given request + * parameter has, or <code>null</code> if the parameter does not exist. + * + * <p> + * If the parameter has a single value, the array has a length of 1. + * + * @param name a <code>String</code> containing the name of the parameter whose value is requested + * @param parameterType type of parameter + * @see at.gv.egovernment.moa.id.util.InOrderServletRequestWrapper#QUERY_PARAM + * and @see at.gv.egovernment.moa.id.util.InOrderServletRequestWrapper#BODY_PARAM + * @return an array of <code>String</code> objects containing the parameter's values or null + * + * @see #getParameter + */ + public final String getParameterValues(final String name, final int parameterType) + { + Hashtable parameters = (parameterType == QUERY_PARAM) ? queryParameters : bodyParameters; + String[] vals = (String[]) parameters.get(name); + if (vals == null) + { + return null; + } + String vallist = vals[0]; + for (int i = 1; i < vals.length; i++) + { + vallist = vallist + "," + vals[i]; + } + return vallist; + } + + /** + * + * Returns an <code>Enumeration</code> of <code>String</code> objects containing the names of the + * parameters. If there are no parameters, the method returns an empty + * <code>Enumeration</code>. + * + * @return an <code>Enumeration</code> of <code>String</code> objects, each <code>String</code> + * containing the name of a request parameter; or an empty <code>Enumeration</code> if the + * request has no parameters + * + */ + public final Enumeration getParameterNames() + { + Vector FullParamOrder = new Vector(); + for (Enumeration enu = queryParamOrder.elements(); enu.hasMoreElements();) { + FullParamOrder.addElement(enu.nextElement()); + } + for (Enumeration enu = bodyParamOrder.elements(); enu.hasMoreElements();) { + FullParamOrder.addElement(enu.nextElement()); + } + return FullParamOrder.elements(); + } + + /** + * + * Returns an <code>Enumeration</code> of <code>String</code> objects containing the names of the + * parameters contained in this request. If the request has no parameters, the method returns an empty + * <code>Enumeration</code>. + * @param parameterType type of parameter + * + * @return an <code>Enumeration</code> of <code>String</code> objects, each <code>String</code> + * containing the name of a request parameter; or an empty <code>Enumeration</code> if the + * request has no parameters + * + */ + public final Enumeration getParameterNames(final int parameterType) + { + if (QUERY_PARAM == parameterType) + return queryParamOrder.elements(); + else + return bodyParamOrder.elements(); + } +} //End InOrderServletRequestWrapper
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/MOAIDMessageProvider.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/MOAIDMessageProvider.java new file mode 100644 index 000000000..d31aa6ec1 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/MOAIDMessageProvider.java @@ -0,0 +1,58 @@ +package at.gv.egovernment.moa.id.util; + +import java.util.Locale; + +import at.gv.egovernment.moa.util.Messages; + +/** + * A singleton wrapper around a <code>Message</code> object, providing the messages used in MOA-ID. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class MOAIDMessageProvider { + + /** DEFAULT_MESSAGE_RESOURCES are resources/properties/id_messages */ + private static final String[] DEFAULT_MESSAGE_RESOURCES = + { "resources/properties/id_messages" }; + /** DEFAULT_MESSAGE_LOCALES are "de", "AT" */ + private static final Locale[] DEFAULT_MESSAGE_LOCALES = + new Locale[] { new Locale("de", "AT") }; + /** The instance for our singleton */ + private static MOAIDMessageProvider instance; + /** The Messages */ + private Messages messages; + + /** + * Returns the single instance of <code>MOAIDMessageProvider</code>. + * + * @return the single instance of <code>MOAIDMessageProvider</code> + */ + public static MOAIDMessageProvider getInstance() { + if (instance == null) + instance = new MOAIDMessageProvider(DEFAULT_MESSAGE_RESOURCES, DEFAULT_MESSAGE_LOCALES); + return instance; + } + + /** + * Create a <code>MOAIDMessageProvider</code>. + * + * @param resourceNames The names of the resources containing the messages. + * @param locales The corresponding locales. + */ + protected MOAIDMessageProvider(String[] resourceNames, Locale[] locales) { + this.messages = new Messages(resourceNames, locales); + } + + /** + * Get the message corresponding to a given message ID. + * + * @param messageId The ID of the message. + * @param parameters The parameters to fill in into the message arguments. + * @return The formatted message. + */ + public String getMessage(String messageId, Object[] parameters) { + return messages.getMessage(messageId, parameters); + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ParameterInOrderFilter.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ParameterInOrderFilter.java new file mode 100644 index 000000000..3f5fddba2 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/ParameterInOrderFilter.java @@ -0,0 +1,62 @@ +package at.gv.egovernment.moa.id.util; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +/** + * @author rschamberger + * + */ +/** + * A Filter class wich uses the InOrderServletRequestWrapper to provide servlets a more precise + * implementation of the getParameter* family. This implementation cares about the order of the parameters + * from Query String and HTTP POST Body. Use this as Filter class for Servlets which such needs. + * + * @author Rudolf Schamberger + * @version $Id$ + */ +public class ParameterInOrderFilter implements Filter +{ + + /** + * filterConfig + */ + private FilterConfig filterConfig; + + /** + * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) + */ + public final void init(final FilterConfig config) + { + this.filterConfig = config; + } + + /** + * @see javax.servlet.Filter#destroy() + */ + public final void destroy() + { + }; + + /** + * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, + * javax.servlet.FilterChain) + */ + public final void doFilter(final ServletRequest request, final ServletResponse response, + final FilterChain chain) throws IOException, ServletException + { + InOrderServletRequestWrapper sRequ = new InOrderServletRequestWrapper((HttpServletRequest) request, + filterConfig.getServletContext()); + //parse the Query (and Body) parameters + sRequ.parseParameters((HttpServletRequest) request); + //process the rest of filter chain + chain.doFilter(sRequ, response); + } +}
\ No newline at end of file diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/Random.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/Random.java new file mode 100644 index 000000000..da75b4213 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/Random.java @@ -0,0 +1,22 @@ +package at.gv.egovernment.moa.id.util; + +import java.util.Date; + +/** + * Random number generator used to generate ID's + * @author Paul Ivancsics + * @version $Id$ + */ +public class Random { + + /** random number generator used */ + private static java.util.Random random = new java.util.Random(new Date().getTime()); + /** + * Creates a new random number, to be used as an ID. + * + * @return random long as a String + */ + public static String nextRandom() { + return "" + random.nextLong(); + } +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/SSLUtils.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/SSLUtils.java new file mode 100644 index 000000000..9fa0803c4 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/util/SSLUtils.java @@ -0,0 +1,180 @@ +package at.gv.egovernment.moa.id.util; + +import iaik.pki.PKIConfiguration; +import iaik.pki.PKIException; +import iaik.pki.PKIFactory; +import iaik.pki.PKIProfile; +import iaik.pki.jsse.IAIKX509TrustManager; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.Security; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +import org.apache.regexp.RE; +import org.apache.regexp.RESyntaxException; + +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.iaik.config.PKIConfigurationImpl; +import at.gv.egovernment.moa.id.iaik.pki.PKIProfileImpl; +import at.gv.egovernment.moa.id.iaik.pki.jsse.MOAIDTrustManager; +import at.gv.egovernment.moa.logging.Logger; + +/** + * Utility for a obtaining a secure socket factory using <code>IAIKX509TrustManager</code>. + * This <code>TrustManager</code> implementation features CRL checking.<br/> + * <code>SSLUtils</code> caches secure socket factories for given <code>ConnectionParameter</code>s. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class SSLUtils { + + /** SSLSocketFactory store, mapping URL->SSLSocketFactory **/ + private static Map sslSocketFactories = new HashMap(); + + /** + * Initializes the SSLSocketFactory store. + */ + public static void initialize() { + sslSocketFactories = new HashMap(); + Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); + System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); + } + + /** + * Creates an <code>SSLSocketFactory</code> which utilizes an + * <code>IAIKX509TrustManager</code> for the given trust store, + * and the given key store. + * + * @param conf configuration provider providing a generic properties pointing + * to trusted CA store and certificate store root + * @param connParam connection parameter containing the client key store settings + * to be used in case of client authentication; + * if <code>connParam.getClientKeyStore() == null</code>, client authentication + * is assumed to be disabled + * @return <code>SSLSocketFactory</code> to be used by an <code>HttpsURLConnection</code> + * @throws IOException thrown while reading key store file + * @throws GeneralSecurityException thrown while creating the socket factory + * @throws ConfigurationException on invalid configuration data + * @throws PKIException while initializing the <code>IAIKX509TrustManager</code> + */ + public static SSLSocketFactory getSSLSocketFactory( + ConfigurationProvider conf, + ConnectionParameter connParam) + throws IOException, GeneralSecurityException, ConfigurationException, PKIException { + + Logger.debug("Get SSLSocketFactory for " + connParam.getUrl()); + // retrieve SSLSocketFactory if already created + SSLSocketFactory ssf = (SSLSocketFactory)sslSocketFactories.get(connParam.getUrl()); + if (ssf != null) + return ssf; + // else create new SSLSocketFactory + String trustStoreURL = conf.getTrustedCACertificates(); + if (trustStoreURL == null) + throw new ConfigurationException( + "config.08", new Object[] {"TrustedCACertificates"}); + String acceptedServerCertURL = connParam.getAcceptedServerCertificates(); + TrustManager[] tms = getTrustManagers(conf, trustStoreURL, acceptedServerCertURL); + KeyManager[] kms = at.gv.egovernment.moa.util.SSLUtils.getKeyManagers( + "pkcs12", connParam.getClientKeyStore(), connParam.getClientKeyStorePassword()); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(kms, tms, null); + ssf = ctx.getSocketFactory(); + // store SSLSocketFactory + sslSocketFactories.put(connParam.getUrl(), ssf); + return ssf; + } + + /** + * Initializes an <code>IAIKX509TrustManager</code> for a given trust store, + * using configuration data. + * + * @param conf MOA-ID configuration provider + * @param trustStoreURL trust store URL + * @param acceptedServerCertURL file URL pointing to directory containing accepted server SSL certificates + * @return <code>TrustManager</code> array containing the <code>IAIKX509TrustManager</code> + * @throws ConfigurationException on invalid configuration data + * @throws IOException on data-reading problems + * @throws PKIException while initializing the <code>IAIKX509TrustManager</code> + */ + public static TrustManager[] getTrustManagers( + ConfigurationProvider conf, String trustStoreURL, String acceptedServerCertURL) + throws ConfigurationException, PKIException, IOException, GeneralSecurityException { + + PKIConfiguration cfg = null; + if (! PKIFactory.getInstance().isAlreadyConfigured()) + cfg = new PKIConfigurationImpl(conf); + String boolString = conf.getGenericConfigurationParameter(ConfigurationProvider.TRUST_MANAGER_REVOCATION_CHECKING); + //not using BoolUtils because default value hast to be true! + boolean checkRevocation = !("false".equals(boolString) || "0".equals(boolString)); + PKIProfile profile = new PKIProfileImpl(trustStoreURL, checkRevocation); + // This call fixes a bug occuring when PKIConfiguration is + // initialized by the MOA-SP initialization code, in case + // MOA-SP is called by API + MOAIDTrustManager.initializeLoggingContext(); + IAIKX509TrustManager tm = new MOAIDTrustManager(acceptedServerCertURL); + tm.init(cfg, profile); + return new TrustManager[] {tm}; + } + /** + * Reads a file, given by URL, into a byte array, + * securing the connection by IAIKX509TrustManager. + * @param connParam containing URL and accepted server certificates + * @param conf ConfigurationProvider for reading + * @return String representation of content + * @throws ConfigurationException on invalid configuration data + * @throws PKIException on invalid configuration data + * @throws IOException on data-reading problems + * @throws GeneralSecurityException on security issues + */ + public static String readHttpsURL(ConfigurationProvider conf, ConnectionParameter connParam) + throws ConfigurationException, PKIException, IOException, GeneralSecurityException { + + URL url = new URL(connParam.getUrl()); + HttpsURLConnection conn = (HttpsURLConnection)url.openConnection(); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + SSLSocketFactory sslSocketFactory = getSSLSocketFactory(conf, connParam); + conn.setSSLSocketFactory(sslSocketFactory); + conn.connect(); + String contentType = conn.getContentType(); + RE regExp = null; + try { + regExp = new RE("(;.*charset=)(\"*)(.*[^\"])"); + } catch (RESyntaxException e) { + //RESyntaxException is not possible = expr. is costant + } + boolean charsetSupplied = regExp.match(contentType); + String encoding = "ISO-8859-1"; //default HTTP encoding + if (charsetSupplied) { + encoding = regExp.getParen(3); + } + InputStream instream = new BufferedInputStream(conn.getInputStream()); + InputStreamReader isr = new InputStreamReader(instream, encoding); + Reader in = new BufferedReader(isr); + int ch; + StringBuffer buffer = new StringBuffer(); + while ((ch = in.read()) > -1) { + buffer.append((char)ch); + } + in.close(); + conn.disconnect(); + return buffer.toString(); + } +} |