From 8883fd1c0efa08fe0a73a11a1c8986fa3ea72753 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Fri, 13 Sep 2019 09:02:58 +0200 Subject: refactor utils into a separate module --- .../idp/process/support/SecureRandomHolder.java | 61 ++++ .../gv/egiz/eaaf/core/impl/utils/ArrayUtils.java | 44 +++ .../egiz/eaaf/core/impl/utils/DataURLBuilder.java | 113 +++++++ .../at/gv/egiz/eaaf/core/impl/utils/FileUtils.java | 175 ++++++++++ .../at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java | 178 ++++++++++ .../eaaf/core/impl/utils/HttpClientFactory.java | 152 +++++++++ .../eaaf/core/impl/utils/IHttpClientFactory.java | 21 ++ .../egiz/eaaf/core/impl/utils/KeyStoreUtils.java | 199 +++++++++++ .../egiz/eaaf/core/impl/utils/KeyValueUtils.java | 375 +++++++++++++++++++++ .../eaaf/core/impl/utils/NodeIteratorAdapter.java | 113 +++++++ .../egiz/eaaf/core/impl/utils/NodeListAdapter.java | 70 ++++ .../at/gv/egiz/eaaf/core/impl/utils/Random.java | 176 ++++++++++ .../SecurePendingRequestIdGenerationStrategy.java | 214 ++++++++++++ .../gv/egiz/eaaf/core/impl/utils/ServletUtils.java | 43 +++ .../SimplePendingRequestIdGenerationStrategy.java | 38 +++ .../gv/egiz/eaaf/core/impl/utils/StreamUtils.java | 201 +++++++++++ .../eaaf/core/impl/utils/TransactionIDUtils.java | 101 ++++++ .../at/gv/egiz/eaaf/core/impl/utils/X509Utils.java | 62 ++++ 18 files changed, 2336 insertions(+) create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java (limited to 'eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl') 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 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 VerifyIdentityLink or VerifyAuthenticationBlock, + * including the MOASessionID as a parameter. + * + * @param authBaseURL base URL (context path) of the MOA ID Authentication component, + * including a trailing '/' + * @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 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 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 InputStream, and + * closes the InputStream. + * + * @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 String[], starting with "0". + * + * @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 + *

+ * Important: The key/values from properties must be of type String! + * + * @param properties + * @return + */ + public static Map convertPropertiesToMap(Properties properties) { + return new HashMap((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} of key/value pairs + * + * @param keys Input data of key/value pairs + * @param prefix Prefix which should be removed + * @return {Map} of key/value pairs without prefix in key, but never null + */ + public static Map removePrefixFromKeys(Map keys, String prefix) { + final Map result = new HashMap(); + final Iterator> interator = keys.entrySet().iterator(); + while(interator.hasNext()) { + final Entry 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} of key/value pairs without prefix in key, but never null + */ + public static Map getSubSetWithPrefix(Map 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} of key/value pairs in which all keys are absolute but never null + */ + public static Map makeKeysAbsolut(Map input, String prefix, String absolutIdentifier) { + final Map result = new HashMap(); + final Iterator> interator = input.entrySet().iterator(); + while(interator.hasNext()) { + final Entry 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 counters = new ArrayList(); + 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} of list keys + * @param listPrefix {String} prefix of the list + * @return {int} highest free list counter + */ + public static int findNextFreeListCounter(Set 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 + *

+ * 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 getListOfCSVValues(@Nullable String csv) { + final List list = new ArrayList(); + 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 + *
+ * 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 convertListToMap(List elements) { + final Map map = new HashMap(); + 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 NodeIterator implementation based on a + * ListIterator. + * + * @see java.util.ListIterator + * @see org.w3c.dom.traversal.NodeIterator + * + */ +public class NodeIteratorAdapter implements NodeIterator { + + /** The ListIterator to wrap. */ + private ListIterator nodeIterator; + + /** + * Create a new NodeIteratorAdapter. + * @param nodeIterator The ListIterator 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 NodeList implementation based on a List. + * + * @see java.util.List + * @see org.w3c.dom.NodeList + */ + +public class NodeListAdapter implements NodeList { + /** The List to wrap. */ + private List nodeList; + + /** + * Create a new NodeListAdapter. + * + * @param nodeList The List 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 + *
+ * 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 InputStreams. + * + * @param is1 The 1st InputStream to compare. + * @param is2 The 2nd InputStream to compare. + * @return boolean true, if both streams contain the exactly the + * same content, false 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 true, if the byte arrays are equal, false + * 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 InputStream to read. + * @return The bytes contained in the given InputStream. + * @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 String from a stream, using given encoding. + * @param in The InputStream to read. + * @param encoding The character encoding to use for converting the bytes + * of the InputStream into a String. + * @return The content of the given InputStream converted into + * a String. + * @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 Throwable passed in as a string. + * @param t The Throwable. + * @return a String representing the stack trace of the Throwable. + */ + 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
+ * 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 sortCertificates( + List 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; + } +} -- cgit v1.2.3