summaryrefslogtreecommitdiff
path: root/eaaf_core_utils
diff options
context:
space:
mode:
Diffstat (limited to 'eaaf_core_utils')
-rw-r--r--eaaf_core_utils/pom.xml150
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java61
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java44
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java113
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java175
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java178
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java152
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java21
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java199
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java375
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java113
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java70
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java176
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java214
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java43
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java38
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java201
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java101
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java62
19 files changed, 2486 insertions, 0 deletions
diff --git a/eaaf_core_utils/pom.xml b/eaaf_core_utils/pom.xml
new file mode 100644
index 00000000..6168f601
--- /dev/null
+++ b/eaaf_core_utils/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>at.gv.egiz</groupId>
+ <artifactId>eaaf</artifactId>
+ <version>1.0.12-SNAPSHOT</version>
+ </parent>
+ <groupId>at.gv.egiz.eaaf</groupId>
+ <artifactId>eaaf_core_utils</artifactId>
+ <name>Utils for EAAF core components</name>
+ <description>Core component Utils for identity managment implementations</description>
+
+ <licenses>
+ <license>
+ <name>European Union Public License, version 1.2 (EUPL-1.2)</name>
+ <url>https://opensource.org/licenses/EUPL-1.2</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <developers>
+ <developer>
+ <name>Thomas Lenz</name>
+ <email>thomas.lenz@egiz.gv.at</email>
+ <organization>eGovernment Innovation Center (EGIZ)</organization>
+ <organizationUrl>https://www.egiz.gv.at</organizationUrl>
+ </developer>
+ </developers>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>at.gv.egiz.eaaf</groupId>
+ <artifactId>eaaf_core_api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>joda-time</groupId>
+ <artifactId>joda-time</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <finalName>eaaf_core_utils</finalName>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.7.0</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ <goal>testCompile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- enable co-existence of testng and junit -->
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire.version}</version>
+ <configuration>
+ <threadCount>1</threadCount>
+ </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.surefire</groupId>
+ <artifactId>surefire-junit47</artifactId>
+ <version>${surefire.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+
+ </plugins>
+ </build>
+
+
+</project>
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java
new file mode 100644
index 00000000..a297367f
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+package at.gv.egiz.eaaf.core.impl.idp.process.support;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+/**
+ * Holder for a secure random instance following the initialization on demand holder design pattern. The secure random
+ * instance is a singleton that is initialized on first usage.
+ *
+ * @author tknall
+ *
+ */
+public class SecureRandomHolder {
+
+ private SecureRandomHolder() {
+ }
+
+ private static final SecureRandom SRND_INSTANCE;
+ static {
+ try {
+ SRND_INSTANCE = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Unable to instantiate SHA1PRNG.", e);
+ }
+ }
+
+ /**
+ * Returns a secure random generator instance.
+ * @return The secure random instance.
+ */
+ public static SecureRandom getInstance() {
+ return SecureRandomHolder.SRND_INSTANCE;
+ }
+
+} \ No newline at end of file
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java
new file mode 100644
index 00000000..8585bc05
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.util.List;
+
+public class ArrayUtils {
+
+ /**
+ * Check if a String 's' is part of a List 'l' in qualsIgnoreCase mode
+ *
+ * @param s Search String
+ * @param l List of String elements
+ * @return true if 's' is in 'l', otherwise false
+ */
+ public static boolean containsCaseInsensitive(String s, List<String> l){
+ if (l == null || s == null)
+ return false;
+
+ return l.stream().anyMatch(x -> x.equalsIgnoreCase(s));
+
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java
new file mode 100644
index 00000000..a81fafbc
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright 2014 Federal Chancellery Austria
+ * MOA-ID has been developed in a cooperation between BRZ, the Federal
+ * Chancellery Austria - ICT staff unit, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * http://www.osor.eu/eupl/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ ******************************************************************************/
+/*
+ * Copyright 2003 Federal Chancellery Austria
+ * MOA-ID has been developed in a cooperation between BRZ, the Federal
+ * Chancellery Austria - ICT staff unit, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * http://www.osor.eu/eupl/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ */
+
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import at.gv.egiz.eaaf.core.api.data.EAAFConstants;
+
+/**
+ * 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 pendingReqId sessionID to be included in the dataURL
+ * @return String
+ */
+ public String buildDataURL(String authBaseURL, String authServletName, String pendingReqId) {
+ String dataURL;
+ if (!authBaseURL.endsWith("/"))
+ authBaseURL += "/";
+
+ if (authServletName.startsWith("/"))
+ authServletName = authServletName.substring(1);
+
+ dataURL = authBaseURL + authServletName;
+
+ if (StringUtils.isNotEmpty(pendingReqId))
+ dataURL = addParameter(dataURL, EAAFConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID, pendingReqId);
+
+ 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/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java
new file mode 100644
index 00000000..fff83e53
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FileUtils {
+ private static final Logger log = LoggerFactory.getLogger(FileUtils.class);
+
+
+ /**
+ * Reads a file, given by URL, into a byte array.
+ * @param urlString file URL
+ * @return file content
+ * @throws IOException on any exception thrown
+ */
+ public static byte[] readURL(String urlString) throws IOException {
+ final URL url = new URL(urlString);
+ final InputStream in = new BufferedInputStream(url.openStream());
+ final byte[] content = StreamUtils.readStream(in);
+ in.close();
+ return content;
+ }
+
+ /**
+ * Reads a file from a resource.
+ * @param name resource name
+ * @return file content as a byte array
+ * @throws IOException on any exception thrown
+ */
+ public static byte[] readResource(String name) throws IOException {
+ final ClassLoader cl = FileUtils.class.getClassLoader();
+ final BufferedInputStream in = new BufferedInputStream(cl.getResourceAsStream(name));
+ final byte[] content = StreamUtils.readStream(in);
+ in.close();
+ return content;
+ }
+ /**
+ * Reads a file from a resource.
+ * @param name filename
+ * @param encoding character encoding
+ * @return file content
+ * @throws IOException on any exception thrown
+ */
+ public static String readResource(String name, String encoding) throws IOException {
+ final byte[] content = readResource(name);
+ return new String(content, encoding);
+ }
+
+
+ /**
+ * Returns the absolute URL of a given url which is relative to the parameter root
+ * @param url
+ * @param root
+ * @return String
+ * @throws MalformedURLException
+ */
+ public static String makeAbsoluteURL(String url, URI root) throws MalformedURLException {
+ if (root != null)
+ return makeAbsoluteURL(url, root.toURL().toString());
+ else
+ return makeAbsoluteURL(url, StringUtils.EMPTY);
+
+ }
+
+ /**
+ * Returns the absolute URL of a given url which is relative to the parameter root
+ * @param url
+ * @param root
+ * @return String
+ */
+ public static String makeAbsoluteURL(String url, String root) {
+ //if url is relative to rootConfigFileDirName make it absolute
+
+ log.trace("Making AbsoluteURL URL: " + url + " Root-Path: " + root);
+
+ if (StringUtils.isEmpty(root))
+ root = null;
+
+ File keyFile;
+ String newURL = url;
+
+ if(null == url) return null;
+
+ if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:") || url.startsWith("ftp:")) {
+ return url;
+
+ } else {
+ // check if absolute - if not make it absolute
+ keyFile = new File(url);
+ if (!keyFile.isAbsolute()) {
+ keyFile = new File(root, url);
+
+ if (keyFile.toString().startsWith("file:"))
+ newURL = keyFile.toString();
+
+ else
+ newURL = keyFile.toURI().toString();
+
+ }
+ return newURL;
+ }
+ }
+
+
+ private static void copy( InputStream fis, OutputStream fos )
+ {
+ try
+ {
+ final byte[] buffer = new byte[ 0xFFFF ];
+ for ( int len; (len = fis.read(buffer)) != -1; )
+ fos.write( buffer, 0, len );
+ }
+ catch( final IOException e ) {
+ System.err.println( e );
+ }
+ finally {
+ if ( fis != null )
+ try { fis.close(); } catch ( final IOException e ) { e.printStackTrace(); }
+ if ( fos != null )
+ try { fos.close(); } catch ( final IOException e ) { e.printStackTrace(); }
+ }
+ }
+
+ public static void copyFile(File src, File dest)
+ {
+ try
+ {
+ copy( new FileInputStream( src ), new FileOutputStream( dest ) );
+ }
+ catch( final IOException e ) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java
new file mode 100644
index 00000000..cf1abaa7
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright 2014 Federal Chancellery Austria
+ * MOA-ID has been developed in a cooperation between BRZ, the Federal
+ * Chancellery Austria - ICT staff unit, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * http://www.osor.eu/eupl/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ ******************************************************************************/
+/*
+ * Copyright 2003 Federal Chancellery Austria
+ * MOA-ID has been developed in a cooperation between BRZ, the Federal
+ * Chancellery Austria - ICT staff unit, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * http://www.osor.eu/eupl/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ */
+
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+
+
+/**
+ *
+ * @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();
+// }
+
+ /**
+ * Helper method to retrieve server URL including context path
+ * @param request HttpServletRequest
+ * @return Server URL including context path (e.g. http://localhost:8443/moa-id-auth
+ */
+ public static String getBaseURL(HttpServletRequest request) {
+ StringBuffer buffer = new StringBuffer(getServerURL(request));
+
+ // add context path if available
+ String contextPath = request.getContextPath();
+ if (!StringUtils.isEmpty(contextPath)) {
+ buffer.append(contextPath);
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Helper method to retrieve server URL
+ * @param request HttpServletRequest
+ * @return Server URL (e.g. http://localhost:8443)
+ */
+ public static String getServerURL(HttpServletRequest request) {
+ StringBuffer buffer = new StringBuffer();
+
+ // get protocol
+ String protocol = request.getScheme();
+ buffer.append(protocol).append("://");
+
+ // server name
+ buffer.append(request.getServerName());
+
+ // add port if necessary
+ int port = request.getServerPort();
+ if ((protocol.equals("http") && port != 80) || (protocol.equals("https") && port != 443)) {
+ buffer.append(':');
+ buffer.append(port);
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Extract the IDP PublicURLPrefix from authrequest
+ *
+ * @param req HttpServletRequest
+ * @return PublicURLPrefix <String> which ends always without /
+ */
+ public static String extractAuthURLFromRequest(HttpServletRequest req) {
+ String authURL = req.getScheme() + "://" + req.getServerName();
+ if ((req.getScheme().equalsIgnoreCase("https") && req.getServerPort()!=443) || (req.getScheme().equalsIgnoreCase("http") && req.getServerPort()!=80)) {
+ authURL = authURL.concat(":" + req.getServerPort());
+ }
+ authURL = authURL.concat(req.getContextPath());
+ return authURL;
+
+ }
+
+ /**
+ * Extract the IDP requested URL from authrequest
+ *
+ * @param req HttpServletRequest
+ * @return RequestURL <String> which ends always without /
+ */
+ public static String extractAuthServletPathFromRequest(HttpServletRequest req) {
+ return extractAuthURLFromRequest(req).concat(req.getServletPath());
+
+ }
+
+ public 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/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java
new file mode 100644
index 00000000..a141d92d
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java
@@ -0,0 +1,152 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.security.NoSuchAlgorithmException;
+
+import javax.annotation.PostConstruct;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolException;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.protocol.HttpContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+
+public class HttpClientFactory implements IHttpClientFactory {
+ private static final Logger log = LoggerFactory.getLogger(HttpClientFactory.class);
+ @Autowired(required=true) private IConfiguration basicConfig;
+
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE = "client.http.connection.pool.use";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "client.http.connection.pool.maxtotal";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "client.http.connection.pool.maxperroute";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "client.http.connection.timeout.socket";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "client.http.connection.timeout.connection";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "client.http.connection.timeout.request";
+ public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = "client.http.ssl.hostnameverifier.trustall";
+
+ // default configuration values
+ public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15";
+ public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "15";
+ public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "30";
+ public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500";
+ public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100";
+
+ private HttpClientBuilder httpClientBuilder = null;
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory#getHttpClient()
+ */
+ @Override
+ public CloseableHttpClient getHttpClient() {
+ return getHttpClient(true);
+
+ }
+
+ @Override
+ public CloseableHttpClient getHttpClient(boolean followRedirects) {
+ RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+ if (!followRedirects)
+ redirectStrategy = new RedirectStrategy() {
+
+ @Override
+ public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context)
+ throws ProtocolException {
+ return false;
+ }
+
+ @Override
+ public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context)
+ throws ProtocolException {
+ return null;
+ }
+ };
+
+ return httpClientBuilder
+ .setRedirectStrategy(redirectStrategy)
+ .build();
+
+ }
+
+ @PostConstruct
+ private void initalize() {
+ //initialize http client
+ log.trace("Initializing HTTP Client-builder ... ");
+ httpClientBuilder = HttpClients.custom();
+
+ //set default request configuration
+ final RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(Integer.valueOf(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000)
+ .setConnectionRequestTimeout(Integer.valueOf(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000)
+ .setSocketTimeout(Integer.valueOf(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) * 1000)
+ .build();
+ httpClientBuilder.setDefaultRequestConfig(requestConfig);
+
+ //set pool connection if requested
+ if (basicConfig.getBasicConfigurationBoolean(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE,
+ true)) {
+ final PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager();
+ pool.setDefaultMaxPerRoute(Integer.valueOf(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE)));
+ pool.setMaxTotal(Integer.valueOf(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL)));
+
+
+
+ httpClientBuilder.setConnectionManager(pool);
+ log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), pool.getDefaultMaxPerRoute());
+
+ }
+
+ try {
+ log.trace("Initializing SSL Context ... ");
+ final SSLContext sslContext = SSLContext.getDefault();
+ HostnameVerifier hostnameVerifier = null;
+ if (basicConfig.getBasicConfigurationBoolean(
+ PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL,
+ false)) {
+ hostnameVerifier = new NoopHostnameVerifier();
+ log.warn("HTTP client-builder deactivates SSL Host-name verification!");
+ }
+
+ final LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext , hostnameVerifier);
+ httpClientBuilder.setSSLSocketFactory(sslSocketFactory );
+
+ } catch (final NoSuchAlgorithmException e) {
+ log.warn("HTTP client-builder can NOT initialze SSL-Context", e);
+
+ }
+
+
+ log.info("HTTP client-builder successfuly initialized");
+
+ }
+
+
+
+
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java
new file mode 100644
index 00000000..1975fb52
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java
@@ -0,0 +1,21 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+
+public interface IHttpClientFactory {
+
+ /**
+ * Return an instance of a Apache HTTP client that follows http redirects automatically
+ *
+ * @return
+ */
+ CloseableHttpClient getHttpClient();
+
+ /**
+ * Return an instance of a Apache HTTP client
+ * @param followRedirects
+ * @return
+ */
+ CloseableHttpClient getHttpClient(boolean followRedirects);
+
+} \ No newline at end of file
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java
new file mode 100644
index 00000000..e3d74066
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+
+/**
+ * Utility for creating and loading key stores.
+ *
+ * @author Paul Ivancsics
+ * @version $Id$
+ */
+public class KeyStoreUtils {
+
+ /**
+ * JAVA KeyStore
+ */
+ private static final String KEYSTORE_TYPE_JKS = "JKS";
+
+ /**
+ * PKCS12 KeyStore
+ */
+ private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12";
+
+
+
+ /**
+ * Loads a key store from file.
+ *
+ * @param keystoreType key store type
+ * @param urlString URL of key store
+ * @param password password protecting the key store
+ * @return key store loaded
+ * @throws IOException thrown while reading the key store from file
+ * @throws GeneralSecurityException thrown while creating the key store
+ */
+ public static KeyStore loadKeyStore(
+ String keystoreType,
+ String urlString,
+ String password)
+ throws IOException, GeneralSecurityException {
+
+ URL keystoreURL = new URL(urlString);
+ InputStream in = keystoreURL.openStream();
+ return loadKeyStore(keystoreType, in, password);
+ }
+ /**
+ * Loads a key store from an <code>InputStream</code>, and
+ * closes the <code>InputStream</code>.
+ *
+ * @param keystoreType key store type
+ * @param in input stream
+ * @param password password protecting the key store
+ * @return key store loaded
+ * @throws IOException thrown while reading the key store from the stream
+ * @throws GeneralSecurityException thrown while creating the key store
+ */
+ public static KeyStore loadKeyStore(
+ String keystoreType,
+ InputStream in,
+ String password)
+ throws IOException, GeneralSecurityException {
+
+ char[] chPassword = null;
+ if (password != null)
+ chPassword = password.toCharArray();
+ KeyStore ks = KeyStore.getInstance(keystoreType);
+ ks.load(in, chPassword);
+ in.close();
+ return ks;
+ }
+ /**
+ * Creates a key store from X509 certificate files, aliasing them with
+ * the index in the <code>String[]</code>, starting with <code>"0"</code>.
+ *
+ * @param keyStoreType key store type
+ * @param certFilenames certificate filenames
+ * @return key store created
+ * @throws IOException thrown while reading the certificates from file
+ * @throws GeneralSecurityException thrown while creating the key store
+ */
+ public static KeyStore createKeyStore(
+ String keyStoreType,
+ String[] certFilenames)
+ throws IOException, GeneralSecurityException {
+
+ KeyStore ks = KeyStore.getInstance(keyStoreType);
+ ks.load(null, null);
+ for (int i = 0; i < certFilenames.length; i++) {
+ Certificate cert = loadCertificate(certFilenames[i]);
+ ks.setCertificateEntry("" + i, cert);
+ }
+ return ks;
+ }
+
+ /**
+ * Loads an X509 certificate from file.
+ * @param certFilename filename
+ * @return the certificate loaded
+ * @throws IOException thrown while reading the certificate from file
+ * @throws GeneralSecurityException thrown while creating the certificate
+ */
+ private static Certificate loadCertificate(String certFilename)
+ throws IOException, GeneralSecurityException {
+
+ FileInputStream in = new FileInputStream(certFilename);
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ Certificate cert = certFactory.generateCertificate(in);
+ in.close();
+ return cert;
+ }
+
+
+ /**
+ * Loads a keyStore without knowing the keyStore type
+ * @param keyStorePath URL to the keyStore
+ * @param password Password protecting the keyStore
+ * @return keyStore loaded
+ * @throws KeyStoreException thrown if keyStore cannot be loaded
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ public static KeyStore loadKeyStore(String keyStorePath, String password) throws KeyStoreException, IOException{
+
+ //InputStream is = new FileInputStream(keyStorePath);
+ URL keystoreURL = new URL(keyStorePath);
+ InputStream in = keystoreURL.openStream();
+ InputStream isBuffered = new BufferedInputStream(in);
+ return loadKeyStore(isBuffered, password);
+
+ }
+
+ /**
+ * Loads a keyStore without knowing the keyStore type
+ * @param in input stream
+ * @param password Password protecting the keyStore
+ * @return keyStore loaded
+ * @throws KeyStoreException thrown if keyStore cannot be loaded
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+public static KeyStore loadKeyStore(InputStream is, String password) throws KeyStoreException, IOException{
+ is.mark(1024*1024);
+ KeyStore ks = null;
+ try {
+ try {
+ ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password);
+ } catch (IOException e2) {
+ is.reset();
+ ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password);
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+
+ }
+ return ks;
+
+ }
+
+
+
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java
new file mode 100644
index 00000000..e753f19f
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java
@@ -0,0 +1,375 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author tlenz
+ *
+ */
+public class KeyValueUtils {
+ private static final Logger log = LoggerFactory.getLogger(KeyValueUtils.class);
+
+ public static final String KEY_DELIMITER = ".";
+ public static final String CSV_DELIMITER = ",";
+ public static final String KEYVVALUEDELIMITER = "=";
+ public static final String DEFAULT_VALUE = "default";
+
+ /**
+ * Convert Java properties into a Map<String, String>
+ * <br><br>
+ * <b>Important:</b> The key/values from properties must be of type String!
+ *
+ * @param properties
+ * @return
+ */
+ public static Map<String, String> convertPropertiesToMap(Properties properties) {
+ return new HashMap<String, String>((Map) properties);
+
+ //INFO Java8 solution ;)
+// return properties.entrySet().stream().collect(
+// Collectors.toMap(
+// e -> e.getKey().toString(),
+// e -> e.getValue().toString()
+// )
+// );
+
+ }
+
+ /**
+ * Extract the first child of an input key after a the prefix
+ *
+ * @param key Full input key
+ * @param prefix Prefix
+ * @return Child key {String} if it exists or null
+ */
+ public static String getFirstChildAfterPrefix(String key, String prefix) {
+ final String idAfterPrefix = removePrefixFromKey(key, prefix);
+ if (idAfterPrefix != null) {
+ final int index = idAfterPrefix.indexOf(KEY_DELIMITER);
+ if (index > 0) {
+ final String adding = idAfterPrefix.substring(0, index);
+ if (!(adding.isEmpty())) {
+ return adding;
+
+ }
+ } else if (!(idAfterPrefix.isEmpty())) {
+ return idAfterPrefix;
+
+ }
+
+ }
+ return null;
+ }
+
+ /**
+ * Extract the prefix from an input key
+ *
+ * @param key Full input key
+ * @param suffix Suffix of this key
+ * @return Prefix {String} of the key or null if input key does not ends with postfix string
+ */
+ public static String getPrefixFromKey(String key, String suffix) {
+ if (key != null && suffix != null && key.endsWith(suffix)) {
+ final String idPreforeSuffix = key.substring(0, key.length()-suffix.length());
+ if (idPreforeSuffix.endsWith(KEY_DELIMITER))
+ return idPreforeSuffix.substring(0, idPreforeSuffix.length()-1);
+ else
+ return idPreforeSuffix;
+ }
+ return null;
+
+ }
+
+ /**
+ * Remove a prefix string from a key
+ *
+ * @param key Full input key
+ * @param prefix Prefix, which should be removed
+ * @return The suffix of the input key or null if the input does not starts with the prefix
+ */
+ public static String removePrefixFromKey(String key, String prefix) {
+ if (prefix == null)
+ prefix = new String();
+
+ if (key!=null && key.startsWith(prefix)) {
+ String afterPrefix = key.substring(prefix.length());
+ final int index = afterPrefix.indexOf(KEY_DELIMITER);
+
+ if (index == 0) {
+ afterPrefix = afterPrefix.substring(1);
+
+ }
+ return afterPrefix;
+
+ }
+ return null;
+ }
+
+ /**
+ * Remove a prefix string from all keys in {Map<String, String>} of key/value pairs
+ *
+ * @param keys Input data of key/value pairs
+ * @param prefix Prefix which should be removed
+ * @return {Map<String, String>} of key/value pairs without prefix in key, but never null
+ */
+ public static Map<String, String> removePrefixFromKeys(Map<String, String> keys, String prefix) {
+ final Map<String, String> result = new HashMap<String, String>();
+ final Iterator<Entry<String, String>> interator = keys.entrySet().iterator();
+ while(interator.hasNext()) {
+ final Entry<String, String> el = interator.next();
+ final String newKey = removePrefixFromKey(el.getKey(), prefix);
+ if (StringUtils.isNotEmpty(newKey)) {
+ result.put(newKey, el.getValue());
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Get a subset of key/value pairs which starts with a prefix string
+ * The Prefix is removed from the key
+ *
+ * @param keys Input data of key/value pairs
+ * @param prefix Prefix string
+ * @return {Map<String, String>} of key/value pairs without prefix in key, but never null
+ */
+ public static Map<String, String> getSubSetWithPrefix(Map<String, String> keys, String prefix) {
+ return removePrefixFromKeys(keys, prefix);
+ }
+
+
+ /**
+ * Add a prefix to key/value pairs to make the key absolute according to key namespace convention
+ *
+ * @param input Input key/value pairs which should be updated
+ * @param prefix Key prefix, which should be added if the key is not absolute
+ * @param absolutIdentifier Key identifier, which indicates an absolute key
+ * @return {Map<String, String>} of key/value pairs in which all keys are absolute but never null
+ */
+ public static Map<String, String> makeKeysAbsolut(Map<String, String> input, String prefix, String absolutIdentifier) {
+ final Map<String, String> result = new HashMap<String, String>();
+ final Iterator<Entry<String, String>> interator = input.entrySet().iterator();
+ while(interator.hasNext()) {
+ final Entry<String, String> el = interator.next();
+ if (!el.getKey().startsWith(absolutIdentifier)) {
+ //key is not absolute -> add prefix
+ result.put(prefix
+ + KEY_DELIMITER
+ + el.getKey(),
+ el.getValue());
+
+ } else {
+ //key is absolute
+ result.put(el.getKey(), el.getValue());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get the parent key string from an input key
+ *
+ * @param key input key
+ * @return parent key or the empty String if no parent exists
+ */
+ public static String getParentKey(String key) {
+ if (StringUtils.isNotEmpty(key)) {
+ final int index = key.lastIndexOf(KEY_DELIMITER);
+ if (index > 0) {
+ return key.substring(0, index);
+
+ }
+ }
+
+ return new String();
+ }
+
+ /**
+ * Find the highest free list counter
+ *
+ * @param input Array of list keys
+ * @param listPrefix {String} prefix of the list
+ * @return {int} highest free list counter
+ */
+ public static int findNextFreeListCounter(String[] input,
+ String listPrefix) {
+ final List<Integer> counters = new ArrayList<Integer>();
+ if (input == null || input.length == 0)
+ return 0;
+
+ else {
+ for (final String key : input) {
+ final String listIndex = getFirstChildAfterPrefix(key, listPrefix);
+ counters.add(Integer.parseInt(listIndex));
+
+ }
+ Collections.sort(counters);
+ return counters.get(counters.size()-1) + 1;
+ }
+ }
+
+ /**
+ * Find the highest free list counter
+ *
+ * @param keySet {Set<String>} of list keys
+ * @param listPrefix {String} prefix of the list
+ * @return {int} highest free list counter
+ */
+ public static int findNextFreeListCounter(Set<String> keySet,
+ String listPrefix) {
+ if (keySet.isEmpty())
+ return 0;
+
+ final String[] array = new String[keySet.size()];
+ keySet.toArray(array);
+ return findNextFreeListCounter(array, listPrefix);
+ }
+
+
+ /**
+ * Normalize a CSV encoded list of value of an key/value pair
+ *
+ * This method removes all whitespace at the begin or the
+ * end of CSV values and remove newLine signs at the end of value.
+ * The ',' is used as list delimiter
+ *
+ * @param value CSV encoded input data
+ * @return normalized CSV encoded data or null if {value} is null or empty
+ */
+ public static String normalizeCSVValueString(String value) {
+ String normalizedCodes = null;
+ if (StringUtils.isNotEmpty(value)) {
+ final String[] codes = value.split(CSV_DELIMITER);
+ for (final String el: codes) {
+ if (normalizedCodes == null)
+ normalizedCodes = StringUtils.chomp(el.trim());
+ else
+ normalizedCodes += "," + StringUtils.chomp(el.trim());
+
+ }
+ }
+ return normalizedCodes;
+ }
+
+
+ /**
+ * Check a String if it is a comma separated list of values
+ *
+ * This method uses the ',' as list delimiter.
+ *
+ * @param value CSV encoded input data
+ * @return true if the input data contains a ',' and has more then 1 list element, otherwise false
+ */
+ public static boolean isCSVValueString(String value) {
+ if (StringUtils.isNotEmpty(value)) {
+ final String[] codes = value.split(CSV_DELIMITER);
+ if (codes.length >= 2) {
+ if (StringUtils.isNotEmpty(codes[1].trim()))
+ return true;
+
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a CSV list to a List of CSV values
+ * <br><br>
+ * This method removes all whitespace at the begin or the
+ * end of CSV values and remove newLine signs at the end of value.
+ * The ',' is used as list delimiter
+ *
+ * @param csv CSV encoded input data
+ * @return List of CSV normalized values, but never null
+ */
+ @Nonnull
+ public static List<String> getListOfCSVValues(@Nullable String csv) {
+ final List<String> list = new ArrayList<String>();
+ if (StringUtils.isNotEmpty(csv)) {
+ final String[] values = csv.split(CSV_DELIMITER);
+ for (final String el: values)
+ list.add(el.trim());
+
+ }
+
+ return list;
+ }
+
+ /**
+ * Convert a List of String elements to a Map of Key/Value pairs
+ * <br>
+ * Every List element used as a key/value pair and the '=' sign represents the delimiter between key and value
+ *
+ * @param elements List of key/value elements
+ * @return Map of Key / Value pairs, but never null
+ */
+ public static Map<String, String> convertListToMap(List<String> elements) {
+ final Map<String, String> map = new HashMap<String, String>();
+ for (final String el : elements) {
+ if (el.contains(KEYVVALUEDELIMITER)) {
+ final String[] split = el.split(KEYVVALUEDELIMITER);
+ map.put(split[0], split[1]);
+
+ } else
+ log.debug("Key/Value Mapper: '" + el + "' contains NO '='. Ignore it.");
+
+ }
+
+ return map;
+ }
+
+ /**
+ * This method remove all newline delimiter (\n or \r\n) from input data
+ *
+ * @param value Input String
+ * @return Input String without newline characters
+ */
+ public static String removeAllNewlineFromString(String value) {
+ return value.replaceAll("(\\t|\\r?\\n)+", "");
+
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java
new file mode 100644
index 00000000..ec57b92a
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.util.ListIterator;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeFilter;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * A <code>NodeIterator</code> implementation based on a
+ * <code>ListIterator</code>.
+ *
+ * @see java.util.ListIterator
+ * @see org.w3c.dom.traversal.NodeIterator
+ *
+ */
+public class NodeIteratorAdapter implements NodeIterator {
+
+ /** The <code>ListIterator</code> to wrap. */
+ private ListIterator nodeIterator;
+
+ /**
+ * Create a new <code>NodeIteratorAdapter</code>.
+ * @param nodeIterator The <code>ListIterator</code> to iterate over.
+ */
+ public NodeIteratorAdapter(ListIterator nodeIterator) {
+ this.nodeIterator = nodeIterator;
+ }
+
+ /**
+ * @see org.w3c.dom.traversal.NodeIterator#getRoot()
+ */
+ public Node getRoot() {
+ return null;
+ }
+
+ /**
+ * @see org.w3c.dom.traversal.NodeIterator#getWhatToShow()
+ */
+ public int getWhatToShow() {
+ return NodeFilter.SHOW_ALL;
+ }
+
+ /**
+ * @see org.w3c.dom.traversal.NodeIterator#getFilter()
+ */
+ public NodeFilter getFilter() {
+ return null;
+ }
+
+ /**
+ * @see org.w3c.dom.traversal.NodeIterator#getExpandEntityReferences()
+ */
+ public boolean getExpandEntityReferences() {
+ return false;
+ }
+
+ /**
+ * @see org.w3c.dom.traversal.NodeIterator#nextNode()
+ */
+ public Node nextNode() throws DOMException {
+ if (nodeIterator.hasNext()) {
+ return (Node) nodeIterator.next();
+ }
+ return null;
+ }
+
+ /**
+ * @see org.w3c.dom.traversal.NodeIterator#previousNode()
+ */
+ public Node previousNode() throws DOMException {
+ if (nodeIterator.hasPrevious()) {
+ return (Node) nodeIterator.previous();
+ }
+ return null;
+ }
+
+ /**
+ * @see org.w3c.dom.traversal.NodeIterator#detach()
+ */
+ public void detach() {
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java
new file mode 100644
index 00000000..69045aaa
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.util.List;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A <code>NodeList</code> implementation based on a <code>List</code>.
+ *
+ * @see java.util.List
+ * @see org.w3c.dom.NodeList
+ */
+
+public class NodeListAdapter implements NodeList {
+ /** The <code>List</code> to wrap. */
+ private List nodeList;
+
+ /**
+ * Create a new <code>NodeListAdapter</code>.
+ *
+ * @param nodeList The <code>List</code> containing the nodes.
+ */
+ public NodeListAdapter(List nodeList) {
+ this.nodeList = nodeList;
+ }
+
+ /**
+ * @see org.w3c.dom.NodeList#item(int)
+ */
+ public Node item(int index) {
+ return (Node) nodeList.get(index);
+ }
+
+ /**
+ * @see org.w3c.dom.NodeList#getLength()
+ */
+ public int getLength() {
+ return nodeList.size();
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java
new file mode 100644
index 00000000..e236b3a9
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+
+import java.nio.ByteBuffer;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.lang3.ArrayUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.eaaf.core.impl.idp.process.support.SecureRandomHolder;
+
+
+/**
+ * Random number generator used to generate ID's
+ * @author Paul Ivancsics
+ * @version $Id$
+ */
+public class Random {
+ private static final Logger log = LoggerFactory.getLogger(Random.class);
+
+ private final static char[] allowedPreFix =
+ {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
+ private static final DateFormat dateFormater = new SimpleDateFormat("yyyyddMM");
+
+ /** random number generator used */
+ private static SecureRandom random;
+ //private static SeedGenerator seedgenerator;
+
+ static {
+ try {
+ random = SecureRandom.getInstance("SHA256PRNG-FIPS186");
+
+ } catch (NoSuchAlgorithmException e) {
+ log.warn("Can NOT initialize SecureRandom with: 'SHA256PRNG-FIPS186'. Use 'StrongSecureRandom' as backup");
+ random = SecureRandomHolder.getInstance();
+
+ }
+
+
+ //random = iaik.security.random.SHA256FIPS186Random.getDefault();
+ }
+
+ /**
+ * Generate a unique process reference-value [160bit], which always starts with a letter
+ * <br>
+ * This unique ID consists of single letter, a 64bit date String[yyyyddMM],
+ * and a 88bit random value.
+ *
+ * @return 160bit ID, which is hex encoded
+ */
+ public static String nextProcessReferenceValue() {
+ //pre-process all three parts of a unique reference value
+ String now = dateFormater.format(new Date()); //8 bytes = 64bit
+ byte[] randValue = nextByteRandom(11);
+ char preFix = allowedPreFix[Math.abs(random.nextInt() % allowedPreFix.length)];
+
+ //generate ID
+ String returnValue = preFix + new String(Hex.encodeHex(ArrayUtils.addAll(now.getBytes(), randValue))); // 20 bytes = 160 bits
+ if (returnValue.length() > 40)
+ return returnValue.substring(0, 40);
+ else
+ return returnValue;
+
+ }
+
+
+
+ /**
+ * Creates a new random number [256bit], and encode it as hex value.
+ *
+ * @return random hex encoded value [256bit]
+ */
+ public static String nextHexRandom32() {
+ return new String(Hex.encodeHex(nextByteRandom(32))); // 32 bytes = 256 bits
+
+ }
+
+ /**
+ * Creates a new random number [128bit], and encode it as hex value.
+ *
+ * @return random hex encoded value [128bit]
+ */
+ public static String nextHexRandom16() {
+ return new String(Hex.encodeHex(nextByteRandom(16))); // 16 bytes = 128 bits
+
+ }
+
+ /**
+ * Creates a new random number [64bit], to be used as an ID.
+ *
+ * @return random long as a String [64bit]
+ */
+ public static String nextLongRandom() {
+ return "".concat(String.valueOf(Math.abs(generateLongRandom(32)))); // 32 bytes = 256 bits
+
+ }
+
+ /**
+ * Creates a new random number, to be used as an ID.
+ *
+ * @return random long as a String [64bit]
+ */
+ @Deprecated
+ public static String nextRandom() {
+ long l = ByteBuffer.wrap(nextByteRandom(32)).getLong(); // 32 bytes = 256 bits
+ return "" + Math.abs(l);
+
+ }
+
+/**
+ * Creates a new random byte[]
+ *
+ * @param size Size of random number in byte
+ * @return
+ */
+public static byte[] nextBytes(int size) {
+ return nextByteRandom(size);
+
+}
+
+ public static void seedRandom() {
+ //TODO: implement reflection on IAIK Seed generator
+// seedgenerator = iaik.security.random.AutoSeedGenerator.getDefault();
+// if (seedgenerator.seedAvailable())
+// random.setSeed(seedgenerator.getSeed());
+
+ random.setSeed(System.nanoTime());
+ }
+
+ private static long generateLongRandom(int size) {
+ return ByteBuffer.wrap(nextByteRandom(size)).getLong();
+ }
+
+ /**
+ * Generate a new random number
+ *
+ * @param size Size of random number in byte
+ * @return
+ */
+ private static synchronized byte[] nextByteRandom(int size) {
+ byte[] b = new byte[size];
+ random.nextBytes(b);
+ return b;
+
+ }
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java
new file mode 100644
index 00000000..f0ef9b38
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java
@@ -0,0 +1,214 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Arrays;
+import java.util.Base64;
+
+import javax.annotation.PostConstruct;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DurationFieldType;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy;
+import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EAAFException;
+import at.gv.egiz.eaaf.core.exceptions.EAAFIllegalStateException;
+import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException;
+
+/**
+ * PendingRequestId generation strategy based on signed tokens that facilitates extended token validation
+ *
+ * @author tlenz
+ *
+ */
+public class SecurePendingRequestIdGenerationStrategy implements IPendingRequestIdGenerationStrategy {
+ private static final Logger log = LoggerFactory.getLogger(SecurePendingRequestIdGenerationStrategy.class);
+
+ @Autowired(required=true) IConfiguration baseConfig;
+
+ public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET = "core.pendingrequestid.digist.secret";
+ public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM = "core.pendingrequestid.digist.algorithm";
+ public static final String CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME = "core.pendingrequestid.maxlifetime";
+
+ public static final String DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM = "HmacSHA256";
+ public static final String DEFAULT_PENDINGREQUESTID_MAX_LIFETIME = "300";
+
+ private static final int ENCODED_TOKEN_PARTS = 3;
+ private static final String TOKEN_SEPARATOR = "|";
+ private static final DateTimeFormatter TOKEN_TEXTUAL_DATE_FORMAT =
+ DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS");
+
+ private int maxPendingRequestIdLifeTime = 300;
+ private final int maxPendingReqIdSize = 1024;
+ private String digistAlgorithm = null;
+ private SecretKey key = null;
+ private final byte[] salt = "notRequiredInThisScenario".getBytes();
+
+ @Override
+ public String generateExternalPendingRequestId() throws EAAFException {
+ try {
+ final String toSign = buildInternalToken(Random.nextLongRandom(), DateTime.now());
+ final StringBuilder externalPendingRequestId= new StringBuilder();
+ externalPendingRequestId.append(toSign);
+ externalPendingRequestId.append(TOKEN_SEPARATOR);
+ externalPendingRequestId.append(Base64.getEncoder().encodeToString(calculateHMAC(toSign)));
+ return Base64.getUrlEncoder().encodeToString(externalPendingRequestId.toString().getBytes("UTF-8"));
+
+ } catch (final UnsupportedEncodingException e) {
+ throw new EAAFException("internal.99", new Object[] {e.getMessage()}, e);
+
+ }
+
+ }
+
+ @Override
+ public String getPendingRequestIdWithOutChecks(String externalPendingReqId) throws PendingReqIdValidationException {
+ final String[] tokenElements = extractTokens(externalPendingReqId);
+ return tokenElements[1];
+
+ }
+
+ @Override
+ public String validateAndGetPendingRequestId(String externalPendingReqId) throws PendingReqIdValidationException {
+ try {
+ final String[] tokenElements = extractTokens(externalPendingReqId);
+ final String internalPendingReqId = tokenElements[1];
+ final DateTime timeStamp = TOKEN_TEXTUAL_DATE_FORMAT.parseDateTime(tokenElements[0]);
+
+ log.trace("Checking HMAC from externalPendingReqId ... ");
+ final byte[] tokenDigest = Base64.getDecoder().decode(tokenElements[2]);
+ final byte[] refDigist = calculateHMAC(buildInternalToken(internalPendingReqId, timeStamp));
+ if (!Arrays.equals(tokenDigest, refDigist)) {
+ log.warn("Digest of Token does NOT match");
+ log.debug("Token: {} | Ref: {}", tokenDigest, refDigist);
+ throw new PendingReqIdValidationException(null, "Digest of pendingRequestId does NOT match");
+
+ }
+ log.debug("PendingRequestId HMAC digest check successful");
+
+ log.trace("Checking valid period ... ");
+ final DateTime now = DateTime.now();
+ if (timeStamp.withFieldAdded(
+ DurationFieldType.seconds(), maxPendingRequestIdLifeTime).isBefore(now)) {
+ log.warn("Token exceeds the valid period");
+ log.debug("Token: {} | Now: {}", timeStamp, now );
+ throw new PendingReqIdValidationException(internalPendingReqId, "PendingRequestId exceeds the valid period");
+
+ }
+ log.debug("Token valid-period check successful");
+
+ return internalPendingReqId;
+
+
+ } catch (final IllegalArgumentException | EAAFIllegalStateException e) {
+ log.warn("Token is NOT a valid String. Msg: {}", e.getMessage());
+ log.debug("TokenValue: {}", externalPendingReqId);
+ throw new PendingReqIdValidationException(null, "PendingReqId is NOT a valid String", e);
+
+ }
+ }
+
+ @NonNull
+ private String[] extractTokens(@Nullable String externalPendingReqId) throws PendingReqIdValidationException {
+ if (StringUtils.isEmpty(externalPendingReqId)) {
+ log.info("PendingReqId is 'null' or empty");
+ throw new PendingReqIdValidationException(null, "PendingReqId is 'null' or empty");
+
+ }
+
+ log.trace("RAW external pendingReqId: {}", externalPendingReqId);
+ final byte[] externalPendingReqIdBytes = Base64.getUrlDecoder().decode(externalPendingReqId);
+
+ if (externalPendingReqIdBytes.length > maxPendingReqIdSize) {
+ log.warn("pendingReqId size exceeds {}", maxPendingReqIdSize);
+ throw new PendingReqIdValidationException(null, "pendingReqId exceeds max.size: " + maxPendingReqIdSize);
+
+ }
+
+ final String stringToken = new String(externalPendingReqIdBytes);
+ if (StringUtils.countMatches(stringToken, TOKEN_SEPARATOR) == ENCODED_TOKEN_PARTS - 1) {
+ final String[] tokenElements = StringUtils.split(stringToken,
+ TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS);
+ return tokenElements;
+
+ } else {
+ log.warn("PendingRequestId has an unvalid format");
+ log.debug("PendingRequestId: {}", stringToken);
+ throw new PendingReqIdValidationException(null, "PendingReqId has an unvalid format");
+
+ }
+
+ }
+
+
+ @PostConstruct
+ private void initialize() throws EAAFConfigurationException {
+ log.debug("Initializing " + this.getClass().getName() + " ... ");
+
+ final String pendingReqIdDigistSecret = baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET);
+ if (StringUtils.isEmpty(pendingReqIdDigistSecret))
+ throw new EAAFConfigurationException("config.08", new Object[] {CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET});
+
+ digistAlgorithm = baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM, DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM);
+
+ maxPendingRequestIdLifeTime = Integer.valueOf(
+ baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME, DEFAULT_PENDINGREQUESTID_MAX_LIFETIME));
+
+ try {
+ final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA256");
+ final KeySpec spec = new PBEKeySpec(pendingReqIdDigistSecret.toCharArray(), salt, 10000, 128);
+ key = keyFactory.generateSecret(spec);
+
+
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ log.error("Can NOT initialize TokenService with configuration object", e);
+ throw new EAAFConfigurationException("config.09",
+ new Object[] { CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET,
+ "Can NOT generate HMAC key"},
+ e);
+
+ }
+
+ log.info(this.getClass().getName() + " initialized with digistAlg: {} and maxLifeTime: {}", digistAlgorithm, maxPendingRequestIdLifeTime);
+
+ }
+
+ private String buildInternalToken(String internalPendingReqId, DateTime now) {
+ return new StringBuilder()
+ .append(TOKEN_TEXTUAL_DATE_FORMAT.print(now))
+ .append(TOKEN_SEPARATOR)
+ .append(internalPendingReqId).toString();
+ }
+
+ private byte[] calculateHMAC(String toSign) throws EAAFIllegalStateException {
+ try {
+ final Mac mac = Mac.getInstance(digistAlgorithm);
+ mac.init(key);
+ return mac.doFinal(toSign.getBytes("UTF-8"));
+
+ } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) {
+ log.error("Can NOT generate secure pendingRequestId", e);
+ throw new EAAFIllegalStateException(new Object[] {"Can NOT caluclate digist for secure pendingRequestId"}, e);
+
+ }
+
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java
new file mode 100644
index 00000000..38e873e2
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class ServletUtils {
+
+
+ public static String getBaseUrl( HttpServletRequest request ) {
+ if ( ( request.getServerPort() == 80 ) ||
+ ( request.getServerPort() == 443 ) )
+ return request.getScheme() + "://" +
+ request.getServerName() +
+ request.getContextPath();
+ else
+ return request.getScheme() + "://" +
+ request.getServerName() + ":" + request.getServerPort() +
+ request.getContextPath();
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java
new file mode 100644
index 00000000..6b8fe9b7
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java
@@ -0,0 +1,38 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy;
+import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException;
+
+/**
+ * Simple pendingRequestId generation strategy that facilitates no extended validation
+ *
+ * @author tlenz
+ *
+ */
+public class SimplePendingRequestIdGenerationStrategy implements IPendingRequestIdGenerationStrategy {
+
+ @Override
+ public String generateExternalPendingRequestId() {
+ return Random.nextLongRandom();
+
+ }
+
+ @Override
+ public String validateAndGetPendingRequestId(String pendingReqId) throws PendingReqIdValidationException {
+ return getPendingRequestIdWithOutChecks(pendingReqId);
+
+ }
+
+ @Override
+ public String getPendingRequestIdWithOutChecks(String externalPendingReqId) throws PendingReqIdValidationException {
+ if (StringUtils.isEmpty(externalPendingReqId))
+ throw new PendingReqIdValidationException(externalPendingReqId, "PendingRequestId is empty or null");
+
+
+
+ return externalPendingReqId;
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java
new file mode 100644
index 00000000..530da777
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+
+
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+/**
+ * Utility methods for streams.
+ *
+ * @author Patrick Peck
+ * @version $Id$
+ */
+public class StreamUtils {
+
+ /**
+ * Compare the contents of two <code>InputStream</code>s.
+ *
+ * @param is1 The 1st <code>InputStream</code> to compare.
+ * @param is2 The 2nd <code>InputStream</code> to compare.
+ * @return boolean <code>true</code>, if both streams contain the exactly the
+ * same content, <code>false</code> otherwise.
+ * @throws IOException An error occurred reading one of the streams.
+ */
+ public static boolean compareStreams(InputStream is1, InputStream is2)
+ throws IOException {
+
+ byte[] buf1 = new byte[256];
+ byte[] buf2 = new byte[256];
+ int length1;
+ int length2;
+
+ try {
+ while (true) {
+ length1 = is1.read(buf1);
+ length2 = is2.read(buf2);
+
+ if (length1 != length2) {
+ return false;
+ }
+ if (length1 <= 0) {
+ return true;
+ }
+ if (!compareBytes(buf1, buf2, length1)) {
+ return false;
+ }
+ }
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ // close both streams
+ try {
+ is1.close();
+ is2.close();
+ } catch (IOException e) {
+ // ignore this
+ }
+ }
+ }
+
+ /**
+ * Compare two byte arrays, up to a given maximum length.
+ *
+ * @param b1 1st byte array to compare.
+ * @param b2 2nd byte array to compare.
+ * @param length The maximum number of bytes to compare.
+ * @return <code>true</code>, if the byte arrays are equal, <code>false</code>
+ * otherwise.
+ */
+ private static boolean compareBytes(byte[] b1, byte[] b2, int length) {
+ if (b1.length != b2.length) {
+ return false;
+ }
+
+ for (int i = 0; i < b1.length && i < length; i++) {
+ if (b1[i] != b2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Reads a byte array from a stream.
+ * @param in The <code>InputStream</code> to read.
+ * @return The bytes contained in the given <code>InputStream</code>.
+ * @throws IOException on any exception thrown
+ */
+ public static byte[] readStream(InputStream in) throws IOException {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ copyStream(in, out, null);
+
+ /*
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int b;
+ while ((b = in.read()) >= 0)
+ out.write(b);
+
+ */
+ in.close();
+ return out.toByteArray();
+ }
+
+ /**
+ * Reads a <code>String</code> from a stream, using given encoding.
+ * @param in The <code>InputStream</code> to read.
+ * @param encoding The character encoding to use for converting the bytes
+ * of the <code>InputStream</code> into a <code>String</code>.
+ * @return The content of the given <code>InputStream</code> converted into
+ * a <code>String</code>.
+ * @throws IOException on any exception thrown
+ */
+ public static String readStream(InputStream in, String encoding) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ copyStream(in, out, null);
+
+ /*
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int b;
+ while ((b = in.read()) >= 0)
+ out.write(b);
+ */
+ in.close();
+ return out.toString(encoding);
+ }
+
+ /**
+ * 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) throws IOException {
+ if (source == null) {
+ throw new NullPointerException("Argument \"source\" must not be null.");
+ }
+ if (buffer == null) {
+ buffer = new byte[8192];
+ }
+
+ if (destination != null) {
+ int bytesRead;
+ while ((bytesRead = source.read(buffer)) >= 0) {
+ destination.write(buffer, 0, bytesRead);
+ }
+ } else {
+ while (source.read(buffer) >= 0);
+ }
+ }
+
+ /**
+ * Gets the stack trace of the <code>Throwable</code> passed in as a string.
+ * @param t The <code>Throwable</code>.
+ * @return a String representing the stack trace of the <code>Throwable</code>.
+ */
+ public static String getStackTraceAsString(Throwable t)
+ {
+ ByteArrayOutputStream stackTraceBIS = new ByteArrayOutputStream();
+ t.printStackTrace(new PrintStream(stackTraceBIS));
+ return new String(stackTraceBIS.toByteArray());
+ }
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java
new file mode 100644
index 00000000..2e016848
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright 2017 Graz University of Technology
+ * EAAF-Core Components has been developed in a cooperation between EGIZ,
+ * A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+/*******************************************************************************
+ *******************************************************************************/
+package at.gv.egiz.eaaf.core.impl.utils;
+
+
+import at.gv.egiz.eaaf.core.api.IRequest;
+
+/**
+ * @author tlenz
+ *
+ */
+public class TransactionIDUtils {
+
+ //MDC variables for logging
+ public static final String MDC_TRANSACTION_ID = "transactionId";
+ public static final String MDC_SESSION_ID = "sessionId";
+ public static final String MDC_SERVICEPROVIDER_ID = "oaId";
+
+ /**
+ * Set all MDC variables from pending request to this threat context<br>
+ * These includes SessionID, TransactionID, and unique service-provider identifier
+ *
+ * @param pendingRequest
+ */
+ public static void setAllLoggingVariables(IRequest pendingRequest) {
+ setTransactionId(pendingRequest.getUniqueTransactionIdentifier());
+ setSessionId(pendingRequest.getUniqueSessionIdentifier());
+ setServiceProviderId(pendingRequest.getServiceProviderConfiguration().getUniqueIdentifier());
+
+ }
+
+ /**
+ * Remove all MDC variables from this threat context
+ *
+ */
+ public static void removeAllLoggingVariables() {
+ removeSessionId();
+ removeTransactionId();
+ removeServiceProviderId();
+
+ }
+
+
+ public static void setServiceProviderId(String oaUniqueId) {
+ org.slf4j.MDC.put(MDC_SERVICEPROVIDER_ID, oaUniqueId);
+
+ }
+
+ public static void removeServiceProviderId() {
+ org.slf4j.MDC.remove(MDC_SERVICEPROVIDER_ID);
+
+ }
+
+ public static void setTransactionId(String pendingRequestID) {
+ org.slf4j.MDC.put(MDC_TRANSACTION_ID,
+ "TID-" + pendingRequestID);
+
+ }
+
+ public static void removeTransactionId() {
+ org.slf4j.MDC.remove(MDC_TRANSACTION_ID);
+
+ }
+
+ public static void setSessionId(String uniqueSessionId) {
+ org.slf4j.MDC.put(MDC_SESSION_ID,
+ "SID-" + uniqueSessionId);
+
+ }
+
+ public static void removeSessionId() {
+ org.slf4j.MDC.remove(MDC_SESSION_ID);
+
+ }
+
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java
new file mode 100644
index 00000000..b3fb42c4
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java
@@ -0,0 +1,62 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import javax.security.auth.x500.X500Principal;
+
+public class X509Utils {
+
+ /**
+ * Sorts the Certificate Chain by IssuerDN and SubjectDN. The [0]-Element should be the Hostname,
+ * the last Element should be the Root Certificate.
+ *
+ * @param certs
+ * The first element must be the correct one.
+ * @return sorted Certificate Chain
+ */
+ public static List<X509Certificate> sortCertificates(
+ List<X509Certificate> certs)
+ {
+ int length = certs.size();
+ if (certs.size() <= 1)
+ {
+ return certs;
+ }
+
+ for (X509Certificate cert : certs)
+ {
+ if (cert == null)
+ {
+ throw new NullPointerException();
+ }
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ boolean found = false;
+ X500Principal issuer = certs.get(i).getIssuerX500Principal();
+ for (int j = i + 1; j < length; j++)
+ {
+ X500Principal subject = certs.get(j).getSubjectX500Principal();
+ if (issuer.equals(subject))
+ {
+ // sorting necessary?
+ if (i + 1 != j)
+ {
+ X509Certificate tmp = certs.get(i + 1);
+ certs.set(i + 1, certs.get(j));
+ certs.set(j, tmp);
+ }
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ break;
+ }
+ }
+
+ return certs;
+ }
+}