diff options
Diffstat (limited to 'eaaf_core_utils')
50 files changed, 4936 insertions, 344 deletions
diff --git a/eaaf_core_utils/checks/spotbugs-exclude.xml b/eaaf_core_utils/checks/spotbugs-exclude.xml new file mode 100644 index 00000000..c1271f91 --- /dev/null +++ b/eaaf_core_utils/checks/spotbugs-exclude.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FindBugsFilter> + <Match> + <!-- bPK requires SHA1 from specification --> + <Class name="at.gv.egiz.eaaf.core.impl.builder.BpkBuilder" /> + <OR> + <Bug pattern="WEAK_MESSAGE_DIGEST_SHA1" /> + </OR> + </Match> + <Match> + <Class name="at.gv.egiz.eaaf.core.impl.utils.EaafSerializationUtils" /> + <OR> + <Bug pattern="OBJECT_DESERIALIZATION" /> + </OR> + </Match> + <Match> + <!-- Paths and URLs only loaded from configuration --> + <Class name="at.gv.egiz.eaaf.core.impl.utils.FileUtils" /> + <OR> + <Bug pattern="URLCONNECTION_SSRF_FD" /> + <Bug pattern="PATH_TRAVERSAL_IN" /> + </OR> + </Match> + <Match> + <!-- Paths and URLs only loaded from configuration --> + <Class name="at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils" /> + <OR> + <Bug pattern="URLCONNECTION_SSRF_FD" /> + <Bug pattern="PATH_TRAVERSAL_IN" /> + </OR> + </Match> +</FindBugsFilter>
\ No newline at end of file diff --git a/eaaf_core_utils/pom.xml b/eaaf_core_utils/pom.xml index ca735940..6ca82d9c 100644 --- a/eaaf_core_utils/pom.xml +++ b/eaaf_core_utils/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>at.gv.egiz</groupId> <artifactId>eaaf</artifactId> - <version>1.1.3-SNAPSHOT</version> + <version>1.2.1-SNAPSHOT</version> </parent> <groupId>at.gv.egiz.eaaf</groupId> <artifactId>eaaf_core_utils</artifactId> @@ -39,22 +39,23 @@ <dependency> <groupId>at.gv.egiz.eaaf</groupId> <artifactId>eaaf_core_api</artifactId> - </dependency> + </dependency> <dependency> <groupId>at.asitplus.hsmfacade</groupId> <artifactId>provider</artifactId> - </dependency> + <scope>provided</scope> + </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-core</artifactId> - </dependency> - + <scope>provided</scope> + </dependency> <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-webmvc</artifactId> + <groupId>org.bouncycastle</groupId> + <artifactId>bctls-jdk15to18</artifactId> </dependency> - + <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> @@ -80,7 +81,11 @@ <artifactId>joda-time</artifactId> </dependency> - + <dependency> + <groupId>org.bitbucket.b_c</groupId> + <artifactId>jose4j</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> @@ -93,11 +98,6 @@ </dependency> <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> @@ -111,13 +111,44 @@ <groupId>com.squareup.okhttp3</groupId> <artifactId>mockwebserver</artifactId> <scope>test</scope> + <exclusions> + <exclusion> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp-tls</artifactId> <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.bouncycastle</groupId> + <artifactId>bctls-jdk15on</artifactId> + </exclusion> + <exclusion> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpkix-jdk15on</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.2.3</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.vintage</groupId> + <artifactId>junit-vintage-engine</artifactId> + <version>${junit-jupiter-api.version}</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-migrationsupport</artifactId> + <version>${junit-jupiter-api.version}</version> </dependency> - </dependencies> <build> @@ -131,49 +162,13 @@ <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> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-maven-plugin</artifactId> + <version>${spotbugs-maven-plugin.version}</version> <configuration> - <threadCount>1</threadCount> + <failOnError>true</failOnError> + <excludeFilterFile>checks/spotbugs-exclude.xml</excludeFilterFile> </configuration> - <dependencies> - <dependency> - <groupId>org.apache.maven.surefire</groupId> - <artifactId>surefire-junit47</artifactId> - <version>${surefire.version}</version> - </dependency> - </dependencies> </plugin> </plugins> diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/exception/EaafKeyUsageException.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/exception/EaafKeyUsageException.java new file mode 100644 index 00000000..8b4e68a4 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/exception/EaafKeyUsageException.java @@ -0,0 +1,21 @@ +package at.gv.egiz.eaaf.core.exception; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; + +public class EaafKeyUsageException extends EaafException { + + private static final long serialVersionUID = -2641273589744430903L; + + public static final String ERROR_CODE_01 = "internal.key.01"; + + public EaafKeyUsageException(String errorCode, String... params) { + super(errorCode, new Object[] {params}); + + } + + public EaafKeyUsageException(String errorCode, Throwable e, String... params) { + super(errorCode, new Object[] {params}, e); + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/builder/BpkBuilder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/builder/BpkBuilder.java new file mode 100644 index 00000000..903aa300 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/builder/BpkBuilder.java @@ -0,0 +1,446 @@ +/* + * 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. +*/ + +package at.gv.egiz.eaaf.core.impl.builder; + +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map.Entry; + +import javax.annotation.Nonnull; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.Base64Utils; + +import at.gv.egiz.eaaf.core.api.data.EaafConstants; +import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import lombok.extern.slf4j.Slf4j; + + +/** + * Builder for the bPK, as defined in + * <code>"Ableitung f¨r die bereichsspezifische Personenkennzeichnung"</code> + * version <code>1.0.1</code> from + * <code>"reference.e-government.gv.at"</code>. + * + */ +@Slf4j +public class BpkBuilder { + + private static final String ERROR_CODE_33 = "builder.33"; + + private static final String ERROR_MSG_WRONG_TARGET_FORMAT = "bPK-target format must be full URI"; + + + /** + * Calculates an area specific unique person-identifier from a baseID. + * + * @param baseID baseId from user but never null + * @param targetIdentifier target identifier for area specific identifier + * calculation but never null + * @return Pair consists of (unique person identifier for this target, + * targetArea) but never null + * @throws EaafBuilderException if some input data are not valid + */ + public static Pair<String, String> generateAreaSpecificPersonIdentifier(final String baseID, + final String targetIdentifier) throws EaafBuilderException { + return generateAreaSpecificPersonIdentifier(baseID, EaafConstants.URN_PREFIX_BASEID, + targetIdentifier); + + } + + /** + * Calculates an area specific unique person-identifier from an unique + * identifier with a specific type. + * + * @param baseID baseId from user but never null + * @param baseIdType Type of the baseID but never null + * @param targetIdentifier target identifier for area specific identifier + * calculation but never null + * @return Pair consists of (unique person identifier for this target, + * targetArea) but never null + * @throws EaafBuilderException if some input data are not valid + */ + public static Pair<String, String> generateAreaSpecificPersonIdentifier(final String baseID, + final String baseIdType, final String targetIdentifier) throws EaafBuilderException { + if (StringUtils.isEmpty(baseID)) { + throw new EaafBuilderException(ERROR_CODE_33, new Object[] { "baseID is empty or null" }, + "BaseId is empty or null"); + } + + if (StringUtils.isEmpty(baseIdType)) { + throw new EaafBuilderException(ERROR_CODE_33, + new Object[] { "the type of baseID is empty or null" }, "Type of baseId is empty or null"); + } + + if (StringUtils.isEmpty(targetIdentifier)) { + throw new EaafBuilderException(ERROR_CODE_33, + new Object[] { "SP specific target identifier is empty or null" }, + "SP specific target identifier is empty or null"); + } + + if (baseIdType.startsWith(EaafConstants.URN_PREFIX_BASEID)) { + log.trace("Find baseID. Starting unique identifier caluclation for this target"); + + if (targetIdentifier.startsWith(EaafConstants.URN_PREFIX_CDID)) { + log.trace("Calculate bPK identifier for target: " + targetIdentifier); + return Pair.newInstance(calculatebPKwbPK(baseID + "+" + targetIdentifier), + targetIdentifier); + + } else if (targetIdentifier.startsWith(EaafConstants.URN_PREFIX_WBPK)) { + log.trace("Calculate wbPK identifier for target: " + targetIdentifier); + String commonBpkTarget = normalizeBpkTargetIdentifierToCommonFormat(targetIdentifier); + return Pair.newInstance(calculatebPKwbPK( + baseID + "+" + normalizeBpkTargetIdentifierToBpkCalculationFormat(commonBpkTarget)), + commonBpkTarget); + + } else if (targetIdentifier.startsWith(EaafConstants.URN_PREFIX_EIDAS)) { + log.trace("Calculate eIDAS identifier for target: " + targetIdentifier); + final String[] splittedTarget = targetIdentifier.split("\\+"); + final String cititzenCountryCode = splittedTarget[1]; + final String eidasOutboundCountry = splittedTarget[2]; + + if (cititzenCountryCode.equalsIgnoreCase(eidasOutboundCountry)) { + log.warn("Suspect configuration FOUND!!! CitizenCountry equals DestinationCountry"); + + } + return buildEidasIdentifer(baseID, baseIdType, cititzenCountryCode, eidasOutboundCountry); + + } else { + throw new EaafBuilderException(ERROR_CODE_33, + new Object[] { "Target identifier: " + targetIdentifier + " is NOT allowed or unknown" }, + "Target identifier: " + targetIdentifier + " is NOT allowed or unknown"); + } + + } else { + log.trace("BaseID is not of type " + EaafConstants.URN_PREFIX_BASEID + + ". Check type against requested target ..."); + if (baseIdType.equals(targetIdentifier)) { + log.debug("Unique identifier is already area specific. Is nothing todo"); + return Pair.newInstance(baseID, targetIdentifier); + + } else { + log.warn("Get unique identifier for target: " + baseIdType + " but target: " + + targetIdentifier + " is required!"); + throw new EaafBuilderException(ERROR_CODE_33, + new Object[] { "Get unique identifier for target: " + baseIdType + " but target: " + + targetIdentifier + " is required" }, + "Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + + " is required"); + + } + } + } + + + + /** + * Create an encrypted bPK. + * + * @param bpk unencrypted bPK + * @param target bPK target in full form + * @param publicKey Public-Key used for encryption + * @return encrypted bPK + * @throws EaafBuilderException In case of an error + */ + public static String encryptBpk(final String bpk, String target, final PublicKey publicKey) + throws EaafBuilderException { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + if (!target.startsWith(EaafConstants.URN_PREFIX_WITH_COLON)) { + throw new EaafBuilderException("builder.32", + null, ERROR_MSG_WRONG_TARGET_FORMAT); + + } + + target = normalizeBpkTargetIdentifierToBpkCalculationFormat( + normalizeBpkTargetIdentifierToCommonFormat(target)); + + final String input = + "V1::" + target + "::" + bpk + "::" + sdf.format(new Date()); + // System.out.println(input); + byte[] result; + try { + final byte[] inputBytes = input.getBytes("ISO-8859-1"); + result = encrypt(inputBytes, publicKey); + return new String(Base64Utils.encode(result), "ISO-8859-1").replaceAll("\r\n", ""); + // return new String(Base64Utils.encode(result, + // "ISO-8859-1")).replaceAll("\r\n", ""); + + } catch (final Exception e) { + throw new EaafBuilderException("bPK encryption FAILED", null, e.getMessage(), e); + + } + } + + /** + * Decrypt an encrypted bPK. + * + * @param encryptedBpk encrypted bPK + * @param target bPK target in full form + * @param privateKey private-key for decryption + * @return bPK Pair consists of (unique person identifier for this target, + * targetArea) but never null + * @throws EaafBuilderException In case of an error + */ + public static Pair<String, String> decryptBpk(final String encryptedBpk, String target, + final PrivateKey privateKey) throws EaafBuilderException { + String decryptedString; + + if (!target.startsWith(EaafConstants.URN_PREFIX_WITH_COLON)) { + throw new EaafBuilderException("builder.32", + null, ERROR_MSG_WRONG_TARGET_FORMAT); + + } + + try { + final byte[] encryptedBytes = Base64Utils.decode(encryptedBpk.getBytes("ISO-8859-1")); + final byte[] decryptedBytes = decrypt(encryptedBytes, privateKey); + decryptedString = new String(decryptedBytes, "ISO-8859-1"); + + } catch (final Exception e) { + throw new EaafBuilderException("bPK decryption FAILED", null, e.getMessage(), e); + + } + + String[] parts = decryptedString.split("::"); + if (parts.length != 4) { + log.trace("Encrypted bPK has value: {}", decryptedString); + throw new EaafBuilderException("builder.31", new Object[] {parts.length}, + "encBpk has a suspect format"); + + } + + final String sector = parts[1]; + final String bPK = parts[2]; + + if (target.equals(normalizeBpkTargetIdentifierToCommonFormat(sector))) { + return Pair.newInstance(bPK, target); + + } else { + throw new EaafBuilderException("builder.30", new Object[] {sector, target}, + "Decrypted bPK-target does not match"); + + } + } + + /** + * Normalize wbPK target identifier for FN, ZVR, and ERSB to XFN, XZVR, and XERSB. + * + * <p>If the target is not of this types the target will be returned as it is</p> + * @param targetIdentifier bPK input target + * @return XFN, XZVR, XERSB, or targetIdentfier if no normalization is required + */ + @Nullable + public static String normalizeBpkTargetIdentifierToCommonFormat(@Nullable String targetIdentifier) { + if (targetIdentifier != null + && !targetIdentifier.startsWith(EaafConstants.URN_PREFIX_WBPK_TARGET_WITH_X)) { + for (Entry<String, String> mapper : EaafConstants.URN_WBPK_TARGET_X_TO_NONE_MAPPER.entrySet()) { + if (targetIdentifier.startsWith(mapper.getValue())) { + String wbpkTarget = mapper.getKey() + targetIdentifier.substring(mapper.getValue().length()); + log.trace("Normalize wbPK target: {} to {}", targetIdentifier, wbpkTarget); + return wbpkTarget; + + } + } + } + + return targetIdentifier; + } + + /** + * Normalize wbPK target identifier for XFN, XZVR, and XERSB to bPK non-X format like, FN, ZVR, and ERSB. + * + * <p>If the target is not of this types the target will be returned as it is</p> + * + * @param targetIdentifier bPK input target + * @return FN, ZVR, ERSB, or targetIdentfier if no normalization is required + */ + @Nullable + public static String normalizeBpkTargetIdentifierToNonXFormat(@Nullable String targetIdentifier) { + if (targetIdentifier != null && targetIdentifier.startsWith(EaafConstants.URN_PREFIX_WBPK)) { + for (Entry<String, String> mapper : EaafConstants.URN_WBPK_TARGET_X_TO_NONE_MAPPER.entrySet()) { + if (targetIdentifier.startsWith(mapper.getKey())) { + String wbpkTarget = mapper.getValue() + targetIdentifier.substring(mapper.getKey().length()); + log.trace("Find new wbPK target: {}. Replace it by: {}", targetIdentifier, wbpkTarget); + return wbpkTarget; + + } + } + } + + return targetIdentifier; + } + + /** + * Normalize wbPK target identifier for XFN, XZVR, and XERSB to bPK calculation format like, FN, VR, and ERJ. + * + * <p>If the target is not of this types the target will be returned as it is</p> + * + * @param targetIdentifier bPK input target + * @return FN, VR, ERJ, or targetIdentfier if no normalization is required + */ + @Nullable + public static String normalizeBpkTargetIdentifierToBpkCalculationFormat(@Nullable String targetIdentifier) { + if (targetIdentifier != null && targetIdentifier.startsWith(EaafConstants.URN_PREFIX_WBPK)) { + for (Entry<String, String> mapper : EaafConstants.URN_WBPK_TARGET_X_TO_CALC_TARGET_MAPPER.entrySet()) { + if (targetIdentifier.startsWith(mapper.getKey())) { + String wbpkTarget = mapper.getValue() + targetIdentifier.substring(mapper.getKey().length()); + log.trace("Find new wbPK target: {}. Replace it by: {}", targetIdentifier, wbpkTarget); + return wbpkTarget; + + } + } + } + + return targetIdentifier; + } + + /** + * Remove prefixes from bPK target identifier and get only the SP specific part. + * + * @param type full qualified bPK target with 'urn:publicid:gv.at:' prefix + * @return SP specific part, or full type if reduction is not supported + */ + @Nonnull + public static String removeBpkTypePrefix(@Nonnull final String type) { + Assert.isTrue(type != null, "bPKType is 'NULL'"); + if (type.startsWith(EaafConstants.URN_PREFIX_WBPK)) { + return type.substring(EaafConstants.URN_PREFIX_WBPK.length()); + + } else if (type.startsWith(EaafConstants.URN_PREFIX_CDID)) { + return type.substring(EaafConstants.URN_PREFIX_CDID.length()); + + } else if (type.startsWith(EaafConstants.URN_PREFIX_EIDAS)) { + return type.substring(EaafConstants.URN_PREFIX_EIDAS.length()); + + } else { + return type; + + } + } + + /** + * Builds the eIDAS from the given parameters. + * + * @param baseId baseID of the citizen + * @param baseIdType Type of the baseID + * @param sourceCountry CountryCode of that country, which build the eIDAs + * ID + * @param destinationCountry CountryCode of that country, which receives the + * eIDAs ID + * + * @return Pair eIDAs/bPKType in a BASE64 encoding + * @throws EaafBuilderException if some input data are not valid + */ + private static Pair<String, String> buildEidasIdentifer(final String baseId, + final String baseIdType, final String sourceCountry, final String destinationCountry) + throws EaafBuilderException { + String bpk = null; + String bpkType = null; + + // check if we have been called by public sector application + if (baseIdType.startsWith(EaafConstants.URN_PREFIX_BASEID)) { + bpkType = EaafConstants.URN_PREFIX_EIDAS + sourceCountry + "+" + destinationCountry; + log.debug("Building eIDAS identification from: [identValue]+" + bpkType); + bpk = calculatebPKwbPK(baseId + "+" + bpkType); + + } else { // if not, sector identification value is already calculated by BKU + log.debug("eIDAS eIdentifier already provided by BKU"); + bpk = baseId; + } + + if (StringUtils.isEmpty(bpk) || StringUtils.isEmpty(sourceCountry) + || StringUtils.isEmpty(destinationCountry)) { + throw new EaafBuilderException("builder.00", + new Object[] { "eIDAS-ID", + "Unvollständige Parameterangaben: identificationValue=" + bpk + ", Zielland=" + + destinationCountry + ", Ursprungsland=" + sourceCountry }, + "eIDAS-ID: Unvollständige Parameterangaben: identificationValue=" + bpk + ", Zielland=" + + destinationCountry + ", Ursprungsland=" + sourceCountry); + } + + log.trace("eIDAS pseudonym generation finished. "); + final String eIdentifier = sourceCountry + "/" + destinationCountry + "/" + bpk; + + return Pair.newInstance(eIdentifier, bpkType); + } + + private static String calculatebPKwbPK(final String basisbegriff) throws EaafBuilderException { + try { + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + final byte[] hash = md.digest(basisbegriff.getBytes("ISO-8859-1")); + final String hashBase64 = + new String(Base64Utils.encode(hash), "ISO-8859-1").replaceAll("\r\n", ""); // Base64Utils.encode(hash); + return hashBase64; + + } catch (final Exception ex) { + throw new EaafBuilderException(ERROR_CODE_33, new Object[] {ex.toString() }, + ex.getMessage(), ex); + + } + + } + + private static byte[] encrypt(final byte[] inputBytes, final PublicKey publicKey) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException { + byte[] result; + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle + + } catch (final NoSuchAlgorithmException e) { + cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider + } + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + result = cipher.doFinal(inputBytes); + + return result; + } + + private static byte[] decrypt(final byte[] encryptedBytes, final PrivateKey privateKey) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException { + byte[] result; + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle + + } catch (final NoSuchAlgorithmException e) { + cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider + + } + cipher.init(Cipher.DECRYPT_MODE, privateKey); + result = cipher.doFinal(encryptedBytes); + return result; + + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java index e60c326c..623e9d2c 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java @@ -2,37 +2,46 @@ package at.gv.egiz.eaaf.core.impl.credential; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.security.Key; import java.security.KeyStore; +import java.security.KeyStore.LoadStoreParameter; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; import java.security.Security; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.PostConstruct; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; -import at.asitplus.hsmfacade.provider.HsmFacadeProvider; -import at.asitplus.hsmfacade.provider.RemoteKeyStoreLoadParameter; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; - import lombok.extern.slf4j.Slf4j; @Slf4j @@ -43,6 +52,7 @@ public class EaafKeyStoreFactory { public static final String CONFIG_PROP_HSM_FACADE_SSLTRUST = "security.hsmfacade.trustedsslcert"; public static final String CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME = "security.hsmfacade.username"; public static final String CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD = "security.hsmfacade.password"; + public static final String CONFIG_PROP_HSM_FACADE_GRPC_DEADLINE = "security.hsmfacade.grpc.deadline"; public static final String ERRORCODE_00 = "internal.keystore.00"; public static final String ERRORCODE_01 = "internal.keystore.01"; @@ -52,10 +62,26 @@ public class EaafKeyStoreFactory { public static final String ERRORCODE_05 = "internal.keystore.05"; public static final String ERRORCODE_06 = "internal.keystore.06"; public static final String ERRORCODE_07 = "internal.keystore.07"; - + public static final String ERRORCODE_10 = "internal.keystore.10"; + public static final String ERRORCODE_11 = "internal.keystore.11"; + + public static final String ERRORCODE_KEY_00 = "internal.key.00"; + + private static final String HSM_FACADE_PROVIDER_CLASS = "at.asitplus.hsmfacade.provider.HsmFacadeProvider"; + private static final String HSM_FACADE_KEYSTORELOADPARAMETERS_CLASS + = "at.asitplus.hsmfacade.provider.RemoteKeyStoreLoadParameter"; + private static final String HSM_FACADE_PROVIDER_METHOD_CONSTRUCT = "getInstance"; + private static final String HSM_FACADE_PROVIDER_METHOD_INIT = "init"; + private static final String HSM_FACADE_PROVIDER_METHOD_ISINITIALIZED = "isInitialized"; + private static final String HSM_FACADE_PROVIDER_METHOD_HEALTHCHECK = "healthcheck"; + private static final String HSM_FACADE_PROVIDER_INIT_ERROR_MSG + = "Has HSM-Facade class supported '{}' method: {}"; private static final String HSM_FACADE_PROVIDER = "HsmFacade"; private static final String HSM_FACADE_KEYSTORE_TYPE = "RemoteKeyStore"; - + private static final String HSM_FACADE_DEFAULT_DEADLINE = "30"; + + public enum HsmFacadeStatus { UP, DOWN, UNKNOWN } + @Autowired private IConfiguration basicConfig; @Autowired @@ -64,6 +90,43 @@ public class EaafKeyStoreFactory { private boolean isHsmFacadeInitialized = false; /** + * Get a new symmetric key based on a {@link SymmetricKeyConfiguration} object. + * + * @param config Symmetric key configuration + * @return {@link Pair} of a new {@link SecretKey} instance and an optional {@link Provider}. + * The {@link SecretKey} is {@link Nonnull}. If the {@link Provider} is not <code>null</code> + * this {@link SecretKey} requires a specific {@link Provider} for {@link Key} operations. + * @throws EaafException In case of a KeyStore initialization error + */ + @Nonnull + public Pair<SecretKey, Provider> buildNewSymmetricKey(SymmetricKeyConfiguration config) throws EaafException { + log.trace("Starting symmetric-key generation based on configuration object ... "); + if (SymmetricKeyType.PASSPHRASE.equals(config.getKeyType())) { + return generatePassPhraseBasedSymmetricKey(config); + + } else if (SymmetricKeyType.HSMFACADE.equals(config.getKeyType())) { + if (isHsmFacadeInitialized) { + return getSymmetricKeyFromHsmFacade(config); + + } else { + log.error("HSMFacade can NOT be used for symmetric Key: {} because {} is not initialized", + config.getFriendlyName()); + throw new EaafConfigurationException(ERRORCODE_00, + new Object[] { config.getFriendlyName() }); + + } + + } else { + log.warn("Symmetric KeyType: {} is unrecognized", config.getKeyType()); + throw new EaafConfigurationException(ERRORCODE_01, + new Object[] { config.getFriendlyName() }); + + } + + + } + + /** * Get a new KeyStore based on a KeyStore configuration-object. * * @param config KeyStore configuration @@ -113,45 +176,168 @@ public class EaafKeyStoreFactory { return isHsmFacadeInitialized; } + + /** + * Get the current status for HSM-Facade interaction. + * + * @return {@link HsmFacadeStatus} to indicate the current status. + */ + public HsmFacadeStatus checkHsmFacadeStatus() { + if (isHsmFacadeInitialized()) { + final Provider alreadyLoadedProvider = Security.getProvider(HSM_FACADE_PROVIDER); + if (alreadyLoadedProvider != null) { + try { + final Method healthCheck = + alreadyLoadedProvider.getClass().getMethod(HSM_FACADE_PROVIDER_METHOD_HEALTHCHECK, new Class[]{}); + boolean currentHealthStatus = (boolean) healthCheck.invoke(alreadyLoadedProvider); + HsmFacadeStatus status = currentHealthStatus ? HsmFacadeStatus.UP : HsmFacadeStatus.DOWN; + log.trace("Current HSM-Facade status is: {}", status); + return status; + + } catch (final Exception e) { + log.info("Can not determine state of alreay loaded HSM Facade: {} because HealthCheck not support", + alreadyLoadedProvider.getVersion()); + log.debug("Full HSM-Facade health-check exception", e); + return HsmFacadeStatus.UNKNOWN; + + } + + } else { + log.warn("HSM-Facade is marked as 'initialized', but not load as Security-Provider"); + return HsmFacadeStatus.DOWN; + } + + } else { + log.trace("HSM-Facade is not initialized. Set status do 'unknown'"); + return HsmFacadeStatus.UNKNOWN; + + } + } + @PostConstruct private void initialize() throws EaafException { + final Class<?> hsmProviderClazz = getHsmProviderClass(); + if (hsmProviderClazz != null) { + final String hsmFacadeHost = basicConfig.getBasicConfiguration(CONFIG_PROP_HSM_FACADE_HOST); + final Provider alreadyLoadedProvider = Security.getProvider(HSM_FACADE_PROVIDER); + if (alreadyLoadedProvider != null + && alreadyLoadedProvider.getClass().isAssignableFrom(hsmProviderClazz)) { + log.info("Find already initialized Java SecurityProvider: {}", alreadyLoadedProvider.getName()); + //mark it as initialized if the state can not be determined + boolean isAlreadyInitialized = true; + try { + final Method initializeCheck = + alreadyLoadedProvider.getClass().getMethod(HSM_FACADE_PROVIDER_METHOD_ISINITIALIZED, new Class[]{}); + isAlreadyInitialized = (boolean) initializeCheck.invoke(alreadyLoadedProvider); + + } catch (final Exception e) { + log.warn("Can not determine state of alreay loaded HSM Facade. Mark it as 'initialized'"); + log.debug("HSM Facade check error: {}", e.getMessage()); + + } + isHsmFacadeInitialized = isAlreadyInitialized; + + if (isHsmFacadeInitialized) { + log.info("HSM Facade is already initialized. {} can provide KeyStores based on remote HSM", + EaafKeyStoreFactory.class.getSimpleName()); + + } else { + log.info("HSM Facade is already loaded but not initialized. {} can NOT provide KeyStores based on remote HSM", + EaafKeyStoreFactory.class.getSimpleName()); + + } + + } else if (StringUtils.isNotEmpty(hsmFacadeHost)) { + log.debug("Find host for HSMFacade. Starting crypto provider initialization ... "); + initializeHsmFacadeSecurityProvider(hsmProviderClazz, hsmFacadeHost); - final String hsmFacadeHost = basicConfig.getBasicConfiguration(CONFIG_PROP_HSM_FACADE_HOST); - if (StringUtils.isNotEmpty(hsmFacadeHost)) { - log.debug("Find host for HSMFacade. Starting crypto provider initialization ... "); - try { - final int port = Integer.parseUnsignedInt( - getConfigurationParameter(CONFIG_PROP_HSM_FACADE_PORT)); - final String clientUsername = - getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME); - final String clientPassword = - getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD); - - final HsmFacadeProvider provider = HsmFacadeProvider.Companion.getInstance(); - provider.init(getHsmFacadeTrustSslCertificate(), clientUsername, clientPassword, hsmFacadeHost, port); - //Security.addProvider(provider); - Security.insertProviderAt(provider, 0); - isHsmFacadeInitialized = true; - log.info("HSM Facade is initialized. {} can provide KeyStores based on remote HSM", + } else { + log.info("HSM Facade is on ClassPath but not configurated. {} can only provide software keystores", EaafKeyStoreFactory.class.getSimpleName()); - } catch (final EaafException e) { - throw e; - - } catch (final Exception e) { - log.error("HSM Facade initialization FAILED with an generic error.", e); - throw new EaafConfigurationException(ERRORCODE_03, new Object[] { e.getMessage() }, e); } } else { - log.info("HSM Facade is not configurated. {} can only provide software keystores", + log.info("HSM Facade is not on ClassPath. {} can only provide software keystores", EaafKeyStoreFactory.class.getSimpleName()); } } + private void initializeHsmFacadeSecurityProvider(Class<?> hsmProviderClazz, String hsmFacadeHost) + throws EaafException { + try { + final int port = Integer.parseUnsignedInt( + getConfigurationParameter(CONFIG_PROP_HSM_FACADE_PORT)); + final String clientUsername = + getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME); + final String clientPassword = + getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD); + final long grpcDeadline = getConfigurationParameterLong(CONFIG_PROP_HSM_FACADE_GRPC_DEADLINE, + HSM_FACADE_DEFAULT_DEADLINE); + + + //initialize HSM-Facade by using JAVA Reflection, because in that case HSM-Facade + //has not be in ClassPath on every project + final Method constructor = hsmProviderClazz.getMethod(HSM_FACADE_PROVIDER_METHOD_CONSTRUCT, new Class[]{}); + final Method initMethod = hsmProviderClazz.getMethod(HSM_FACADE_PROVIDER_METHOD_INIT, + X509Certificate.class, String.class, String.class, String.class, int.class, long.class); + if (initMethod != null && constructor != null) { + final Object rawProvider = constructor.invoke(hsmProviderClazz); + initMethod.invoke( + rawProvider, getHsmFacadeTrustSslCertificate(), + clientUsername, clientPassword, hsmFacadeHost, port, grpcDeadline); + + if (rawProvider instanceof Provider) { + Security.addProvider((Provider) rawProvider); + + isHsmFacadeInitialized = true; + log.info("HSM Facade is initialized. {} can provide KeyStores based on remote HSM", + EaafKeyStoreFactory.class.getSimpleName()); + + } else { + log.warn("Is HSM-Facade class type of 'java.security.Provider': {}", + rawProvider instanceof Provider); + throw new EaafException(ERRORCODE_10, new Object[] {HSM_FACADE_PROVIDER_CLASS}); + + } + + } else { + log.warn(HSM_FACADE_PROVIDER_INIT_ERROR_MSG, + HSM_FACADE_PROVIDER_METHOD_CONSTRUCT, constructor != null); + log.warn(HSM_FACADE_PROVIDER_INIT_ERROR_MSG, + HSM_FACADE_PROVIDER_METHOD_INIT, initMethod != null); + throw new EaafException(ERRORCODE_10, new Object[] {HSM_FACADE_PROVIDER_CLASS}); + + } + + //final HsmFacadeProvider provider = HsmFacadeProvider.Companion.getInstance(); + //provider.init(getHsmFacadeTrustSslCertificate(), clientUsername, clientPassword, hsmFacadeHost, port); + + } catch (final EaafException e) { + throw e; + + } catch (final Exception e) { + log.error("HSM Facade initialization FAILED with an generic error.", e); + throw new EaafConfigurationException(ERRORCODE_03, new Object[] { e.getMessage() }, e); + + } + + } + + private Class<?> getHsmProviderClass() { + try { + return Class.forName(HSM_FACADE_PROVIDER_CLASS); + + } catch (final ClassNotFoundException e1) { + log.debug("No HSM-Facade implemenation in ClassPath. HSM-Facade will not be available"); + return null; + + } + } + @Nonnull private Pair<KeyStore, Provider> getKeyStoreFromFileSystem(KeyStoreConfiguration config) throws EaafConfigurationException, EaafFactoryException { @@ -162,28 +348,41 @@ public class EaafKeyStoreFactory { final String keyStorePassword = checkConfigurationParameter(config.getSoftKeyStorePassword(), ERRORCODE_06, config.getFriendlyName(), "Software-KeyStore missing Password for KeyStore"); - final String absKeyStorePath = FileUtils.makeAbsoluteUrl(keyStorePath, basicConfig - .getConfigurationRootDirectory()); - final Resource ressource = resourceLoader.getResource(absKeyStorePath); + Resource ressource; + if (config.isSkipMakeAbsolutPaths()) { + log.debug("Use filepath from config: {}", keyStorePath); + ressource = resourceLoader.getResource(keyStorePath); + + } else { + final String absKeyStorePath = FileUtils.makeAbsoluteUrl(keyStorePath, basicConfig + .getConfigurationRootDirectory()); + log.debug("Use filepath from config: {}", absKeyStorePath); + + ressource = resourceLoader.getResource(absKeyStorePath); + + } + if (!ressource.exists()) { - throw new EaafConfigurationException(ERRORCODE_05, + throw new EaafConfigurationException(ERRORCODE_06, new Object[] { config.getFriendlyName(), - "File not found at: " + absKeyStorePath }); + "RessourceLoader does NOT find File at: " + ressource.getURI() }); } final InputStream is = ressource.getInputStream(); - final KeyStore keyStore = KeyStoreUtils.loadKeyStore(is, keyStorePassword); + final KeyStore keyStore = KeyStoreUtils.loadKeyStore(is, keyStorePassword, config.getKeyStoreType()); is.close(); - if (keyStore == null) { - throw new EaafFactoryException(ERRORCODE_06, - new Object[] { config.getFriendlyName(), "KeyStore not valid or password wrong" }); - - } return Pair.newInstance(keyStore, null); - } catch (KeyStoreException | IOException e) { + } catch (final EaafException e) { + throw e; + + } catch (final IOException e) { + throw new EaafFactoryException(ERRORCODE_06, + new Object[] { config.getFriendlyName(), "KeyStore not valid or password wrong" }); + + } catch (final Exception e) { log.error("Software KeyStore initialization FAILED with an generic error.", e); throw new EaafConfigurationException(ERRORCODE_03, new Object[] { e.getMessage() }, e); @@ -193,24 +392,102 @@ public class EaafKeyStoreFactory { @Nonnull private Pair<KeyStore, Provider> getKeyStoreFromHsmFacade(KeyStoreConfiguration config) throws EaafFactoryException, EaafConfigurationException { - final String keyStoreName = checkConfigurationParameter(config.getKeyStoreName(), - ERRORCODE_06, config.getFriendlyName(), "KeyStoreName missing for HSM Facade"); + return getKeyStoreFromHsmFacade(config.getKeyStoreName(), config.getFriendlyName()); + + } + + @Nonnull + private Pair<KeyStore, Provider> getKeyStoreFromHsmFacade(String keyStoreName, String friendlyName) + throws EaafFactoryException, EaafConfigurationException { + final String validatedKeyStoreName = checkConfigurationParameter(keyStoreName, + ERRORCODE_06, friendlyName, "KeyStoreName missing for HSM Fac)ade"); try { final KeyStore keyStore = KeyStore.getInstance(HSM_FACADE_KEYSTORE_TYPE, HSM_FACADE_PROVIDER); - keyStore.load(new RemoteKeyStoreLoadParameter(keyStoreName)); + keyStore.load(getHsmFacadeKeyStoreParameter(validatedKeyStoreName)); return Pair.newInstance(keyStore, keyStore.getProvider()); } catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException - | NoSuchProviderException e) { + | NoSuchProviderException | EaafException e) { log.error("Can not initialize KeyStore: {} with reason: {}", - config.getFriendlyName(), e.getMessage()); + friendlyName, e.getMessage()); throw new EaafFactoryException(ERRORCODE_06, - new Object[] { config.getFriendlyName(), e.getMessage() }, e); + new Object[] {friendlyName, e.getMessage() }, e); } } + private KeyStore.LoadStoreParameter getHsmFacadeKeyStoreParameter(String keyStoreName) throws EaafException { + try { + final Class<?> clazz = Class.forName(HSM_FACADE_KEYSTORELOADPARAMETERS_CLASS); + final Constructor<?> constructor = clazz.getConstructor(String.class); + final Object keyStoreParams = constructor.newInstance(keyStoreName); + return (LoadStoreParameter) keyStoreParams; + + } catch (final Exception e) { + log.error("Can NOT build class: {} for HSM-Facade provider", HSM_FACADE_KEYSTORELOADPARAMETERS_CLASS, e); + throw new EaafException(ERRORCODE_10, new Object[] {HSM_FACADE_PROVIDER_CLASS}, e); + + } + + } + + @Nonnull + private Pair<SecretKey, Provider> generatePassPhraseBasedSymmetricKey(SymmetricKeyConfiguration config) + throws EaafConfigurationException { + checkConfigurationParameter(config.getSoftKeyPassphrase(), + ERRORCODE_KEY_00, config.getFriendlyName(), "passphrase missing"); + checkConfigurationParameter(config.getSoftKeySalt(), + ERRORCODE_KEY_00, config.getFriendlyName(), "salt missing"); + + try { + final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA256"); + final KeySpec spec = new PBEKeySpec( + config.getSoftKeyPassphrase().toCharArray(), + config.getSoftKeySalt().getBytes("UTF-8"), + 10000, 128); + return Pair.newInstance(keyFactory.generateSecret(spec), null); + + } catch (NoSuchAlgorithmException | InvalidKeySpecException | UnsupportedEncodingException e) { + log.error("Passphrase based symmetric-key generation FAILED", e); + throw new EaafConfigurationException(ERRORCODE_KEY_00, + new Object[] { config.getFriendlyName(), e.getMessage() }, + e); + + } + } + + @Nonnull + private Pair<SecretKey, Provider> getSymmetricKeyFromHsmFacade(SymmetricKeyConfiguration config) + throws EaafFactoryException, EaafConfigurationException, EaafKeyAccessException { + final Pair<KeyStore, Provider> keyStore = getKeyStoreFromHsmFacade( + config.getKeyStoreName(), config.getFriendlyName()); + + checkConfigurationParameter(config.getKeyAlias(), + ERRORCODE_KEY_00, config.getFriendlyName(), "keyAlias missing"); + + try { + final SecretKey secretKey = (SecretKey) keyStore.getFirst().getKey(config.getKeyAlias(), null); + if (secretKey == null) { + throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, + config.getFriendlyName(), config.getKeyAlias(), "No SecretKey with Alias "); + + } + + return Pair.newInstance(secretKey, keyStore.getSecond()); + + } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e) { + throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, e, + config.getFriendlyName(), config.getKeyAlias(), e.getMessage()); + + } catch (final ClassCastException e) { + throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, + config.getFriendlyName(), config.getKeyAlias(), "Wrong SecretKey type "); + + } + + } + private X509Certificate getHsmFacadeTrustSslCertificate() throws EaafConfigurationException { try { final String certFilePath = getConfigurationParameter(CONFIG_PROP_HSM_FACADE_SSLTRUST); @@ -241,6 +518,19 @@ public class EaafKeyStoreFactory { } @Nonnull + private Long getConfigurationParameterLong(@Nonnull String configParamKey, String defaultValue) + throws EaafConfigurationException { + try { + return Long.valueOf(basicConfig.getBasicConfiguration(configParamKey, defaultValue)); + + } catch (NumberFormatException e) { + throw new EaafConfigurationException(ERRORCODE_05, new Object[] { configParamKey, e.getMessage()}); + + } + + } + + @Nonnull private String getConfigurationParameter(@Nonnull String configParamKey) throws EaafConfigurationException { return checkConfigurationParameter( diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java index b4b44724..12541222 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java @@ -24,13 +24,13 @@ import lombok.extern.slf4j.Slf4j; public class EaafKeyStoreUtils { private static final String ERROR_MSG_REASON = "Maybe 'Alias' is not valid"; private static final String ERROR_MSG_1 = "Can NOT access key: {} in KeyStore: {}. Reason: {}"; - private static final String ERROR_MSG_2 = "Key: {} will be NOT available"; + private static final String ERROR_MSG_2 = "Key: {} will be NOT available"; /** * Read all certificates from a {@link KeyStore}. * * @param keyStore KeyStore with certificates - * @return {@link List} of {@link X509Certificate}, but never null + * @return Unmodifiable {@link List} of {@link X509Certificate}, but never null * @throws KeyStoreException In case of an error during KeyStore operations */ @Nonnull @@ -45,6 +45,7 @@ public class EaafKeyStoreUtils { final Certificate cert = keyStore.getCertificate(el); if (cert != null && cert instanceof X509Certificate) { result.add((X509Certificate) cert); + } else { log.info("Can not process entry: {}. Reason: {}", el, cert != null ? cert.getType() : "cert is null"); } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/KeyStoreConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/KeyStoreConfiguration.java index 970efd22..c1a1d917 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/KeyStoreConfiguration.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/KeyStoreConfiguration.java @@ -4,10 +4,9 @@ import java.util.Map; import javax.annotation.Nonnull; -import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; - import org.apache.commons.lang3.StringUtils; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -53,6 +52,12 @@ public class KeyStoreConfiguration { */ private String softKeyStorePassword; + + /** + * Use filePaths as it is and does not make it absolut. + */ + private boolean skipMakeAbsolutPaths = false; + /** * Build a {@link KeyStoreConfiguration} from a configuration map. <br> * <p> diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/SymmetricKeyConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/SymmetricKeyConfiguration.java new file mode 100644 index 00000000..9477789c --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/SymmetricKeyConfiguration.java @@ -0,0 +1,221 @@ +package at.gv.egiz.eaaf.core.impl.credential; + +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.commons.lang3.StringUtils; + +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Getter +@Setter +public class SymmetricKeyConfiguration { + + public static final String PROP_CONFIG_KEY_TYPE = + "key.type"; + + public static final String PROP_CONFIG_HSMFACADE_NAME = + "keystore.name"; + public static final String PROP_CONFIG_HSM_KEY_ALIAS = + "key.alias"; + + public static final String PROP_CONFIG_SOFTWARE_KEY_PASSPHRASE = + "key.passphrase"; + public static final String PROP_CONFIG_SOFTWARE_KEY_SALT = + "key.salt"; + + /** + * FriendlyName for this KeyStore. Mainly used for logging. + */ + private String friendlyName; + + /** + * General type of the KeyStore that should be generated. + */ + private SymmetricKeyType keyType; + + /** + * Name of the KeyStore in HSM Facade. + */ + private String keyStoreName; + + /** + * Alias of the Key in HSM Facade keystore. + */ + private String keyAlias; + + /** + * Software key passphrase. + */ + private String softKeyPassphrase; + + /** + * Software key salt. + */ + private String softKeySalt; + + /** + * Build a {@link SymmetricKeyConfiguration} from a configuration map. <br> + * <p> + * The configuration parameters defined in this class are used to load the + * configuration. + * </p> + * + * @param config Configuration + * @param friendlyName FriendlyName for this KeyStore + * @return Configuration object for {@link EaafKeyStoreFactory} + * @throws EaafConfigurationException In case of a configuration error. + */ + public static SymmetricKeyConfiguration buildFromConfigurationMap(Map<String, String> config, + String friendlyName) throws EaafConfigurationException { + + final SymmetricKeyConfiguration internalConfig = new SymmetricKeyConfiguration(); + internalConfig.setFriendlyName(friendlyName); + + final SymmetricKeyType internalKeyStoreType = SymmetricKeyType.fromString( + getConfigurationParameter(config, PROP_CONFIG_KEY_TYPE)); + if (internalKeyStoreType != null) { + internalConfig.setKeyType(internalKeyStoreType); + + } else { + log.error("Symmetric Key-configuration: {} sets an unknown Keytype: {}", + friendlyName, getConfigurationParameter(config, PROP_CONFIG_KEY_TYPE)); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_01, + new Object[] { friendlyName }); + + } + + if (internalKeyStoreType.equals(SymmetricKeyType.HSMFACADE)) { + log.trace("Set-up HSM-Facade Symmentric-Key ... "); + internalConfig.setKeyStoreName(getConfigurationParameter(config, PROP_CONFIG_HSMFACADE_NAME)); + internalConfig.setKeyAlias(getConfigurationParameter(config, PROP_CONFIG_HSM_KEY_ALIAS)); + + } else { + log.trace("Set-up software passphrase based symmetric key ... "); + internalConfig.setSoftKeyPassphrase(getConfigurationParameter(config, PROP_CONFIG_SOFTWARE_KEY_PASSPHRASE)); + internalConfig.setSoftKeySalt(getConfigurationParameter(config, PROP_CONFIG_SOFTWARE_KEY_SALT)); + + } + + return internalConfig; + } + + /** + * Set the Type of the symmetric key based on String identifier. + * + * @param keyType String based KeyStore type + * @throws EaafConfigurationException In case of an unknown KeyStore type + */ + public void setKeyType(@Nonnull String keyType) throws EaafConfigurationException { + final SymmetricKeyType internalKeyStoreType = SymmetricKeyType.fromString(keyType); + if (internalKeyStoreType != null) { + setKeyType(internalKeyStoreType); + + } else { + log.error("KeyStore: {} sets an unknown KeyStore type: {}", + friendlyName, keyType); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_01, + new Object[] { friendlyName }); + + } + + } + + /** + * Set the Type of the symmetric Key based on String identifier. + * + * @param type of tke symmetric key + */ + public void setKeyType(@Nonnull SymmetricKeyType type) { + this.keyType = type; + + } + + /** + * Validate the internal state of this configuration object. + * + * @throws EaafConfigurationException In case of a configuration error + */ + public void validate() throws EaafConfigurationException { + if (SymmetricKeyType.HSMFACADE.equals(keyType)) { + log.trace("Validate HSM-Facade symmetric key ... "); + checkConfigurationValue(keyStoreName, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'KeyStoreName' for HSM-Facade"); + checkConfigurationValue(keyAlias, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'KeyAlias' for HSM-Facade"); + + } else { + log.trace("Validate passphrase based symmetric key ... "); + checkConfigurationValue(softKeyPassphrase, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'passphrase' for symmetric-key generation"); + checkConfigurationValue(softKeySalt, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'salt' for symmetric-key generation"); + + } + } + + public enum SymmetricKeyType { + PASSPHRASE("passphrase"), HSMFACADE("hsmfacade"); + + private final String keyType; + + SymmetricKeyType(final String keyStoreType) { + this.keyType = keyStoreType; + } + + /** + * Get Type of this Key. + * + * @return + */ + public String getKeyType() { + return this.keyType; + } + + /** + * Get KeyType from String representation. + * + * @param s Config parameter + * @return + */ + public static SymmetricKeyType fromString(final String s) { + try { + return SymmetricKeyType.valueOf(s.toUpperCase()); + + } catch (IllegalArgumentException | NullPointerException e) { + return null; + } + } + + @Override + public String toString() { + return getKeyType(); + + } + } + + @Nonnull + private static String getConfigurationParameter(@Nonnull Map<String, String> config, + @Nonnull String configParamKey) + throws EaafConfigurationException { + final String configValue = config.get(configParamKey); + checkConfigurationValue(configValue, EaafKeyStoreFactory.ERRORCODE_04, configParamKey); + return configValue; + + } + + private static void checkConfigurationValue(String configValue, String errorCode, String... params) + throws EaafConfigurationException { + if (StringUtils.isEmpty(configValue)) { + throw new EaafConfigurationException(errorCode, + params); + + } + + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafHttpRequestRetryHandler.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafHttpRequestRetryHandler.java new file mode 100644 index 00000000..3aa908e8 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafHttpRequestRetryHandler.java @@ -0,0 +1,33 @@ +package at.gv.egiz.eaaf.core.impl.http; + +import java.net.UnknownHostException; +import java.util.Arrays; + +import javax.net.ssl.SSLException; + +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; + +public class EaafHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler implements + HttpRequestRetryHandler { + + /** + * Create the request retry handler using the following list of non-retriable. + * IOException classes: <br> + * <ul> + * <li>UnknownHostException</li> + * <li>SSLException</li> + * </ul> + * + * @param retryCount how many times to retry; 0 means no retries + * @param requestSentRetryEnabled true if it's OK to retry non-idempotent + * requests that have been sent + */ + public EaafHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) { + super(retryCount, requestSentRetryEnabled, Arrays.asList( + UnknownHostException.class, + SSLException.class)); + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslContextBuilder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslContextBuilder.java new file mode 100644 index 00000000..1cd739de --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslContextBuilder.java @@ -0,0 +1,433 @@ +package at.gv.egiz.eaaf.core.impl.http; + +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.http.ssl.PrivateKeyDetails; +import org.apache.http.ssl.PrivateKeyStrategy; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.TrustStrategy; +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; + +/** + * Fork of {@link SSLContextBuilder} that uses JSSE provider to get TrustManager. + * + * <p>This implementation fix an incompatibility between {@link BouncyCastleJsseProvider} and JAVA JDK >= v9</p> + * + * @author tlenz + * + */ +public class EaafSslContextBuilder { + + static final String TLS = "TLS"; + + private String protocol; + private final Set<KeyManager> keyManagers; + private String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + private String keyStoreType = KeyStore.getDefaultType(); + private final Set<TrustManager> trustManagers; + private String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + private SecureRandom secureRandom; + private Provider provider; + + public static EaafSslContextBuilder create() { + return new EaafSslContextBuilder(); + } + + /** + * Get a new SSLContext builder object. + */ + public EaafSslContextBuilder() { + super(); + this.keyManagers = new LinkedHashSet<>(); + this.trustManagers = new LinkedHashSet<>(); + } + + /** + * Sets the SSLContext protocol algorithm name. + * + * @param protocol the SSLContext protocol algorithm name of the requested + * protocol. See the SSLContext section in the <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java + * Cryptography Architecture Standard Algorithm Name + * Documentation</a> for more information. + * @return this builder + * @see <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java + * Cryptography Architecture Standard Algorithm Name Documentation</a> + * @deprecated Use {@link #setProtocol(String)}. + */ + @Deprecated + public EaafSslContextBuilder useProtocol(final String protocol) { + this.protocol = protocol; + return this; + } + + /** + * Sets the SSLContext protocol algorithm name. + * + * @param protocol the SSLContext protocol algorithm name of the requested + * protocol. See the SSLContext section in the <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java + * Cryptography Architecture Standard Algorithm Name + * Documentation</a> for more information. + * @return this builder + * @see <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java + * Cryptography Architecture Standard Algorithm Name Documentation</a> + * @since 4.4.7 + */ + public EaafSslContextBuilder setProtocol(final String protocol) { + this.protocol = protocol; + return this; + } + + public EaafSslContextBuilder setSecureRandom(final SecureRandom secureRandom) { + this.secureRandom = secureRandom; + return this; + } + + public EaafSslContextBuilder setProvider(final Provider provider) { + this.provider = provider; + return this; + } + + public EaafSslContextBuilder setProvider(final String name) { + this.provider = Security.getProvider(name); + return this; + } + + /** + * Sets the key store type. + * + * @param keyStoreType the SSLkey store type. See the KeyStore section in the + * <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java + * Cryptography Architecture Standard Algorithm Name + * Documentation</a> for more information. + * @return this builder + * @see <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java + * Cryptography Architecture Standard Algorithm Name Documentation</a> + * @since 4.4.7 + */ + public EaafSslContextBuilder setKeyStoreType(final String keyStoreType) { + this.keyStoreType = keyStoreType; + return this; + } + + /** + * Sets the key manager factory algorithm name. + * + * @param keyManagerFactoryAlgorithm the key manager factory algorithm name of + * the requested protocol. See the + * KeyManagerFactory section in the <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java + * Cryptography Architecture Standard + * Algorithm Name Documentation</a> for more + * information. + * @return this builder + * @see <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java + * Cryptography Architecture Standard Algorithm Name Documentation</a> + * @since 4.4.7 + */ + public EaafSslContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) { + this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm; + return this; + } + + /** + * Sets the trust manager factory algorithm name. + * + * @param trustManagerFactoryAlgorithm the trust manager algorithm name of the + * requested protocol. See the + * TrustManagerFactory section in the + * <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java + * Cryptography Architecture Standard + * Algorithm Name Documentation</a> for more + * information. + * @return this builder + * @see <a href= + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java + * Cryptography Architecture Standard Algorithm Name Documentation</a> + * @since 4.4.7 + */ + public EaafSslContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) { + this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm; + return this; + } + + /** + * Load custom truststore. + * + * @param truststore {@link KeyStore} if trusted certificates + * @param trustStrategy Trust validation strategy + * @return {@link EaafSslContextBuilder} + * @throws NoSuchAlgorithmException In case of an invalid TrustManager algorithm + * @throws KeyStoreException In case of an invalid KeyStore + */ + public EaafSslContextBuilder loadTrustMaterial( + final KeyStore truststore, + final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { + + final String alg = trustManagerFactoryAlgorithm == null + ? TrustManagerFactory.getDefaultAlgorithm() + : trustManagerFactoryAlgorithm; + + final TrustManagerFactory tmfactory = provider != null + ? TrustManagerFactory.getInstance(alg, provider) + : TrustManagerFactory.getInstance(alg); + tmfactory.init(truststore); + final TrustManager[] tms = tmfactory.getTrustManagers(); + if (tms != null) { + if (trustStrategy != null) { + for (int i = 0; i < tms.length; i++) { + final TrustManager tm = tms[i]; + if (tm instanceof X509TrustManager) { + tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy); + } + } + } + Collections.addAll(this.trustManagers, tms); + } + return this; + } + + public EaafSslContextBuilder loadTrustMaterial( + final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { + return loadTrustMaterial(null, trustStrategy); + } + + + /** + * Load SSL client-authentication key-material into SSL context. + * + * @param keystore {@link KeyStore} for SSL client-authentication + * @param keyPassword Password for this keystore + * @param aliasStrategy Stategy to select keys by alias + * @return {@link EaafSslContextBuilder} + * @throws NoSuchAlgorithmException In case of an invalid KeyManagerFactory algorithm + * @throws KeyStoreException In case of an invalid KeyStore + * @throws UnrecoverableKeyException In case of a invalid Key in this KeyStore + */ + public EaafSslContextBuilder loadKeyMaterial( + final KeyStore keystore, + final char[] keyPassword, + final PrivateKeyStrategy aliasStrategy) + throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { + final KeyManagerFactory kmfactory = KeyManagerFactory + .getInstance(keyManagerFactoryAlgorithm == null ? KeyManagerFactory.getDefaultAlgorithm() + : keyManagerFactoryAlgorithm); + kmfactory.init(keystore, keyPassword); + final KeyManager[] kms = kmfactory.getKeyManagers(); + if (kms != null) { + if (aliasStrategy != null) { + for (int i = 0; i < kms.length; i++) { + final KeyManager km = kms[i]; + if (km instanceof X509ExtendedKeyManager) { + kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy); + } + } + } + Collections.addAll(keyManagers, kms); + } + return this; + } + + public EaafSslContextBuilder loadKeyMaterial( + final KeyStore keystore, + final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, + UnrecoverableKeyException { + return loadKeyMaterial(keystore, keyPassword, null); + } + + protected void initSslContext( + final SSLContext sslContext, + final Collection<KeyManager> keyManagers, + final Collection<TrustManager> trustManagers, + final SecureRandom secureRandom) throws KeyManagementException { + sslContext.init( + !keyManagers.isEmpty() ? keyManagers.toArray(new KeyManager[keyManagers.size()]) : null, + !trustManagers.isEmpty() ? trustManagers.toArray(new TrustManager[trustManagers.size()]) : null, + secureRandom); + } + + /** + * Build a {@link SSLContext} from this builder. + * + * @return new {@link SSLContext} + * @throws NoSuchAlgorithmException In case of an unknown SSL protocol + * @throws KeyManagementException In case of a key-access error + */ + public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException { + final SSLContext sslContext; + final String protocolStr = this.protocol != null ? this.protocol : TLS; + if (this.provider != null) { + sslContext = SSLContext.getInstance(protocolStr, this.provider); + } else { + sslContext = SSLContext.getInstance(protocolStr); + } + initSslContext(sslContext, keyManagers, trustManagers, secureRandom); + return sslContext; + } + + static class TrustManagerDelegate implements X509TrustManager { + + private final X509TrustManager trustManager; + private final TrustStrategy trustStrategy; + + TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) { + super(); + this.trustManager = trustManager; + this.trustStrategy = trustStrategy; + } + + @Override + public void checkClientTrusted( + final X509Certificate[] chain, final String authType) throws CertificateException { + this.trustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted( + final X509Certificate[] chain, final String authType) throws CertificateException { + if (!this.trustStrategy.isTrusted(chain, authType)) { + this.trustManager.checkServerTrusted(chain, authType); + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return this.trustManager.getAcceptedIssuers(); + } + + } + + static class KeyManagerDelegate extends X509ExtendedKeyManager { + + private final X509ExtendedKeyManager keyManager; + private final PrivateKeyStrategy aliasStrategy; + + KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) { + super(); + this.keyManager = keyManager; + this.aliasStrategy = aliasStrategy; + } + + @Override + public String[] getClientAliases( + final String keyType, final Principal[] issuers) { + return this.keyManager.getClientAliases(keyType, issuers); + } + + public Map<String, PrivateKeyDetails> getClientAliasMap( + final String[] keyTypes, final Principal[] issuers) { + final Map<String, PrivateKeyDetails> validAliases = new HashMap<>(); + for (final String keyType : keyTypes) { + final String[] aliases = this.keyManager.getClientAliases(keyType, issuers); + if (aliases != null) { + for (final String alias : aliases) { + validAliases.put(alias, + new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); + } + } + } + return validAliases; + } + + public Map<String, PrivateKeyDetails> getServerAliasMap( + final String keyType, final Principal[] issuers) { + final Map<String, PrivateKeyDetails> validAliases = new HashMap<>(); + final String[] aliases = this.keyManager.getServerAliases(keyType, issuers); + if (aliases != null) { + for (final String alias : aliases) { + validAliases.put(alias, + new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); + } + } + return validAliases; + } + + @Override + public String chooseClientAlias( + final String[] keyTypes, final Principal[] issuers, final Socket socket) { + final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers); + return this.aliasStrategy.chooseAlias(validAliases, socket); + } + + @Override + public String[] getServerAliases( + final String keyType, final Principal[] issuers) { + return this.keyManager.getServerAliases(keyType, issuers); + } + + @Override + public String chooseServerAlias( + final String keyType, final Principal[] issuers, final Socket socket) { + final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers); + return this.aliasStrategy.chooseAlias(validAliases, socket); + } + + @Override + public X509Certificate[] getCertificateChain(final String alias) { + return this.keyManager.getCertificateChain(alias); + } + + @Override + public PrivateKey getPrivateKey(final String alias) { + return this.keyManager.getPrivateKey(alias); + } + + @Override + public String chooseEngineClientAlias( + final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) { + final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers); + return this.aliasStrategy.chooseAlias(validAliases, null); + } + + @Override + public String chooseEngineServerAlias( + final String keyType, final Principal[] issuers, final SSLEngine sslEngine) { + final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers); + return this.aliasStrategy.chooseAlias(validAliases, null); + } + + } + + @Override + public String toString() { + return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType + + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers + + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + + trustManagers + + ", secureRandom=" + secureRandom + "]"; + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java index 1e1e2137..d2377d69 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java @@ -33,18 +33,23 @@ public class EaafSslKeySelectionStrategy implements PrivateKeyStrategy { @Override public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) { log.trace("Selection SSL client-auth key for alias: {}", keyAlias); + if (aliases.keySet().isEmpty()) { + log.debug("No Key with Alias: {} in empty KeyStore", keyAlias); + return null; + + } + final PrivateKeyDetails selected = aliases.get(keyAlias); if (selected != null) { log.trace("Select SL client-auth key with type:", selected.getType()); return keyAlias; - } else { + } else { log.warn("KeyStore contains NO key with alias: {}. Using first key from keystore", keyAlias); log.info("Available aliases: {}", StringUtils.join(aliases.keySet(), ", ")); return aliases.keySet().iterator().next(); - + } - } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java index 582ad545..9239d0c5 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java @@ -5,11 +5,12 @@ import java.util.UUID; import javax.annotation.Nonnull; -import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; -import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; - import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.ServiceUnavailableRetryStrategy; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -56,7 +57,17 @@ public class HttpClientConfiguration { @Setter private boolean followHttpRedirects = true; + + @Setter + private int httpErrorRetryCount = 3; + + @Setter + private boolean httpErrorRetryPost = false; + @Setter + private ServiceUnavailableRetryStrategy serviceUnavailStrategy = null; + + /** * Get a new HTTP-client configuration object. * @@ -117,7 +128,9 @@ public class HttpClientConfiguration { } - if (StringUtils.isEmpty(this.sslKeyPassword)) { + if (StringUtils.isEmpty(this.sslKeyPassword) + && (KeyStoreType.JKS.equals(keyStoreConfig.getKeyStoreType()) + || KeyStoreType.PKCS12.equals(keyStoreConfig.getKeyStoreType()))) { throw new EaafConfigurationException(ERROR_02, new Object[] { this.friendlyName, this.keyStoreConfig.getFriendlyName()}); @@ -187,5 +200,4 @@ public class HttpClientConfiguration { } } - } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java index 00d5891a..07522b56 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java @@ -1,8 +1,11 @@ package at.gv.egiz.eaaf.core.impl.http; import java.security.KeyStore; +import java.security.Provider; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; @@ -22,6 +25,7 @@ import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.config.SocketConfig; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; @@ -32,16 +36,19 @@ 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.BasicHttpClientConnectionManager; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; import org.apache.http.ssl.SSLContexts; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; +import at.gv.egiz.eaaf.core.impl.data.Pair; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -64,6 +71,10 @@ public class HttpClientFactory implements IHttpClientFactory { "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_CONNECTION_RETRY_COUNT = + "client.http.connection.retry.count"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST = + "client.http.connection.retry.post"; public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = "client.http.ssl.hostnameverifier.trustall"; @@ -89,9 +100,16 @@ public class HttpClientFactory implements IHttpClientFactory { 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"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT = "3"; + public static final String DEFAUTL_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST = String.valueOf(false); + public static final int DEFAULT_CLEANUP_RUNNER_TIME = 30000; + public static final int DEFAULT_CLEANUP_IDLE_TIME = 60; + + private String defaultConfigurationId = null; - private final Map<String, HttpClientBuilder> availableBuilders = new HashMap<>(); + private final Map<String, Pair<HttpClientBuilder, HttpClientConnectionManager>> + availableBuilders = new HashMap<>(); /* * (non-Javadoc) @@ -106,7 +124,7 @@ public class HttpClientFactory implements IHttpClientFactory { @Override public CloseableHttpClient getHttpClient(final boolean followRedirects) { - return availableBuilders.get(defaultConfigurationId).setRedirectStrategy( + return availableBuilders.get(defaultConfigurationId).getFirst().setRedirectStrategy( buildRedirectStrategy(followRedirects)).build(); } @@ -116,27 +134,31 @@ public class HttpClientFactory implements IHttpClientFactory { log.trace("Build http client for: {}", config.getFriendlyName()); HttpClientBuilder builder = null; if (availableBuilders.containsKey(config.getUuid())) { - builder = availableBuilders.get(config.getUuid()); + builder = availableBuilders.get(config.getUuid()).getFirst(); } else { log.debug("Initialize new http-client builder for: {}", config.getFriendlyName()); - //validate configuration object + // validate configuration object config.validate(); builder = HttpClients.custom(); + + // inject request configuration builder.setDefaultRequestConfig(buildDefaultRequestConfig()); + injectInternalRetryHandler(builder, config); - //inject basic authentication infos + // inject basic authentication infos injectBasicAuthenticationIfRequired(builder, config); - //inject authentication if required + // inject authentication if required final LayeredConnectionSocketFactory sslConnectionFactory = getSslContext(config); // set pool connection if required - injectDefaultConnectionPoolIfRequired(builder, sslConnectionFactory); + HttpClientConnectionManager connectionManager + = injectConnectionManager(builder, sslConnectionFactory); - availableBuilders.put(config.getUuid(), builder); + availableBuilders.put(config.getUuid(), Pair.newInstance(builder, connectionManager)); } @@ -145,6 +167,47 @@ public class HttpClientFactory implements IHttpClientFactory { } + /** + * Worker that closes expired connections or connections that in idle + * for more than DEFAULT_CLEANUP_IDLE_TIME seconds. + * + */ + @Scheduled(fixedDelay = DEFAULT_CLEANUP_RUNNER_TIME) + private void httpConnectionPoolCleaner() { + log.trace("Starting http connection-pool eviction policy ... "); + for (final Entry<String, Pair<HttpClientBuilder, HttpClientConnectionManager>> el + : availableBuilders.entrySet()) { + log.trace("Checking connections of http-client: {}", el.getKey()); + el.getValue().getSecond().closeExpiredConnections(); + el.getValue().getSecond().closeIdleConnections(DEFAULT_CLEANUP_IDLE_TIME, TimeUnit.SECONDS); + + } + + } + + private void injectInternalRetryHandler(HttpClientBuilder builder, HttpClientConfiguration config) { + if (config.getHttpErrorRetryCount() > 0) { + log.info("Set HTTP error-retry to {} for http-client: {}", + config.getHttpErrorRetryCount(), config.getFriendlyName()); + builder.setRetryHandler(new EaafHttpRequestRetryHandler( + config.getHttpErrorRetryCount(), + config.isHttpErrorRetryPost())); + + if (config.getServiceUnavailStrategy() != null) { + log.debug("HttpClient configuration: {} set custom ServiceUnavailableRetryStrategy: {}", + config.getFriendlyName(), config.getServiceUnavailStrategy().getClass().getName()); + builder.setServiceUnavailableRetryStrategy(config.getServiceUnavailStrategy()); + + } + + } else { + log.info("Disable HTTP error-retry for http-client: {}", config.getFriendlyName()); + builder.disableAutomaticRetries(); + + } + + } + @PostConstruct private void initalize() throws EaafException { final HttpClientConfiguration defaultHttpClientConfig = buildDefaultHttpClientConfiguration(); @@ -155,8 +218,9 @@ public class HttpClientFactory implements IHttpClientFactory { // set default request configuration defaultHttpClientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig()); + injectInternalRetryHandler(defaultHttpClientBuilder, defaultHttpClientConfig); - //inject http basic authentication + // inject http basic authentication injectBasicAuthenticationIfRequired(defaultHttpClientBuilder, defaultHttpClientConfig); // inject authentication if required @@ -164,11 +228,13 @@ public class HttpClientFactory implements IHttpClientFactory { getSslContext(defaultHttpClientConfig); // set pool connection if required - injectDefaultConnectionPoolIfRequired(defaultHttpClientBuilder, sslConnectionFactory); + HttpClientConnectionManager connectionManager + = injectConnectionManager(defaultHttpClientBuilder, sslConnectionFactory); - //set default http client builder + // set default http client builder defaultConfigurationId = defaultHttpClientConfig.getUuid(); - availableBuilders.put(defaultConfigurationId, defaultHttpClientBuilder); + availableBuilders.put(defaultConfigurationId, + Pair.newInstance(defaultHttpClientBuilder, connectionManager)); } @@ -203,6 +269,13 @@ public class HttpClientFactory implements IHttpClientFactory { config.setDisableHostnameValidation(basicConfig.getBasicConfigurationBoolean( PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false)); + config.setHttpErrorRetryCount(Integer.parseInt(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT))); + config.setHttpErrorRetryPost(Boolean.parseBoolean(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST, + DEFAUTL_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST))); + // validate configuration object config.validate(); @@ -237,8 +310,8 @@ public class HttpClientFactory implements IHttpClientFactory { SSLContext sslContext = null; if (httpClientConfig.getAuthMode().equals(HttpClientConfiguration.ClientAuthMode.SSL)) { log.debug("Open keyStore with type: {}", httpClientConfig.getKeyStoreConfig().getKeyStoreType()); - final KeyStore keyStore = keyStoreFactory.buildNewKeyStore(httpClientConfig.getKeyStoreConfig()) - .getFirst(); + final Pair<KeyStore, Provider> keyStore = keyStoreFactory.buildNewKeyStore(httpClientConfig + .getKeyStoreConfig()); log.trace("Injecting SSL client-authentication into http client ... "); sslContext = HttpUtils.buildSslContextWithSslClientAuthentication(keyStore, @@ -248,7 +321,7 @@ public class HttpClientFactory implements IHttpClientFactory { } else { log.trace("Initializing default SSL Context ... "); sslContext = SSLContexts.createDefault(); - + } // set hostname verifier @@ -266,48 +339,37 @@ public class HttpClientFactory implements IHttpClientFactory { } - private void injectDefaultConnectionPoolIfRequired( + @Nonnull + private HttpClientConnectionManager injectConnectionManager( HttpClientBuilder builder, final LayeredConnectionSocketFactory sslConnectionFactory) { if (basicConfig.getBasicConfigurationBoolean(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE, true)) { - PoolingHttpClientConnectionManager pool; - - // set socketFactoryRegistry if SSLConnectionFactory is Set - if (sslConnectionFactory != null) { - final Registry<ConnectionSocketFactory> socketFactoryRegistry = - RegistryBuilder.<ConnectionSocketFactory>create() - .register("http", PlainConnectionSocketFactory.getSocketFactory()) - .register("https", sslConnectionFactory).build(); - log.trace("Inject SSLSocketFactory into pooled connection"); - pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry); - - } else { - pool = new PoolingHttpClientConnectionManager(); - - } - - pool.setDefaultMaxPerRoute(Integer.parseInt( + PoolingHttpClientConnectionManager connectionPool + = new PoolingHttpClientConnectionManager(getDefaultRegistry(sslConnectionFactory)); + connectionPool.setDefaultMaxPerRoute(Integer.parseInt( basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE, DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE))); - pool.setMaxTotal(Integer.parseInt( + connectionPool.setMaxTotal(Integer.parseInt( basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL, DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); - - pool.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Integer.parseInt( + connectionPool.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Integer.parseInt( basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET, DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) * 1000).build()); + builder.setConnectionManager(connectionPool); + log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", + connectionPool.getMaxTotal(), connectionPool.getDefaultMaxPerRoute()); + return connectionPool; + + } else { + log.debug("Building http-client without Connection-Pool ... "); + final BasicHttpClientConnectionManager basicPool = new BasicHttpClientConnectionManager( + getDefaultRegistry(sslConnectionFactory)); + builder.setConnectionManager(basicPool); + return basicPool; - builder.setConnectionManager(pool); - log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), - pool.getDefaultMaxPerRoute()); - - } else if (sslConnectionFactory != null) { - log.trace("Inject SSLSocketFactory without connection pool"); - builder.setSSLSocketFactory(sslConnectionFactory); - } - + } private RequestConfig buildDefaultRequestConfig() { @@ -323,7 +385,7 @@ public class HttpClientFactory implements IHttpClientFactory { .setSocketTimeout(Integer.parseInt( basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET, DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) - * 1000) + * 1000) .build(); return requestConfig; @@ -350,5 +412,25 @@ public class HttpClientFactory implements IHttpClientFactory { return redirectStrategy; } + + private static Registry<ConnectionSocketFactory> getDefaultRegistry( + final LayeredConnectionSocketFactory sslConnectionFactory) { + final RegistryBuilder<ConnectionSocketFactory> builder = + RegistryBuilder.<ConnectionSocketFactory>create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()); + + if (sslConnectionFactory != null) { + log.trace("Inject own SSLSocketFactory into pooled connection"); + builder.register("https", sslConnectionFactory); + + } else { + log.trace("Inject default SSLSocketFactory into pooled connection"); + builder.register("https", SSLConnectionSocketFactory.getSocketFactory()); + + } + + return builder.build(); + + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java index 2d514912..dd6f69ee 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java @@ -18,10 +18,14 @@ package at.gv.egiz.eaaf.core.impl.http; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.security.UnrecoverableKeyException; import javax.annotation.Nonnull; @@ -29,22 +33,67 @@ import javax.annotation.Nullable; import javax.net.ssl.SSLContext; import javax.servlet.http.HttpServletRequest; -import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; -import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException; - import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.ResponseHandler; import org.apache.http.conn.ssl.TrustAllStrategy; -import org.apache.http.ssl.SSLContextBuilder; -import org.apache.http.ssl.SSLContexts; +import org.apache.http.entity.ContentType; import org.apache.http.ssl.TrustStrategy; +import org.apache.http.util.EntityUtils; +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.data.Triple; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @Slf4j public class HttpUtils { private static final String ERROR_03 = "internal.httpclient.03"; + + /** + * Simple Http response-handler that only give http status-code as result. + * + * @return Status-Code of http response + */ + public static ResponseHandler<StatusLine> simpleStatusCodeResponseHandler() { + return new ResponseHandler<StatusLine>() { + @Override + public StatusLine handleResponse(HttpResponse response) throws ClientProtocolException, IOException { + EntityUtils.consumeQuietly(response.getEntity()); + return response.getStatusLine(); + } + }; + } + + /** + * Http response-handler that gives a pair of http status-code, + * a copy of the full http-body as {@link InputStream} and the response {@link ContentType}. + * + * @return {@link Triple} of http response {@link StatusLine}, http body as {@link InputStream}, + * and {@link ContentType} + */ + public static ResponseHandler<Triple<StatusLine, ByteArrayInputStream, ContentType>> + bodyStatusCodeResponseHandler() { + return new ResponseHandler<Triple<StatusLine, ByteArrayInputStream, ContentType>>() { + @Override + public Triple<StatusLine, ByteArrayInputStream, ContentType> handleResponse(HttpResponse response) + throws ClientProtocolException, IOException { + byte[] bodyBytes = EntityUtils.toByteArray(response.getEntity()); + return Triple.newInstance(response.getStatusLine(), new ByteArrayInputStream(bodyBytes), + ContentType.getOrDefault(response.getEntity())); + + } + }; + } + /** * Helper method to retrieve server URL including context path. * @@ -124,7 +173,7 @@ public class HttpUtils { * @param url URL * @param paramname Name of the parameter. * @param paramvalue Value of the parameter. - * @return + * @return Url with parameter */ public static String addUrlParameter(final String url, final String paramname, final String paramvalue) { @@ -137,6 +186,23 @@ public class HttpUtils { } /** + * Inject HTTP header into http request. + * + * <p>The header is only set if HeaderValue is not null</p> + * + * @param req Http request object + * @param headerName HeaderName + * @param headerValue HeaderValue + */ + public static void addHeaderIfNotEmpty(@NonNull HttpRequest req, @NonNull String headerName, + @Nullable String headerValue) { + if (StringUtils.isNotEmpty(headerValue)) { + req.addHeader(headerName, headerValue); + + } + } + + /** * Initialize a {@link SSLContext} with a {@link KeyStore} that uses X509 Client * authentication. * @@ -155,40 +221,114 @@ public class HttpUtils { * @throws EaafFactoryException In case of a {@link SSLContext} * initialization error */ - public static SSLContext buildSslContextWithSslClientAuthentication(@Nonnull final KeyStore keyStore, + public static SSLContext buildSslContextWithSslClientAuthentication(@Nonnull final Pair<KeyStore, Provider> keyStore, @Nullable String keyAlias, @Nullable String keyPasswordString, boolean trustAllServerCertificates, @Nonnull String friendlyName) throws EaafConfigurationException, EaafFactoryException { try { - log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); - final char[] keyPassword = keyPasswordString == null ? StringUtils.EMPTY.toCharArray() - : keyPasswordString.toCharArray(); - - SSLContextBuilder sslContextBuilder = SSLContexts.custom(); - if (StringUtils.isNotEmpty(keyAlias)) { - sslContextBuilder = sslContextBuilder - .loadKeyMaterial(keyStore, keyPassword, new EaafSslKeySelectionStrategy(keyAlias)); - - } else { - sslContextBuilder = sslContextBuilder - .loadKeyMaterial(keyStore, keyPassword); - - } - - if (trustAllServerCertificates) { - log.warn("Http-client:{} trusts ALL TLS server-certificates!"); - final TrustStrategy trustStrategy = new TrustAllStrategy(); - sslContextBuilder = sslContextBuilder.loadTrustMaterial(trustStrategy); + EaafSslContextBuilder sslContextBuilder = EaafSslContextBuilder.create(); + + injectKeyStore(sslContextBuilder, keyStore, keyAlias, keyPasswordString, friendlyName); + + injectTrustStore(sslContextBuilder, null, trustAllServerCertificates, friendlyName); + + return sslContextBuilder.build(); - } + } catch (NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException + | KeyStoreException e) { + throw new EaafFactoryException(ERROR_03, new Object[] { friendlyName, e.getMessage() }, e); + } + } + + /** + * Initialize a {@link SSLContext} with a {@link KeyStore} that uses X509 Client + * authentication and a custom TrustStore as {@link KeyStore}. + * + * @param keyStore KeyStore with private keys that should be + * used + * @param keyAlias Alias of the key that should be used. If + * the alias is null, than the first key that + * is found will be selected. + * @param keyPasswordString Password of the Key in this keystore + * @param trustStore TrustStore with trusted SSL certificates + * @param trustAllServerCertificates Deactivate SSL server-certificate + * validation + * @param friendlyName FriendlyName of the http client for logging + * purposes + * @return {@link SSLContext} with X509 client authentication + * @throws EaafConfigurationException In case of a configuration error + * @throws EaafFactoryException In case of a {@link SSLContext} + * initialization error + */ + public static SSLContext buildSslContextWithSslClientAuthentication(@Nonnull final Pair<KeyStore, Provider> keyStore, + @Nullable String keyAlias, @Nullable String keyPasswordString, + @Nullable final Pair<KeyStore, Provider> trustStore, boolean trustAllServerCertificates, + @Nonnull String friendlyName) + throws EaafConfigurationException, EaafFactoryException { + try { + EaafSslContextBuilder sslContextBuilder = EaafSslContextBuilder.create(); + + injectKeyStore(sslContextBuilder, keyStore, keyAlias, keyPasswordString, friendlyName); + + injectTrustStore(sslContextBuilder, trustStore, trustAllServerCertificates, friendlyName); + return sslContextBuilder.build(); } catch (NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException | KeyStoreException e) { throw new EaafFactoryException(ERROR_03, new Object[] { friendlyName, e.getMessage() }, e); + } + } + + private static void injectTrustStore(EaafSslContextBuilder sslContextBuilder, + Pair<KeyStore, Provider> trustStore, boolean trustAllServerCertificates, String friendlyName) + throws NoSuchAlgorithmException, KeyStoreException { + + TrustStrategy trustStrategy = null; + if (trustAllServerCertificates) { + log.warn("Http-client:{} trusts ALL TLS server-certificates!", friendlyName); + trustStrategy = new TrustAllStrategy(); + } + + KeyStore trustStoreImpl = null; + if (trustStore != null) { + log.info("Http-client: {} uses custom TrustStore.", friendlyName); + trustStoreImpl = trustStore.getFirst(); + + } + + sslContextBuilder.loadTrustMaterial(trustStoreImpl, trustStrategy); + } + private static void injectKeyStore(EaafSslContextBuilder sslContextBuilder, Pair<KeyStore, Provider> keyStore, + String keyAlias, String keyPasswordString, String friendlyName) + throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException { + + Provider provider; + if (keyStore.getSecond() != null) { + provider = new BouncyCastleJsseProvider(keyStore.getSecond()); + log.debug("KeyStore: {} provide special security-provider. Inject: {} into SSLContext", + friendlyName, provider.getName()); + sslContextBuilder.setProvider(provider); + + } + + log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); + final char[] keyPassword = keyPasswordString == null ? StringUtils.EMPTY.toCharArray() + : keyPasswordString.toCharArray(); + + if (StringUtils.isNotEmpty(keyAlias)) { + sslContextBuilder + .loadKeyMaterial(keyStore.getFirst(), keyPassword, new EaafSslKeySelectionStrategy(keyAlias)); + + } else { + sslContextBuilder.loadKeyMaterial(keyStore.getFirst(), keyPassword); + + } + + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java index 7ec58d46..4e8374e1 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java @@ -2,10 +2,10 @@ package at.gv.egiz.eaaf.core.impl.http; import javax.annotation.Nonnull; -import at.gv.egiz.eaaf.core.exceptions.EaafException; - import org.apache.http.impl.client.CloseableHttpClient; +import at.gv.egiz.eaaf.core.exceptions.EaafException; + public interface IHttpClientFactory { /** diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/pvp/PvpRProfileHttpHeaders.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/pvp/PvpRProfileHttpHeaders.java new file mode 100644 index 00000000..cd6d7404 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/pvp/PvpRProfileHttpHeaders.java @@ -0,0 +1,86 @@ +package at.gv.egiz.eaaf.core.impl.http.pvp; + +/** + * PVP2 R-Profile HTTP-Header definitions. + * + * @author tlenz + * + */ +public class PvpRProfileHttpHeaders { + + //PVP 1.x headers + public static final String PVP_1X_VALUE_VERSION_PREFIX = "1."; + + public static final String PVP_1X_PREFIX = "X-"; + public static final String PVP_1X_VERSION_NAME = "Version"; + public static final String PVP_1X_USERID_NAME = "AUTHENTICATE-UserID"; + public static final String PVP_1X_GID_NAME = "AUTHENTICATE-GVGID"; + public static final String PVP_1X_PARTICIPANT_ID_NAME = "AUTHENTICATE-PARTICIPANTID"; + public static final String PVP_1X_GV_OU_ID_NAME = "AUTHENTICATE-GVOUID"; + public static final String PVP_1X_OU_NAME = "AUTHENTICATE-OU"; + public static final String PVP_1X_FUNCTION_NAME = "AUTHENTICATE-GVFUNCTION"; + public static final String PVP_1X_SECCLASS_NAME = "AUTHENTICATE-gvSecClass"; + public static final String PVP_1X_CN_NAME = "AUTHENTICATE-cn"; + public static final String PVP_1X_COST_CENTER_ID_NAME = "ACCOUNTING-CostCenterId"; + public static final String PVP_1X_INVOICE_RECPT_ID_NAME = "ACCOUNTING-InvoiceRecptId"; + public static final String PVP_1X_ROLES_NAME = "AUTHORIZE-ROLES"; + public static final String PVP_1X_GV_OU_OKZ_NAME = "AUTHENTICATE-GVOUOKZ"; + public static final String PVP_1X_VERSION = PVP_1X_PREFIX + PVP_1X_VERSION_NAME; + public static final String PVP_1X_USERID = PVP_1X_PREFIX + PVP_1X_USERID_NAME; + public static final String PVP_1X_GID = PVP_1X_PREFIX + PVP_1X_GID_NAME; + public static final String PVP_1X_PARTICIPANT_ID = PVP_1X_PREFIX + PVP_1X_PARTICIPANT_ID_NAME; + public static final String PVP_1X_GV_OU_ID = PVP_1X_PREFIX + PVP_1X_GV_OU_ID_NAME; + public static final String PVP_1X_OU = PVP_1X_PREFIX + PVP_1X_OU_NAME; + public static final String PVP_1X_FUNCTION = PVP_1X_PREFIX + PVP_1X_FUNCTION_NAME; + public static final String PVP_1X_SECCLASS = PVP_1X_PREFIX + PVP_1X_SECCLASS_NAME; + public static final String PVP_1X_CN = PVP_1X_PREFIX + PVP_1X_CN_NAME; + public static final String PVP_1X_COST_CENTER_ID = PVP_1X_PREFIX + PVP_1X_COST_CENTER_ID_NAME; + public static final String PVP_1X_INVOICE_RECPT_ID = PVP_1X_PREFIX + PVP_1X_INVOICE_RECPT_ID_NAME; + public static final String PVP_1X_ROLES = PVP_1X_PREFIX + PVP_1X_ROLES_NAME; + public static final String PVP_1X_GV_OU_OKZ = PVP_1X_PREFIX + PVP_1X_GV_OU_OKZ_NAME; + + + //PVP 2.x headers + public static final String PVP_2X_VALUE_VERSION_PREFIX = "2."; + + public static final String PVP_2X_VERSION = "X-PVP-VERSION"; + public static final String PVP_2X_USERID = "X-PVP-USERID"; + public static final String PVP_2X_GID = "X-PVP-GID"; + public static final String PVP_2X_PARTICIPANT_ID = "X-PVP-PARTICIPANT-ID"; + public static final String PVP_2X_GV_OU_ID = "X-PVP-OU-GV-OU-ID"; + public static final String PVP_2X_OU = "X-PVP-OU"; + public static final String PVP_2X_FUNCTION = "X-PVP-FUNCTION"; + public static final String PVP_2X_SECCLASS = "X-PVP-SECCLASS"; + public static final String PVP_2X_PRINCIPAL_NAME = "X-PVP-PRINCIPAL-NAME"; + public static final String PVP_2X_BINDING = "X-PVP-BINDING"; + public static final String PVP_2X_OU_OKZ = "X-PVP-OU-OKZ"; + public static final String PVP_2X_COST_CENTER_ID = "X-PVP-COST-CENTER-ID"; + public static final String PVP_2X_INVOICE_RECPT_ID = "X-PVP-INVOICE-RECPT-ID"; + public static final String PVP_2X_ROLES = "X-PVP-ROLES"; + + public static final String PVP_ERROR_440_CODE = "440"; + public static final String PVP_ERROR_440_MSG = "Mandatory PVP-Header {0} fehlt"; + public static final String PVP_ERROR_441_CODE = "441"; + public static final String PVP_ERROR_441_MSG = "Werte in X-PVP-ROLES haben ungültiges Format"; + public static final String PVP_ERROR_442_CODE = "442"; + public static final String PVP_ERROR_442_MSG = "Kein zulässiges Recht in X-PVP-ROLES"; + public static final String PVP_ERROR_443_CODE = "443"; + public static final String PVP_ERROR_443_MSG = "Die UserId ist am Anwendungsportal gesperrt"; + public static final String PVP_ERROR_444_CODE = "444"; + public static final String PVP_ERROR_444_MSG = + "Stammportal ist für Anfragen des angegebenen Participants nicht berechtigt"; + public static final String PVP_ERROR_445_CODE = "445"; + public static final String PVP_ERROR_445_MSG = "Participant am Anwendungsportal nicht registriert"; + public static final String PVP_ERROR_490_CODE = "490"; + public static final String PVP_ERROR_490_MSG = "Zertifikatsüberprüfung fehlgeschlagen. Grund: {0}"; + public static final String PVP_ERROR_493_CODE = "493"; + public static final String PVP_ERROR_493_MSG = "Keine Berechtigung für diese Anwendung im Stammportal"; + public static final String PVP_ERROR_494_CODE = "494"; + public static final String PVP_ERROR_494_MSG = "Die Authentifizierung des Stammportals ist fehlgeschlagen"; + public static final String PVP_ERROR_511_CODE = "511"; + public static final String PVP_ERROR_511_MSG = "PVP Version nicht unterstützt"; + + private PvpRProfileHttpHeaders() { + + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/conf/SpConfigurationImpl.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/conf/SpConfigurationImpl.java index de54d103..2f4e18fa 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/conf/SpConfigurationImpl.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/conf/SpConfigurationImpl.java @@ -20,8 +20,10 @@ package at.gv.egiz.eaaf.core.impl.idp.conf; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,8 +40,8 @@ public class SpConfigurationImpl implements ISpConfiguration { private static final Logger log = LoggerFactory.getLogger(SpConfigurationImpl.class); private final Map<String, String> spConfiguration; - private final List<String> targetAreasWithNoInteralBaseIdRestriction; - private final List<String> targetAreasWithNoBaseIdTransmissionRestriction; + private final Set<String> targetAreasWithNoInteralBaseIdRestriction; + private final Set<String> targetAreasWithNoBaseIdTransmissionRestriction; /** * Service-provider configuration holder. @@ -52,21 +54,19 @@ public class SpConfigurationImpl implements ISpConfiguration { // set oa specific restrictions targetAreasWithNoInteralBaseIdRestriction = Collections - .unmodifiableList(KeyValueUtils.getListOfCsvValues(authConfig.getBasicConfiguration( - CONFIG_KEY_RESTRICTIONS_BASEID_INTERNAL, EaafConstants.URN_PREFIX_CDID))); + .unmodifiableSet(new HashSet<String>(KeyValueUtils.getListOfCsvValues(authConfig.getBasicConfiguration( + CONFIG_KEY_RESTRICTIONS_BASEID_INTERNAL, EaafConstants.URN_PREFIX_CDID)))); targetAreasWithNoBaseIdTransmissionRestriction = Collections - .unmodifiableList(KeyValueUtils.getListOfCsvValues(authConfig.getBasicConfiguration( - CONFIG_KEY_RESTRICTIONS_BASEID_TRANSMISSION, EaafConstants.URN_PREFIX_CDID))); + .unmodifiableSet(new HashSet<String>(KeyValueUtils.getListOfCsvValues(authConfig.getBasicConfiguration( + CONFIG_KEY_RESTRICTIONS_BASEID_TRANSMISSION, EaafConstants.URN_PREFIX_CDID)))); if (log.isTraceEnabled()) { log.trace("Internal policy for OA: " + getUniqueIdentifier()); - for (final String el : targetAreasWithNoInteralBaseIdRestriction) { - log.trace(" Allow baseID processing for prefix " + el); - } - for (final String el : targetAreasWithNoBaseIdTransmissionRestriction) { - log.trace(" Allow baseID transfer for prefix " + el); - } + targetAreasWithNoInteralBaseIdRestriction.stream() + .forEach(el -> log.trace(" Allow baseID processing for prefix " + el)); + targetAreasWithNoBaseIdTransmissionRestriction.stream() + .forEach(el -> log.trace(" Allow baseID transfer for prefix " + el)); } } @@ -143,12 +143,12 @@ public class SpConfigurationImpl implements ISpConfiguration { } @Override - public final List<String> getTargetsWithNoBaseIdInternalProcessingRestriction() { + public final Set<String> getTargetsWithNoBaseIdInternalProcessingRestriction() { return this.targetAreasWithNoInteralBaseIdRestriction; } @Override - public final List<String> getTargetsWithNoBaseIdTransferRestriction() { + public final Set<String> getTargetsWithNoBaseIdTransferRestriction() { return this.targetAreasWithNoBaseIdTransmissionRestriction; } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/AuthenticatedEncryptionPendingRequestIdGenerationStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/AuthenticatedEncryptionPendingRequestIdGenerationStrategy.java new file mode 100644 index 00000000..ca1db67d --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/AuthenticatedEncryptionPendingRequestIdGenerationStrategy.java @@ -0,0 +1,280 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.nio.charset.StandardCharsets; +import java.security.Provider; +import java.util.Base64; + +import javax.annotation.Nonnull; +import javax.annotation.PostConstruct; +import javax.crypto.SecretKey; + +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.jose4j.jca.ProviderContext; +import org.jose4j.jwa.AlgorithmConstraints; +import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; +import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers; +import org.jose4j.jwe.JsonWebEncryption; +import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers; +import org.jose4j.lang.JoseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +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.PendingReqIdValidationException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; +import at.gv.egiz.eaaf.core.impl.data.Pair; + +/** + * PendingRequestId generation strategy based on signed tokens that facilitates + * extended token validation. + * + * @author tlenz + * + */ +public class AuthenticatedEncryptionPendingRequestIdGenerationStrategy + implements IPendingRequestIdGenerationStrategy { + private static final Logger log = + LoggerFactory.getLogger(AuthenticatedEncryptionPendingRequestIdGenerationStrategy.class); + + @Autowired(required = true) IConfiguration baseConfig; + @Autowired EaafKeyStoreFactory keyStoreFactory; + + private static final String FRIENDLYNAME = "pendingRequestId key"; + + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_TYPE = + "core.pendingrequestid.digist.type"; + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET = + "core.pendingrequestid.digist.secret"; + + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_KEYSTORE = + "core.pendingrequestid.digist.keystore.name"; + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_ALIAS = + "core.pendingrequestid.digist.key.alias"; + + 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 = 2; + private static final String TOKEN_SEPARATOR = "|"; + private static final DateTimeFormatter TOKEN_TEXTUAL_DATE_FORMAT = + DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS ZZ").withZoneUTC(); + + private int maxPendingRequestIdLifeTime = 300; + private final int maxPendingReqIdSize = 1024; + private Pair<SecretKey, Provider> key = null; + private final String salt = "notRequiredInThisScenario"; + + @Override + public String generateExternalPendingRequestId() throws EaafException { + try { + final String toSign = buildInternalToken(Random.nextLongRandom(), DateTime.now()); + JsonWebEncryption encToken = new JsonWebEncryption(); + encToken.setAlgorithmHeaderValue(selectKeyWrappingAlgorithm(key.getFirst())); + encToken.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM); + encToken.setKey(key.getFirst()); + encToken.setPayload(toSign); + + + + if (key.getSecond() != null) { + final ProviderContext providerCtx = new ProviderContext(); + providerCtx.getSuppliedKeyProviderContext().setSignatureProvider( + key.getSecond().getName()); + encToken.setProviderContext(providerCtx); + + } + + return Base64.getUrlEncoder() + .encodeToString(encToken.getCompactSerialization().getBytes(StandardCharsets.UTF_8)); + + } catch (final JoseException e) { + throw new EaafException("internal.pendingreqid.02", new Object[] { e.getMessage() }, e); + + } + + } + + @Override + public String getPendingRequestIdWithOutChecks(final String externalPendingReqId) + throws PendingReqIdValidationException { + try { + String stringToken = getDecryptedExternalPendingRequestId(externalPendingReqId); + log.debug("Token decryption successful"); + + if (!(StringUtils.countMatches(stringToken, TOKEN_SEPARATOR) == ENCODED_TOKEN_PARTS - 1)) { + log.warn("PendingRequestId has an unvalid format"); + log.debug("PendingRequestId: {}", stringToken); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.01"); + + } + + final String[] tokenElements = + StringUtils.split(stringToken, TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS); + return tokenElements[1]; + + } catch (JoseException e) { + log.warn("Token is NOT a valid String. Msg: {}", e.getMessage()); + log.debug("TokenValue: {}", externalPendingReqId); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.05", e); + + } + } + + @Override + public String validateAndGetPendingRequestId(final String externalPendingReqId) + throws PendingReqIdValidationException { + try { + String stringToken = getDecryptedExternalPendingRequestId(externalPendingReqId); + log.debug("Token decryption successful"); + + if (!(StringUtils.countMatches(stringToken, TOKEN_SEPARATOR) == ENCODED_TOKEN_PARTS - 1)) { + log.info("PendingRequestId: {}", stringToken); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.01"); + + } + + final String[] tokenElements = + StringUtils.split(stringToken, TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS); + final String internalPendingReqId = tokenElements[1]; + final DateTime timeStamp = TOKEN_TEXTUAL_DATE_FORMAT.parseDateTime(tokenElements[0]); + + log.trace("Checking valid period ... "); + final DateTime now = DateTime.now(); + if (timeStamp.withFieldAdded(DurationFieldType.seconds(), maxPendingRequestIdLifeTime) + .isBefore(now)) { + log.info("Token exceeds the valid period. Token: {} | Now: {}", timeStamp, now); + throw new PendingReqIdValidationException(internalPendingReqId, + "internal.pendingreqid.06"); + + } + log.debug("Token valid-period check successful"); + + return internalPendingReqId; + + } catch (JoseException e) { + log.warn("Token is NOT a valid encrypt. Msg: {}", e.getMessage()); + log.debug("TokenValue: {}", externalPendingReqId); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.04", e); + + } catch (final IllegalArgumentException e) { + log.warn("Token is NOT a valid String. Msg: {}", e.getMessage()); + log.debug("TokenValue: {}", externalPendingReqId); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.05", e); + + } + } + + @Nonnull + private String getDecryptedExternalPendingRequestId(String externalPendingReqId) + throws JoseException, PendingReqIdValidationException { + if (StringUtils.isEmpty(externalPendingReqId)) { + log.info("PendingReqId is 'null' or empty"); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.00"); + + } + + 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, "internal.pendingreqid.03"); + + } + + + JsonWebEncryption encToken = new JsonWebEncryption(); + encToken.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints( + ConstraintType.WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_GCM)); + encToken.setAlgorithmConstraints(new AlgorithmConstraints( + ConstraintType.WHITELIST, + KeyManagementAlgorithmIdentifiers.DIRECT, + KeyManagementAlgorithmIdentifiers.A128GCMKW + )); + encToken.setKey(key.getFirst()); + + if (key.getSecond() != null) { + final ProviderContext providerCtx = new ProviderContext(); + providerCtx.getSuppliedKeyProviderContext().setSignatureProvider( + key.getSecond().getName()); + encToken.setProviderContext(providerCtx); + + } + + encToken.setCompactSerialization(new String(externalPendingReqIdBytes, StandardCharsets.UTF_8)); + return encToken.getPayload(); + + } + + private String selectKeyWrappingAlgorithm(SecretKey first) { + if ("AES".equals(first.getAlgorithm())) { + return KeyManagementAlgorithmIdentifiers.A128GCMKW; + + } else { + return KeyManagementAlgorithmIdentifiers.DIRECT; + + } + } + + @PostConstruct + private void initialize() throws EaafConfigurationException { + log.debug("Initializing " + this.getClass().getName() + " ... "); + + maxPendingRequestIdLifeTime = + Integer.parseInt(baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME, + DEFAULT_PENDINGREQUESTID_MAX_LIFETIME)); + + + SymmetricKeyConfiguration secretKeyConfig = new SymmetricKeyConfiguration(); + secretKeyConfig.setFriendlyName(FRIENDLYNAME); + secretKeyConfig.setKeyType( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_TYPE, + SymmetricKeyType.PASSPHRASE.name())); + + secretKeyConfig.setSoftKeyPassphrase( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET)); + secretKeyConfig.setSoftKeySalt(salt); + + secretKeyConfig.setKeyStoreName( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_KEYSTORE)); + secretKeyConfig.setKeyAlias( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_ALIAS)); + + //validate symmetric-key configuration + secretKeyConfig.validate(); + + try { + key = keyStoreFactory.buildNewSymmetricKey(secretKeyConfig); + + } catch (EaafException 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 Alg: {} and maxLifeTime: {}", + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, maxPendingRequestIdLifeTime); + + } + + private String buildInternalToken(final String internalPendingReqId, final DateTime now) { + return new StringBuilder().append(TOKEN_TEXTUAL_DATE_FORMAT.print(now)).append(TOKEN_SEPARATOR) + .append(internalPendingReqId).toString(); + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EaafObjectInputStream.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EaafObjectInputStream.java new file mode 100644 index 00000000..e15c7a37 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EaafObjectInputStream.java @@ -0,0 +1,39 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidClassException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.util.List; + +import javax.annotation.Nonnull; + +public class EaafObjectInputStream extends ObjectInputStream { + + private List<String> allowedClassNames; + + /** + * Object input-stream with internal class validation. + * + * @param is Inputstream to deserialize. + * @param classNames Whitelisted classnames + * @throws IOException In case of an error + */ + public EaafObjectInputStream(@Nonnull InputStream is, @Nonnull List<String> classNames) throws IOException { + super(is); + this.allowedClassNames = classNames; + + } + + //Only deserialize instances of our expected class + @Override + protected Class<?> resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + if (!allowedClassNames.contains(desc.getName())) { + throw new InvalidClassException("Unauthorized deserialization attempt: {}",desc.getName()); + + } + return super.resolveClass(desc); + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EaafSerializationUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EaafSerializationUtils.java new file mode 100644 index 00000000..e15c6800 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EaafSerializationUtils.java @@ -0,0 +1,69 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.List; + +import org.springframework.lang.Nullable; + +public class EaafSerializationUtils { + + private EaafSerializationUtils() { + + } + + /** + * Serialize a given Java object into a byte array. + * + * @param object Java object to serialize. + * @return Serialized Java object + */ + @Nullable + public static byte[] serialize(@Nullable Object object) { + if (object == null) { + return null; + + } + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(object); + oos.flush(); + + } catch (final IOException ex) { + throw new IllegalArgumentException("Failed to serialize object of type: " + object.getClass(), ex); + + } + + return baos.toByteArray(); + } + + /** + * Deserialize the byte array into an object. + * + * @param bytes a serialized object + * @param allowedClassName List of classnames that are allowed for deserialization + * @return the result of deserializing the bytes + */ + @Nullable + public static Object deserialize(@Nullable byte[] bytes, List<String> allowedClassName) { + if (bytes == null) { + return null; + + } + + try (ObjectInputStream ois = new EaafObjectInputStream(new ByteArrayInputStream(bytes), allowedClassName)) { + return ois.readObject(); + + } catch (final IOException ex) { + throw new IllegalArgumentException("Failed to deserialize object", ex); + + } catch (final ClassNotFoundException ex) { + throw new IllegalStateException("Failed to deserialize object type", ex); + + } + } +} 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 index 99b87819..be51426c 100644 --- 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 @@ -30,12 +30,16 @@ import java.security.KeyStoreException; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; +import lombok.extern.slf4j.Slf4j; + /** * Utility for creating and loading key stores. * * @author Paul Ivancsics * @version $Id$ */ +@Slf4j public class KeyStoreUtils { /** @@ -110,6 +114,32 @@ public class KeyStoreUtils { } /** + * Loads a keyStore with known keyStore type. + * + * @param is input stream + * @param password Password protecting the keyStore + * @param keyStoreType Type of the KeyStore + * @return loaded KeyStore + * @throws IOException In case of a general error + * @throws GeneralSecurityException In case of a KeyStore access error + */ + public static KeyStore loadKeyStore(final InputStream is, final String password, KeyStoreType keyStoreType) + throws IOException, GeneralSecurityException { + String internalType = KEYSTORE_TYPE_PKCS12; + if (keyStoreType.equals(KeyStoreType.JKS)) { + internalType = KEYSTORE_TYPE_JKS; + + } else if (keyStoreType.equals(KeyStoreType.PKCS12)) { + internalType = KEYSTORE_TYPE_PKCS12; + + } + + return loadKeyStore(internalType, is, password); + + } + + + /** * Loads a keyStore without knowing the keyStore type. * * @param is input stream @@ -125,14 +155,18 @@ public class KeyStoreUtils { try { try { ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password); + } catch (final IOException e2) { is.reset(); ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password); + } + } catch (final Exception e) { - e.printStackTrace(); - + log.warn("Can not load keystore", e); + } + 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 index 0c5eeb40..b0a91e74 100644 --- 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 @@ -28,13 +28,12 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Utils to operate on Key/Value based configurations. @@ -43,8 +42,7 @@ import org.slf4j.LoggerFactory; * */ 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 = "="; @@ -154,18 +152,14 @@ public class KeyValueUtils { * null */ public static Map<String, String> removePrefixFromKeys(final Map<String, String> keys, - final String prefix) { - final Map<String, String> result = new HashMap<>(); - 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; + final String prefix) { + return keys.entrySet().stream() + .filter(el -> StringUtils.isNotEmpty(removePrefixFromKey(el.getKey(), prefix))) + .collect(Collectors.toMap( + el -> removePrefixFromKey(el.getKey(), prefix), + el -> el.getValue())); + + } /** @@ -351,19 +345,13 @@ public class KeyValueUtils { * @return Map of Key / Value pairs, but never null */ public static Map<String, String> convertListToMap(final List<String> elements) { - final Map<String, String> 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 elements.stream() + .filter(el -> el.contains(KEYVVALUEDELIMITER)) + .map(el -> el.split(KEYVVALUEDELIMITER)) + .collect(Collectors.toMap( + el -> el[0], + el -> el[1])); - return map; } /** 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 index bc770a8c..5cac4cb0 100644 --- 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 @@ -1,19 +1,14 @@ package at.gv.egiz.eaaf.core.impl.utils; -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; +import java.security.MessageDigest; 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; @@ -32,6 +27,9 @@ 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; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; /** * PendingRequestId generation strategy based on signed tokens that facilitates @@ -45,11 +43,22 @@ public class SecurePendingRequestIdGenerationStrategy private static final Logger log = LoggerFactory.getLogger(SecurePendingRequestIdGenerationStrategy.class); - @Autowired(required = true) - IConfiguration baseConfig; + @Autowired(required = true) IConfiguration baseConfig; + @Autowired EaafKeyStoreFactory keyStoreFactory; + private static final String FRIENDLYNAME = "pendingRequestId key"; + + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_TYPE = + "core.pendingrequestid.digist.type"; public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET = "core.pendingrequestid.digist.secret"; + + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_KEYSTORE = + "core.pendingrequestid.digist.keystore.name"; + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_ALIAS = + "core.pendingrequestid.digist.key.alias"; + + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM = "core.pendingrequestid.digist.algorithm"; public static final String CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME = @@ -61,43 +70,32 @@ public class SecurePendingRequestIdGenerationStrategy 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"); + DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS ZZ").withZoneUTC(); private int maxPendingRequestIdLifeTime = 300; private final int maxPendingReqIdSize = 1024; private String digistAlgorithm = null; private SecretKey key = null; - private final byte[] salt = "notRequiredInThisScenario".getBytes(Charset.defaultCharset()); + private final String salt = "notRequiredInThisScenario"; @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); - - } + 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(StandardCharsets.UTF_8)); } @Override public String getPendingRequestIdWithOutChecks(final String externalPendingReqId) throws PendingReqIdValidationException { - try { - final String[] tokenElements = extractTokens(externalPendingReqId); - return tokenElements[1]; - - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); - - } + final String[] tokenElements = extractTokens(externalPendingReqId); + return tokenElements[1]; + } @Override @@ -111,11 +109,11 @@ public class SecurePendingRequestIdGenerationStrategy 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)) { + + if (!MessageDigest.isEqual(refDigist,tokenDigest)) { log.warn("Digest of Token does NOT match"); log.debug("Token: {} | Ref: {}", tokenDigest, refDigist); - throw new PendingReqIdValidationException(null, - "Digest of pendingRequestId does NOT match"); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.04"); } log.debug("PendingRequestId HMAC digest check successful"); @@ -126,8 +124,7 @@ public class SecurePendingRequestIdGenerationStrategy .isBefore(now)) { log.warn("Token exceeds the valid period"); log.debug("Token: {} | Now: {}", timeStamp, now); - throw new PendingReqIdValidationException(internalPendingReqId, - "PendingRequestId exceeds the valid period"); + throw new PendingReqIdValidationException(internalPendingReqId, "internal.pendingreqid.06"); } log.debug("Token valid-period check successful"); @@ -137,20 +134,17 @@ public class SecurePendingRequestIdGenerationStrategy } 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); - - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.06", e); } } @NonNull private String[] extractTokens(@Nullable final String externalPendingReqId) - throws PendingReqIdValidationException, UnsupportedEncodingException { + throws PendingReqIdValidationException { if (StringUtils.isEmpty(externalPendingReqId)) { log.info("PendingReqId is 'null' or empty"); - throw new PendingReqIdValidationException(null, "PendingReqId is 'null' or empty"); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.00"); } @@ -159,12 +153,11 @@ public class SecurePendingRequestIdGenerationStrategy if (externalPendingReqIdBytes.length > maxPendingReqIdSize) { log.warn("pendingReqId size exceeds {}", maxPendingReqIdSize); - throw new PendingReqIdValidationException(null, - "pendingReqId exceeds max.size: " + maxPendingReqIdSize); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.03"); } - final String stringToken = new String(externalPendingReqIdBytes, "UTF-8"); + final String stringToken = new String(externalPendingReqIdBytes, StandardCharsets.UTF_8); if (StringUtils.countMatches(stringToken, TOKEN_SEPARATOR) == ENCODED_TOKEN_PARTS - 1) { final String[] tokenElements = StringUtils.split(stringToken, TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS); @@ -173,7 +166,7 @@ public class SecurePendingRequestIdGenerationStrategy } else { log.warn("PendingRequestId has an unvalid format"); log.debug("PendingRequestId: {}", stringToken); - throw new PendingReqIdValidationException(null, "PendingReqId has an unvalid format"); + throw new PendingReqIdValidationException(null, "internal.pendingreqid.01"); } @@ -183,13 +176,6 @@ public class SecurePendingRequestIdGenerationStrategy 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); @@ -197,12 +183,29 @@ public class SecurePendingRequestIdGenerationStrategy Integer.parseInt(baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME, DEFAULT_PENDINGREQUESTID_MAX_LIFETIME)); + + SymmetricKeyConfiguration secretKeyConfig = new SymmetricKeyConfiguration(); + secretKeyConfig.setFriendlyName(FRIENDLYNAME); + secretKeyConfig.setKeyType( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_TYPE, + SymmetricKeyType.PASSPHRASE.name())); + + secretKeyConfig.setSoftKeyPassphrase( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET)); + secretKeyConfig.setSoftKeySalt(salt); + + secretKeyConfig.setKeyStoreName( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_KEYSTORE)); + secretKeyConfig.setKeyAlias( + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_HSM_ALIAS)); + + //validate symmetric-key configuration + secretKeyConfig.validate(); + try { - final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA256"); - final KeySpec spec = new PBEKeySpec(pendingReqIdDigistSecret.toCharArray(), salt, 10000, 128); - key = keyFactory.generateSecret(spec); + key = keyStoreFactory.buildNewSymmetricKey(secretKeyConfig).getFirst(); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + } catch (EaafException 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" }, @@ -224,9 +227,9 @@ public class SecurePendingRequestIdGenerationStrategy try { final Mac mac = Mac.getInstance(digistAlgorithm); mac.init(key); - return mac.doFinal(toSign.getBytes("UTF-8")); + return mac.doFinal(toSign.getBytes(StandardCharsets.UTF_8)); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) { + } catch (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/TransactionIdUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIdUtils.java index 4c1601c0..212460d7 100644 --- 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 @@ -21,7 +21,10 @@ package at.gv.egiz.eaaf.core.impl.utils; import java.util.UUID; +import javax.annotation.Nullable; + import at.gv.egiz.eaaf.core.api.IRequest; +import lombok.extern.slf4j.Slf4j; /** * Transaction Identifier Utils. @@ -29,6 +32,7 @@ import at.gv.egiz.eaaf.core.api.IRequest; * @author tlenz * */ +@Slf4j public class TransactionIdUtils { /** @@ -58,11 +62,16 @@ public class TransactionIdUtils { * * @param pendingRequest Http request object */ - public static void setAllLoggingVariables(final IRequest pendingRequest) { - setTransactionId(pendingRequest.getUniqueTransactionIdentifier()); - setSessionId(pendingRequest.getUniqueSessionIdentifier()); - setServiceProviderId(pendingRequest.getServiceProviderConfiguration().getUniqueIdentifier()); - + public static void setAllLoggingVariables(@Nullable final IRequest pendingRequest) { + if (pendingRequest != null) { + setTransactionId(pendingRequest.getUniqueTransactionIdentifier()); + setSessionId(pendingRequest.getUniqueSessionIdentifier()); + setServiceProviderId(pendingRequest.getServiceProviderConfiguration().getUniqueIdentifier()); + + } else { + log.info("Can NOT set MDC variables from pendingRequest because it is 'null'"); + + } } /** 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 index 72c183bf..4d872ebe 100644 --- 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 @@ -1,6 +1,7 @@ package at.gv.egiz.eaaf.core.impl.utils; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.List; import javax.security.auth.x500.X500Principal; @@ -11,6 +12,18 @@ 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 certChain The first element must be the correct one. + * @return sorted Certificate Chain + */ + public static List<X509Certificate> sortCertificates(X509Certificate[] certChain) { + return sortCertificates(Arrays.asList(certChain)); + + } + + /** + * 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 */ @@ -48,4 +61,5 @@ public class X509Utils { return certs; } + } diff --git a/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties b/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties index b20c5f63..79f82af8 100644 --- a/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties +++ b/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties @@ -11,8 +11,22 @@ internal.keystore.06=KeyStore: {0} initialization failed. Reason: {1} internal.keystore.07=Validation of KeyStore: {0} failed. Reason: {1} internal.keystore.08=Can not access Key: {1} in KeyStore: {0} internal.keystore.09=Can not access Key: {1} in KeyStore: {0} Reason: {2} +internal.keystore.10=HSM-Facade NOT INITIALIZED. Find HSM-Facade class: {0} put that looks WRONG. +internal.keystore.11=KeyStore: {0} has a wrong configuration. Property: {0} Reason:{1} + +internal.key.00=Can not generate passphrase based symmetric-key: {0} Reason: {1} +internal.key.01=Can not use key from Keystore: {0} Reason: {1} internal.httpclient.00=HttpClient:{0} uses http Basic-Auth, but 'Username' is NOT set internal.httpclient.01=HttpClient:{0} uses X509 client-auth, but 'KeyStoreConfig' is NOT set internal.httpclient.02=HttpClient:{0} uses KeyStore:{1}, but 'keyPassword' is NOT set -internal.httpclient.03=Can not initialize SSLContext for HttpClient:{0} Reason:{1}
\ No newline at end of file +internal.httpclient.03=Can not initialize SSLContext for HttpClient:{0} Reason:{1} + +internal.pendingreqid.00=Process Token is 'null' or 'empty' +internal.pendingreqid.01=Process Token is NOT valid because it has an invalid format +internal.pendingreqid.02=Can not create process Token +internal.pendingreqid.03=Process Token is NOT valid because it reached maximum size +internal.pendingreqid.04=Process Token is NOT valid because it is cryptographically invalid +internal.pendingreqid.05=Process Token is NOT valid because it has an invalid encoding +internal.pendingreqid.06=Process Token is NOT valid because it exceeds the valid period + diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/logging/EaafUtilsMessageSourceTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/logging/EaafUtilsMessageSourceTest.java index 53ea54dc..125dcb09 100644 --- a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/logging/EaafUtilsMessageSourceTest.java +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/logging/EaafUtilsMessageSourceTest.java @@ -2,19 +2,21 @@ package at.gv.egiz.eaaf.core.impl.logging; import java.util.List; -import at.gv.egiz.eaaf.core.api.logging.IMessageSourceLocation; - import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import at.gv.egiz.eaaf.core.api.logging.IMessageSourceLocation; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/spring/test_eaaf_pvp_not_lazy.beans.xml") +@DirtiesContext public class EaafUtilsMessageSourceTest { @Autowired diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyTest.java new file mode 100644 index 00000000..93ef17b9 --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyTest.java @@ -0,0 +1,447 @@ +package at.gv.egiz.eaaf.core.impl.utils.test; + +import java.io.UnsupportedEncodingException; +import java.security.Provider; +import java.util.Base64; + +import javax.crypto.SecretKey; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.ReadableInstant; +import org.joda.time.format.DateTimeFormat; +import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers; +import org.jose4j.jwe.JsonWebEncryption; +import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers; +import org.jose4j.lang.JoseException; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.AuthenticatedEncryptionPendingRequestIdGenerationStrategy; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("/spring/test_eaaf_pvp_not_lazy.beans.xml") +@DirtiesContext +public class AuthenticatedEncryptionPendingRequestIdGenerationStrategyTest { + + @Autowired private EaafKeyStoreFactory keyStoreFactory; + @Autowired private AuthenticatedEncryptionPendingRequestIdGenerationStrategy pendingIdStrategy; + + + @Test + public void generatePendingRequestId() throws EaafException { + String pendingId = pendingIdStrategy.generateExternalPendingRequestId(); + Assert.assertNotNull("pendingId", pendingId); + + } + + @Test + public void validatePendingRequestId() throws EaafException { + String extPendingId = pendingIdStrategy.generateExternalPendingRequestId(); + Assert.assertNotNull("external pendingId", extPendingId); + + + String pendingId = pendingIdStrategy.validateAndGetPendingRequestId(extPendingId); + Assert.assertNotNull("internal pendingId", pendingId); + + String pendingId2 = pendingIdStrategy.getPendingRequestIdWithOutChecks(extPendingId); + Assert.assertNotNull("internal pendingId", pendingId2); + + Assert.assertEquals("pendingId not match", pendingId, pendingId2); + + } + + @Test + public void nullPendingRequestId() { + try { + pendingIdStrategy.validateAndGetPendingRequestId(null); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.00", e.getErrorId()); + + } + } + + @Test + public void emptyPendingRequestId() { + try { + pendingIdStrategy.validateAndGetPendingRequestId(StringUtils.EMPTY); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.00", e.getErrorId()); + + } + } + + @Test + public void noBase64UrlPendingRequestId() { + try { + pendingIdStrategy.validateAndGetPendingRequestId(RandomStringUtils.randomAlphanumeric(25)); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.05", e.getErrorId()); + + } + } + + @Test + public void toLongBase64UrlPendingRequestId() { + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(RandomStringUtils.randomAlphanumeric(1100).getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.03", e.getErrorId()); + + } + } + + @Test + public void wrongFormat() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.01", e.getErrorId()); + + } + } + + @Test + public void wrongFormatToLong() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25) + "|" + + RandomStringUtils.randomAlphanumeric(25) + "|" + "aabbcc"; + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.01", e.getErrorId()); + + } + } + + @Test + public void wrongFormatNoDate() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25) + "|" + + RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.05", e.getErrorId()); + + } + } + + @Test + public void wrongFormatWrongDate() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = "2020-01-01 12:01:55 111 +00:00" + "|" + + RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNotNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.06", e.getErrorId()); + + } + } + + @Test + public void wrongFormatNotValidation() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.getPendingRequestIdWithOutChecks(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.01", e.getErrorId()); + + } + } + + @Test + public void wrongFormatToLongNotValidation() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25) + "|" + + RandomStringUtils.randomAlphanumeric(25) + "|" + "aabbcc"; + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.getPendingRequestIdWithOutChecks(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.01", e.getErrorId()); + + } + } + + @Test + public void wrongFormatNoDateNotValidation() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25) + "|" + + RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + + String intPendingId = pendingIdStrategy.getPendingRequestIdWithOutChecks(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.assertNotNull("Int PendingId", intPendingId); + + } + + @Test + public void wrongFormatWrongDateNotValidation() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = "2020-01-01 12:01:55 111" + "|" + + RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + + String intPendingId = pendingIdStrategy.getPendingRequestIdWithOutChecks(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.assertNotNull("Int PendingId", intPendingId); + + + } + + @Test + public void validFormat() throws EaafException, JoseException, UnsupportedEncodingException { + String intId = RandomStringUtils.randomAlphanumeric(25); + ReadableInstant now = DateTime.now(); + String payLoad = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS").print(now) + + "|" + intId; + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + + String intPendingId = pendingIdStrategy.getPendingRequestIdWithOutChecks(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.assertNotNull("Int PendingId", intPendingId); + Assert.assertEquals("pendingId not match", intId, intPendingId); + + } + + @Test + public void validFormatNotValidation() throws EaafException, JoseException, UnsupportedEncodingException { + String intId = RandomStringUtils.randomAlphanumeric(25); + String payLoad = "2020-01-01 12:01:55 111" + + "|" + intId; + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + String intPendingId = pendingIdStrategy.getPendingRequestIdWithOutChecks(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.assertNotNull("Int PendingId", intPendingId); + + } + + @Test + public void validFormatWrongDateNotValidation() throws EaafException, JoseException, UnsupportedEncodingException { + String intId = RandomStringUtils.randomAlphanumeric(25); + String payLoad = "2020-01-01 12:01:55 111" + "|" + + intId; + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "pendingReqIdSecret"); + + + String intPendingId = pendingIdStrategy.getPendingRequestIdWithOutChecks(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.assertNotNull("Int PendingId", intPendingId); + Assert.assertEquals("pendingId not match", intId, intPendingId); + + + } + + @Test + public void wrongEncrypted() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.DIRECT, + ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, + "wrongPassword"); + + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "internal.pendingreqid.04", e.getErrorId()); + + } + } + + @Ignore + @Test + public void wrongEncryptionAlg() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.A256KW, + ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "process.99", e.getErrorId()); + Assert.assertEquals("Wrong errorMsg", + "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, " + + "PendingReqId has an unvalid format]", + e.getMessage()); + + } + } + + @Ignore + @Test + public void wrongKeyEncAlg() throws EaafException, JoseException, UnsupportedEncodingException { + String payLoad = RandomStringUtils.randomAlphanumeric(25); + + String extPendingId = generateEncryptedPendingId(payLoad, + KeyManagementAlgorithmIdentifiers.A128KW, + ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256, + "pendingReqIdSecret"); + + try { + pendingIdStrategy.validateAndGetPendingRequestId(Base64.getUrlEncoder() + .encodeToString(extPendingId.getBytes())); + Assert.fail("Wrong pendingId not detected"); + + } catch (PendingReqIdValidationException e) { + Assert.assertNull("internal pendingReqId", e.getInvalidInternalPendingReqId()); + Assert.assertNull("internal pendingReq", e.getInvalidPendingReq()); + Assert.assertEquals("Wrong errorId", "process.99", e.getErrorId()); + Assert.assertEquals("Wrong errorMsg", + "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, " + + "PendingReqId is NOT a valid String]", + e.getMessage()); + + } + } + + private String generateEncryptedPendingId(String payLoad, String direct, String aes128Gcm, String softKeyPassphrase) + throws EaafException, JoseException, UnsupportedEncodingException { + SymmetricKeyConfiguration config = new SymmetricKeyConfiguration(); + config.setFriendlyName("jUnit"); + config.setKeyType(SymmetricKeyType.PASSPHRASE); + config.setSoftKeySalt("notRequiredInThisScenario"); + config.setSoftKeyPassphrase(softKeyPassphrase); + Pair<SecretKey, Provider> key = keyStoreFactory.buildNewSymmetricKey(config); + + JsonWebEncryption encToken = new JsonWebEncryption(); + encToken.setAlgorithmHeaderValue(direct); + encToken.setEncryptionMethodHeaderParameter(aes128Gcm); + encToken.setKey(key.getFirst()); + encToken.setPayload(payLoad); + + return encToken.getCompactSerialization(); + + } + +} diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyWithHsmTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyWithHsmTest.java new file mode 100644 index 00000000..b588bb3a --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyWithHsmTest.java @@ -0,0 +1,44 @@ +package at.gv.egiz.eaaf.core.impl.utils.test; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.utils.AuthenticatedEncryptionPendingRequestIdGenerationStrategy; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("/spring/test_eaaf_pvp_not_lazy_with_hsm.beans.xml") +@DirtiesContext +public class AuthenticatedEncryptionPendingRequestIdGenerationStrategyWithHsmTest { + + @Autowired private AuthenticatedEncryptionPendingRequestIdGenerationStrategy pendingIdStrategy; + + @Test + public void generatePendingRequestId() throws EaafException { + String pendingId = pendingIdStrategy.generateExternalPendingRequestId(); + Assert.assertNotNull("pendingId", pendingId); + + } + + @Test + public void validatePendingRequestId() throws EaafException { + String extPendingId = pendingIdStrategy.generateExternalPendingRequestId(); + Assert.assertNotNull("external pendingId", extPendingId); + + + String pendingId = pendingIdStrategy.validateAndGetPendingRequestId(extPendingId); + Assert.assertNotNull("internal pendingId", pendingId); + + String pendingId2 = pendingIdStrategy.getPendingRequestIdWithOutChecks(extPendingId); + Assert.assertNotNull("internal pendingId", pendingId2); + + Assert.assertEquals("pendingId not match", pendingId, pendingId2); + + } + +} diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/KeyValueUtilsTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/KeyValueUtilsTest.java index 58788392..ca90f05b 100644 --- a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/KeyValueUtilsTest.java +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/KeyValueUtilsTest.java @@ -153,7 +153,8 @@ public class KeyValueUtilsTest { + RandomStringUtils.randomAlphabetic(6) + KeyValueUtils.KEY_DELIMITER + RandomStringUtils.randomAlphabetic(5); final Map<String, String> testMap = generateTestMap(testPrefix, 5, 5); - + testMap.put(testPrefix, RandomStringUtils.randomAlphabetic(10)); + final Map<String, String> result = KeyValueUtils.removePrefixFromKeys(testMap, testPrefix); Assert.assertNotNull("Result is null", result); Assert.assertFalse("Result is empty", result.isEmpty()); diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/builder/BpkBuilderTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/builder/BpkBuilderTest.java new file mode 100644 index 00000000..bccab09f --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/builder/BpkBuilderTest.java @@ -0,0 +1,562 @@ +package at.gv.egiz.eaaf.core.test.builder; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.ECGenParameterSpec; + +import org.apache.commons.lang3.RandomStringUtils; +import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers; +import org.jose4j.jwe.JsonWebEncryption; +import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers; +import org.jose4j.lang.JoseException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import at.gv.egiz.eaaf.core.api.data.EaafConstants; +import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException; +import at.gv.egiz.eaaf.core.impl.builder.BpkBuilder; +import at.gv.egiz.eaaf.core.impl.data.Pair; + +@RunWith(BlockJUnit4ClassRunner.class) +public class BpkBuilderTest { + + private static final String BASEID = "RUxHQVRlc3RQQjBYWFjFkHpnw7xyX1hYWFTDvHpla8OnaQ=="; + + private KeyPair keyPair; + + + /** + * jUnit test initializer. + * @throws NoSuchProviderException In case of an error + * @throws NoSuchAlgorithmException In case of an error + */ + @Before + public void initialize() throws NoSuchAlgorithmException, NoSuchProviderException { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + keyPair = keyGen.generateKeyPair(); + + } + + @Test + public void encBpkTextualLength() throws EaafBuilderException, InvalidKeyException, NoSuchAlgorithmException, + NoSuchProviderException, InvalidAlgorithmParameterException, JoseException { + String bpk = "MDEyMzQ1Njc4OWFiY2RIZg+CU"; + String target = EaafConstants.URN_PREFIX_CDID + "AA"; + + printResult("Legacy RSA 1024:", BpkBuilder.encryptBpk(bpk, target, generateRsaPubKey(1024))); + printResult("Legacy RSA 2048:", BpkBuilder.encryptBpk(bpk, target, generateRsaPubKey(2048))); + printResult("Legacy RSA 3072:", BpkBuilder.encryptBpk(bpk, target, generateRsaPubKey(3072))); + printResult("Legacy RSA 4096:", BpkBuilder.encryptBpk(bpk, target, generateRsaPubKey(4096))); + + + bpk = "V1::urn:publicid:gv.at:cdid+BW::MDEyMzQ1Njc 4OW FiY2RIZg+CU&g=::2004-01-22T20:57:12"; + + printResult("RSA 2048:", createJsonEnc(generateRsaPubKey(2048), bpk, target, + KeyManagementAlgorithmIdentifiers.RSA_OAEP_256)); + printResult("RSA 3072:", createJsonEnc(generateRsaPubKey(3072), bpk, target, + KeyManagementAlgorithmIdentifiers.RSA_OAEP_256)); + printResult("RSA 4096:", createJsonEnc(generateRsaPubKey(4048), bpk, target, + KeyManagementAlgorithmIdentifiers.RSA_OAEP_256)); + + printResult("ECC 256:", createJsonEnc(generateEcPubKey("secp256r1"), bpk, target, + KeyManagementAlgorithmIdentifiers.ECDH_ES_A128KW)); + printResult("ECC 384:", createJsonEnc(generateEcPubKey("secp384r1"), bpk, target, + KeyManagementAlgorithmIdentifiers.ECDH_ES_A128KW)); + printResult("ECC 521:", createJsonEnc(generateEcPubKey("secp521r1"), bpk, target, + KeyManagementAlgorithmIdentifiers.ECDH_ES_A128KW)); + + System.out.println("Finished!"); + } + + private void printResult(String prefix, String body) { + System.out.println(prefix + " " + body.length() + " full:" + body); + + } + + private String createJsonEnc(PublicKey pubKey, String bpk, String target, String keyWrapAlg) throws JoseException { + JsonWebEncryption enc = new JsonWebEncryption(); + enc.setKey(pubKey); + enc.setPayload(bpk); + enc.setAlgorithmHeaderValue(keyWrapAlg); + enc.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM); + enc.setKeyIdHeaderValue("myFirstKey"); + enc.setContentTypeHeaderValue(target); + return enc.getCompactSerialization(); + + } + + private PublicKey generateRsaPubKey(int size) throws NoSuchAlgorithmException { + KeyPairGenerator keyGen3 = KeyPairGenerator.getInstance("RSA"); + keyGen3.initialize(size); + return keyGen3.generateKeyPair().getPublic(); + + } + + private PublicKey generateEcPubKey(String curve) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidAlgorithmParameterException { + KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); + ECGenParameterSpec ecSpec = new ECGenParameterSpec(curve); + generator.initialize(ecSpec, new SecureRandom()); + return generator.generateKeyPair().getPublic(); + + } + + @Test + public void encBpkWrongTarget() throws InvalidKeyException { + String bpk = RandomStringUtils.randomAlphanumeric(25); + String target = RandomStringUtils.randomAlphanumeric(25); + + try { + BpkBuilder.encryptBpk(bpk, target, keyPair.getPublic()); + Assert.fail("Wrong parameters not detected"); + + } catch (EaafBuilderException e) { + Assert.assertEquals("Wrong errorMsg", "builder.32", e.getErrorId()); + + } + } + + @Test + public void decBpkWrongTarget() throws InvalidKeyException { + String bpk = RandomStringUtils.randomAlphanumeric(25); + String target = RandomStringUtils.randomAlphanumeric(25); + + try { + BpkBuilder.decryptBpk(bpk, target, keyPair.getPrivate()); + Assert.fail("Wrong parameters not detected"); + + } catch (EaafBuilderException e) { + Assert.assertEquals("Wrong errorMsg", "builder.32", e.getErrorId()); + + } + } + + @Test + public void decBpkWrongTargetInEncBpk() throws InvalidKeyException, EaafBuilderException { + String bpk = RandomStringUtils.randomAlphanumeric(25); + String target = EaafConstants.URN_PREFIX_CDID + "AA"; + + String encBpk = BpkBuilder.encryptBpk(bpk, target, keyPair.getPublic()); + try { + BpkBuilder.decryptBpk(encBpk, + EaafConstants.URN_PREFIX_CDID + "BB", keyPair.getPrivate()); + Assert.fail("Wrong parameters not detected"); + + } catch (EaafBuilderException e) { + Assert.assertEquals("Wrong errorMsg", "builder.30", e.getErrorId()); + + } + } + + @Test + public void encBpkSuccess() throws EaafBuilderException, InvalidKeyException { + String bpk = RandomStringUtils.randomAlphanumeric(25); + String target = EaafConstants.URN_PREFIX_CDID + "AA"; + + String encBpk = BpkBuilder.encryptBpk(bpk, target, keyPair.getPublic()); + + Assert.assertNotNull("encBpk", encBpk); + + Pair<String, String> decBpk = BpkBuilder.decryptBpk(encBpk, target, keyPair.getPrivate()); + + Assert.assertEquals("wrong bBK", bpk, decBpk.getFirst()); + Assert.assertEquals("wrong bBK-Target", target, decBpk.getSecond()); + + } + + @Test + public void encWbpkSuccess() throws EaafBuilderException, InvalidKeyException { + String bpk = RandomStringUtils.randomAlphanumeric(25); + String target = EaafConstants.URN_PREFIX_WBPK + "XFN+123456i"; + + String encBpk = BpkBuilder.encryptBpk(bpk, target, keyPair.getPublic()); + + Assert.assertNotNull("encBpk", encBpk); + + Pair<String, String> decBpk = BpkBuilder.decryptBpk(encBpk, target, keyPair.getPrivate()); + + Assert.assertEquals("wrong bBK", bpk, decBpk.getFirst()); + Assert.assertEquals("wrong bBK-Target", target, decBpk.getSecond()); + + } + + @Test + public void encWbpkSuccessSecond() throws EaafBuilderException, InvalidKeyException { + String bpk = RandomStringUtils.randomAlphanumeric(25); + String target = EaafConstants.URN_PREFIX_WBPK + "FN+123456i"; + + String encBpk = BpkBuilder.encryptBpk(bpk, target, keyPair.getPublic()); + + Assert.assertNotNull("encBpk", encBpk); + + Pair<String, String> decBpk = BpkBuilder.decryptBpk(encBpk, + EaafConstants.URN_PREFIX_WBPK + "XFN+123456i", keyPair.getPrivate()); + + Assert.assertEquals("wrong bBK", bpk, decBpk.getFirst()); + Assert.assertEquals("wrong bBK-Target", + EaafConstants.URN_PREFIX_WBPK + "XFN+123456i", decBpk.getSecond()); + + } + + + @Test + public void noBaseId() { + try { + BpkBuilder.generateAreaSpecificPersonIdentifier(null, EaafConstants.URN_PREFIX_CDID + "AA"); + + } catch (EaafBuilderException e) { + Assert.assertEquals("Wrong errorCode", "builder.33", e.getErrorId()); + } + } + + @Test + public void noTarget() { + try { + BpkBuilder.generateAreaSpecificPersonIdentifier(BASEID, null); + + } catch (EaafBuilderException e) { + Assert.assertEquals("Wrong errorCode", "builder.33", e.getErrorId()); + } + } + + @Test + public void noBaseIdType() { + try { + BpkBuilder.generateAreaSpecificPersonIdentifier(BASEID, + null, EaafConstants.URN_PREFIX_CDID + "AA"); + + } catch (EaafBuilderException e) { + Assert.assertEquals("Wrong errorCode", "builder.33", e.getErrorId()); + } + } + + @Test + public void wrongBaseIdType() { + try { + BpkBuilder.generateAreaSpecificPersonIdentifier(BASEID, + EaafConstants.URN_PREFIX_CDID + "BB", EaafConstants.URN_PREFIX_CDID + "AA"); + + } catch (EaafBuilderException e) { + Assert.assertEquals("Wrong errorCode", "builder.33", e.getErrorId()); + } + } + + @Test + public void baseIdTypeEqualsTarget() throws EaafBuilderException { + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier(BASEID, + EaafConstants.URN_PREFIX_CDID + "AA", EaafConstants.URN_PREFIX_CDID + "AA"); + + Assert.assertEquals("first bPK", BASEID, + result1.getFirst()); + Assert.assertEquals("first bPK", "urn:publicid:gv.at:cdid+AA", + result1.getSecond()); + + } + + @Test + public void buildBpk() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_CDID + "AA"); + Pair<String, String> result2 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_CDID + "BB"); + + Assert.assertEquals("first bPK", "b1Ip610zZq/Or/uCqgb51lnAdZM=", + result1.getFirst()); + Assert.assertEquals("first bPK", "urn:publicid:gv.at:cdid+AA", + result1.getSecond()); + + Assert.assertEquals("second bPK", "uYst6hjKJvyp7s/ezD8zsnkcj9k=", + result2.getFirst()); + Assert.assertEquals("second bPK", "urn:publicid:gv.at:cdid+BB", + result2.getSecond()); + + } + + @Test + public void buildWbpkFn() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_WBPK + "FN+123456i"); + + Assert.assertEquals("wbPK", "k65HRxpVcoZ2OPZHo3j2LEn/JQE=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:wbpk+XFN+123456i", + result1.getSecond()); + + } + + @Test + public void buildWbpkZvr() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_WBPK + "ZVR+123456"); + + Assert.assertEquals("wbPK", "1WvaBLiTxcc3kVzfB71Zh2sCtvA=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:wbpk+XZVR+123456", + result1.getSecond()); + + } + + @Test + public void buildWbpkErsb() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_WBPK + "ERSB+123456"); + + Assert.assertEquals("wbPK", "xtAWGAiblvhYJiCpUB3dwdRFPpg=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:wbpk+XERSB+123456", + result1.getSecond()); + + } + + @Test + public void buildWbpkXFn() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_WBPK + "XFN+123456i"); + + Assert.assertEquals("wbPK", "k65HRxpVcoZ2OPZHo3j2LEn/JQE=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:wbpk+XFN+123456i", + result1.getSecond()); + + } + + @Test + public void buildWbpkXZvr() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_WBPK + "XZVR+123456"); + + Assert.assertEquals("wbPK", "1WvaBLiTxcc3kVzfB71Zh2sCtvA=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:wbpk+XZVR+123456", + result1.getSecond()); + + } + + @Test + public void buildWbpkXErsb() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_WBPK + "XERSB+123456"); + + Assert.assertEquals("wbPK", "xtAWGAiblvhYJiCpUB3dwdRFPpg=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:wbpk+XERSB+123456", + result1.getSecond()); + + } + + @Test + public void buildWbpkOthers() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_WBPK + "XABC+123456"); + + Assert.assertEquals("wbPK", "wv96/xKUyi6YoYGv7IcIlFTsJIk=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:wbpk+XABC+123456", + result1.getSecond()); + + } + + @Test + public void buildEidasId() throws EaafBuilderException { + + Pair<String, String> result1 = BpkBuilder.generateAreaSpecificPersonIdentifier( + BASEID, EaafConstants.URN_PREFIX_EIDAS + "AT+ES"); + + Assert.assertEquals("eidas", "AT/ES/7AuLZNKsiRr97yvLsQ16SZ6r0q0=", + result1.getFirst()); + Assert.assertEquals("wbPK", "urn:publicid:gv.at:eidasid+AT+ES", + result1.getSecond()); + + } + + @Test + public void normalizeNullTarget() { + Assert.assertNull("Wrong normalized target", + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(null)); + + } + + @Test + public void normalizeBpkTarget() { + String target = EaafConstants.URN_PREFIX_CDID + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong normalized target", + target, + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(target)); + + } + + @Test + public void normalizeWbpkTargetWithX() { + String target = EaafConstants.URN_PREFIX_WBPK_TARGET_WITH_X + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong normalized target", + target, + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(target)); + + } + + @Test + public void normalizeWbpkTargetWithOutXNoMapping() { + String target = EaafConstants.URN_PREFIX_WBPK + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong normalized target", + target, + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(target)); + + } + + @Test + public void normalizeWbpkTargetWithOutXMappingFn() { + Assert.assertEquals("Wrong normalized target", + EaafConstants.URN_PREFIX_WBPK + "XFN+123456i", + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(EaafConstants.URN_PREFIX_WBPK + "FN+123456i")); + + } + + @Test + public void normalizeWbpkTargetWithOutXMappingZvr() { + Assert.assertEquals("Wrong normalized target", + EaafConstants.URN_PREFIX_WBPK + "XZVR+1122334455", + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(EaafConstants.URN_PREFIX_WBPK + "ZVR+1122334455")); + + } + + @Test + public void normalizeWbpkTargetWithOutXMappingErsb() { + Assert.assertEquals("Wrong normalized target", + EaafConstants.URN_PREFIX_WBPK + "XERSB+998877665544", + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(EaafConstants.URN_PREFIX_WBPK + "ERSB+998877665544")); + + } + + @Test + public void normalizeEidasTarget() { + String target = EaafConstants.URN_PREFIX_EIDAS + RandomStringUtils.randomAlphabetic(2) + + "+" + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong normalized target", + target, + BpkBuilder.normalizeBpkTargetIdentifierToCommonFormat(target)); + + } + + @Test + public void calcNormalizeNullTarget() { + Assert.assertNull("Wrong normalized target", + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat(null)); + + } + + @Test + public void calcNormalizeBpkTarget() { + String target = EaafConstants.URN_PREFIX_CDID + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong normalized target", + target, + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat(target)); + + } + + @Test + public void calcNormalizeWbpkTargetWithoutX() { + + Assert.assertEquals("Wrong normalized target", + EaafConstants.URN_PREFIX_WBPK + "FN+123456i", + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat(EaafConstants.URN_PREFIX_WBPK + "FN+123456i")); + + } + + @Test + public void calcNormalizeWbpkTargetWithOutXNoMapping() { + String target = EaafConstants.URN_PREFIX_WBPK + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong normalized target", + target, + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat(target)); + + } + + @Test + public void calcNormalizeWbpkTargetWithXMappingFn() { + Assert.assertEquals("Wrong normalized target", + EaafConstants.URN_PREFIX_WBPK + "FN+123456i", + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat(EaafConstants.URN_PREFIX_WBPK + "XFN+123456i")); + + } + + @Test + public void calcNormalizeWbpkTargetWithXMappingZvr() { + Assert.assertEquals("Wrong normalized target", + EaafConstants.URN_PREFIX_WBPK + "ZVR+1122334455", + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat(EaafConstants.URN_PREFIX_WBPK + "XZVR+1122334455")); + + } + + @Test + public void calcNormalizeWbpkTargetWithXMappingErsb() { + Assert.assertEquals("Wrong normalized target", + EaafConstants.URN_PREFIX_WBPK + "ERSB+998877665544", + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat( + EaafConstants.URN_PREFIX_WBPK + "XERSB+998877665544")); + + } + + @Test + public void calcNormalizeEidasTarget() { + String target = EaafConstants.URN_PREFIX_EIDAS + RandomStringUtils.randomAlphabetic(2) + + "+" + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong normalized target", + target, + BpkBuilder.normalizeBpkTargetIdentifierToNonXFormat(target)); + + } + + @Test + public void removeBpkPrefix() { + String spTarget = RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong SP target without prefix", + spTarget, + BpkBuilder.removeBpkTypePrefix(EaafConstants.URN_PREFIX_CDID + spTarget)); + + } + + @Test + public void removeWpbkPrefix() { + String spTarget = RandomStringUtils.randomAlphabetic(10); + Assert.assertEquals("Wrong SP target without prefix", + spTarget, + BpkBuilder.removeBpkTypePrefix(EaafConstants.URN_PREFIX_WBPK + spTarget)); + + } + + @Test + public void removeEidasPbkPrefix() { + String spTarget = RandomStringUtils.randomAlphabetic(2) + "+" + RandomStringUtils.randomAlphabetic(2); + Assert.assertEquals("Wrong SP target without prefix", + spTarget, + BpkBuilder.removeBpkTypePrefix(EaafConstants.URN_PREFIX_EIDAS + spTarget)); + + } + + @Test + public void removeUnknownPbkPrefix() { + String spTarget = RandomStringUtils.randomAlphabetic(10); + Assert.assertEquals("Wrong SP target without prefix", + EaafConstants.URN_PREFIX_BASEID + spTarget, + BpkBuilder.removeBpkTypePrefix(EaafConstants.URN_PREFIX_BASEID + spTarget)); + + } +} diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/EaafKeyStoreFactoryTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/EaafKeyStoreFactoryTest.java index cefb1e7e..3e82c510 100644 --- a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/EaafKeyStoreFactoryTest.java +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/EaafKeyStoreFactoryTest.java @@ -4,18 +4,23 @@ import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.Provider; +import java.security.Security; import java.security.cert.X509Certificate; import java.util.List; +import javax.crypto.SecretKey; + import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.annotation.DirtiesContext.MethodMode; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -25,6 +30,7 @@ import com.google.common.base.Predicates; import com.google.common.base.Throwables; import com.google.common.collect.FluentIterable; +import at.asitplus.hsmfacade.provider.HsmFacadeProvider; import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.exceptions.EaafException; @@ -33,13 +39,15 @@ import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap; import io.grpc.StatusRuntimeException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/spring/test_eaaf_pvp_lazy.beans.xml") -@DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) +@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD) public class EaafKeyStoreFactoryTest { private static final String HSM_FACASE_HOST = "eid.a-sit.at"; @@ -66,14 +74,15 @@ public class EaafKeyStoreFactoryTest { /** * jUnit test set-up. */ - @Before + @Before public void testSetup() { mapConfig.clearAllConfig(); - + Security.removeProvider(HsmFacadeProvider.getInstance().getName()); + } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void startWithoutConfigHsmFacadeConfig() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -81,7 +90,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void buildyStoreWithOutConfig() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -99,7 +108,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void buildyStoreWithPkcs11() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -118,7 +127,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreWithoutConfig() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -137,7 +146,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreWithoutConfigSecond() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -156,7 +165,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreWithoutPassword() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -177,7 +186,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreWithoutPath() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -199,7 +208,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreWithoutType() throws EaafException { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -217,7 +226,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreWithWrongPath() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -234,13 +243,13 @@ public class EaafKeyStoreFactoryTest { } catch (final EaafException e) { org.springframework.util.Assert.isInstanceOf(EaafConfigurationException.class, e, "Wong ExceptionType"); - Assert.assertEquals("wrong errorCode", "internal.keystore.05", e.getErrorId()); + Assert.assertEquals("wrong errorCode", "internal.keystore.06", e.getErrorId()); } } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreWithWrongPassword() { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -263,7 +272,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreSuccessJks() throws EaafException { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -280,10 +289,13 @@ public class EaafKeyStoreFactoryTest { Assert.assertNotNull("KeyStore is null", keyStore.getFirst()); Assert.assertNull("KeyStore is null", keyStore.getSecond()); + Assert.assertEquals("Wrong HSM-Facade state", EaafKeyStoreFactory.HsmFacadeStatus.UNKNOWN, + keyStoreFactory.checkHsmFacadeStatus()); + } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreAccessOperations() throws EaafException, KeyStoreException { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -352,7 +364,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void softwareKeyStoreSuccessPkcs12() throws EaafException { final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); @@ -372,6 +384,75 @@ public class EaafKeyStoreFactoryTest { } @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void symmetricSoftwareKeyWithOutConfig() { + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.PASSPHRASE); + try { + keyStoreFactory.buildNewSymmetricKey(keyConfig); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + org.springframework.util.Assert.isInstanceOf(EaafConfigurationException.class, e, "Wong ExceptionType"); + Assert.assertEquals("wrong errorCode", "internal.key.00", e.getErrorId()); + + } + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void symmetricSoftwareKeyWithOutSalt() { + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.PASSPHRASE); + keyConfig.setSoftKeyPassphrase(RandomStringUtils.randomAlphanumeric(10)); + try { + keyStoreFactory.buildNewSymmetricKey(keyConfig); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + org.springframework.util.Assert.isInstanceOf(EaafConfigurationException.class, e, "Wong ExceptionType"); + Assert.assertEquals("wrong errorCode", "internal.key.00", e.getErrorId()); + + } + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void symmetricSoftwareKeyValid() throws EaafException { + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertFalse("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.PASSPHRASE); + keyConfig.setSoftKeyPassphrase(RandomStringUtils.randomAlphanumeric(10)); + keyConfig.setSoftKeySalt(RandomStringUtils.randomAlphanumeric(10)); + + Pair<SecretKey, Provider> key = keyStoreFactory.buildNewSymmetricKey(keyConfig); + Assert.assertNotNull("Key container is null", key); + Assert.assertNotNull("Key is null", key.getFirst()); + Assert.assertNull("Provider is not null", key.getSecond()); + + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void hsmFacadeNoHostConfig() { + context.getBean(EaafKeyStoreFactory.class); + + } + + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeOnlyHostConfig() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -386,6 +467,7 @@ public class EaafKeyStoreFactoryTest { } @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeMissingPort() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -405,6 +487,7 @@ public class EaafKeyStoreFactoryTest { } @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeMissingUsername() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -423,6 +506,7 @@ public class EaafKeyStoreFactoryTest { } @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeMissingPassword() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -442,6 +526,7 @@ public class EaafKeyStoreFactoryTest { } @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeMissingTrustedCertificate() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -463,6 +548,7 @@ public class EaafKeyStoreFactoryTest { } @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeMissingTrustedCertificateFile() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -485,7 +571,8 @@ public class EaafKeyStoreFactoryTest { } } - @Test + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeMissingWrongTrustedCertificate() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -508,8 +595,35 @@ public class EaafKeyStoreFactoryTest { } } + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void hsmFacadeWrongGrpcDeadlineParameter() { + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, + RandomStringUtils.randomNumeric(10)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_PORT, + RandomStringUtils.randomNumeric(4)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME, + RandomStringUtils.randomNumeric(10)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD, + RandomStringUtils.randomAlphanumeric(10)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_SSLTRUST, + "src/test/resources/spring/test_eaaf_pvp_lazy.beans.xml"); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_GRPC_DEADLINE, + RandomStringUtils.randomAlphabetic(5)); + + try { + context.getBean(EaafKeyStoreFactory.class); + Assert.fail("Missing HSM Facade not detected"); + + } catch (final BeansException e) { + checkMissingConfigException(e, "internal.keystore.05"); + + } + } + + @Ignore @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeInitialized() { mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, RandomStringUtils.randomNumeric(10)); @@ -521,14 +635,54 @@ public class EaafKeyStoreFactoryTest { RandomStringUtils.randomAlphanumeric(10)); mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_SSLTRUST, PATH_TO_HSM_FACADE_TRUST_CERT); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_GRPC_DEADLINE, + RandomStringUtils.randomNumeric(2)); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + Assert.assertEquals("Wrong HSM-Facade state", EaafKeyStoreFactory.HsmFacadeStatus.UP, + keyStoreFactory.checkHsmFacadeStatus()); + + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void hsmFacadeHealthCheckNoProvider() { + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, + RandomStringUtils.randomNumeric(10)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_PORT, + RandomStringUtils.randomNumeric(4)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME, + RandomStringUtils.randomNumeric(10)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD, + RandomStringUtils.randomAlphanumeric(10)); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_SSLTRUST, + PATH_TO_HSM_FACADE_TRUST_CERT); final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + Security.removeProvider("HsmFacade"); + Assert.assertEquals("Wrong HSM-Facade state", EaafKeyStoreFactory.HsmFacadeStatus.DOWN, + keyStoreFactory.checkHsmFacadeStatus()); } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void hsmFacadeAlreadLoaded() { + HsmFacadeProvider provider = HsmFacadeProvider.getInstance(); + Security.addProvider(provider); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + Assert.assertEquals("Wrong HSM-Facade state", EaafKeyStoreFactory.HsmFacadeStatus.UP, + keyStoreFactory.checkHsmFacadeStatus()); + } + @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeKeyStoreNoKeyStoreName() { configureHsmFacade(); @@ -550,7 +704,7 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) public void hsmFacadeKeyStoreSuccess() throws EaafException { configureHsmFacade(); @@ -578,13 +732,106 @@ public class EaafKeyStoreFactoryTest { } @Test - @DirtiesContext - public void hsmFacadeKeyStoreSuccessASitTestFacade() throws EaafException, KeyStoreException { + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void symmetricHsmFacadeKeyWithOutConfig() { configureHsmFacade(); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.HSMFACADE); + try { + keyStoreFactory.buildNewSymmetricKey(keyConfig); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + org.springframework.util.Assert.isInstanceOf(EaafConfigurationException.class, e, "Wong ExceptionType"); + Assert.assertEquals("wrong errorCode", "internal.keystore.06", e.getErrorId()); + + } + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void symmetricHsmFacadeKeyWithOutKeyAlias() { + configureHsmFacade(); + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.HSMFACADE); + keyConfig.setKeyStoreName("authhandler"); + try { + keyStoreFactory.buildNewSymmetricKey(keyConfig); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + org.springframework.util.Assert.isInstanceOf(EaafConfigurationException.class, e, "Wong ExceptionType"); + Assert.assertEquals("wrong errorCode", "internal.key.00", e.getErrorId()); + + } + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void symmetricHsmFacadeKeyWrongKeyAlias() { + configureHsmFacade(); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.HSMFACADE); + keyConfig.setKeyStoreName("authhandler"); + keyConfig.setKeyAlias("notExist"); + + try { + keyStoreFactory.buildNewSymmetricKey(keyConfig); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + org.springframework.util.Assert.isInstanceOf(EaafKeyAccessException.class, e, "Wong ExceptionType"); + Assert.assertEquals("wrong errorCode", "internal.keystore.09", e.getErrorId()); + + } + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void symmetricHsmFacadeKeyValid() throws EaafException { + configureHsmFacade(); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.HSMFACADE); + keyConfig.setKeyStoreName("authhandler"); + keyConfig.setKeyAlias("aes-key-1"); + + Pair<SecretKey, Provider> key = keyStoreFactory.buildNewSymmetricKey(keyConfig); + Assert.assertNotNull("Key container is null", key); + Assert.assertNotNull("Key is null", key.getFirst()); + Assert.assertNotNull("Provider is null", key.getFirst()); + + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void hsmFacadeKeyStoreSuccessASitTestFacade() throws EaafException, KeyStoreException { + configureHsmFacade(); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + Assert.assertEquals("Wrong HSM-Facade state", EaafKeyStoreFactory.HsmFacadeStatus.UP, + keyStoreFactory.checkHsmFacadeStatus()); + final KeyStoreConfiguration keyStoreConfig = new KeyStoreConfiguration(); keyStoreConfig.setKeyStoreType(KeyStoreType.HSMFACADE); keyStoreConfig.setKeyStoreName("authhandler"); diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/EncryptionTask.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/EncryptionTask.java new file mode 100644 index 00000000..ac456c13 --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/EncryptionTask.java @@ -0,0 +1,156 @@ +package at.gv.egiz.eaaf.core.test.credentials; + +import static org.junit.Assert.assertArrayEquals; + +import java.security.Provider; +import java.util.concurrent.CompletableFuture; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.Assert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.AsyncResult; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.Random; +import at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Getter +public class EncryptionTask implements Runnable { + + private static final String HSM_FACASE_HOST = "eid.a-sit.at"; + private static final String HSM_FACASE_PORT = "9050"; + private static final String HSM_FACASE_SSL_TRUST = "src/test/resources/data/hsm_facade_trust_root.crt"; + private static final String HSM_FACASE_USERNAME = "authhandler-junit"; + private static final String HSM_FACASE_PASSWORD = "supersecret123"; + private static final String PATH_TO_SOFTWARE_KEYSTORE_JKS_WITH_TRUSTED_CERTS = + "src/test/resources/data/junit.jks"; + private static final String PATH_TO_SOFTWARE_KEYSTORE_JKS = + "src/test/resources/data/junit_without_trustcerts.jks"; + private static final String PATH_TO_SOFTWARE_KEYSTORE_PKCS12 = + "src/test/resources/data/junit_without_trustcerts.p12"; + private static final String SOFTWARE_KEYSTORE_PASSWORD = "password"; + + private static final String HSM_FACADE_KEY_ALIAS = "authhandler-sign"; + + private static final String CIPHER_MODE = "AES/GCM/NoPadding"; + private static final int GCM_NONCE_LENGTH = 12; // in bytes + private static final int GCM_TAG_LENGTH = 16; // in bytes + + protected static final String KEYNAME = "AES"; + + @Autowired + private DummyAuthConfigMap mapConfig; + @Autowired + private ApplicationContext context; + + String keyName; + int rounds; + private Exception error; + + public EncryptionTask(ApplicationContext context2, DummyAuthConfigMap mapConfig2, + String keyName, int rounds) { + this.context = context2; + this.mapConfig = mapConfig2; + + this.keyName = keyName; + this.rounds = rounds; + + } + + @Override + public void run() { + run(this.keyName, this.rounds); + + } + + @Async + public CompletableFuture<String> run(String keyName, int rounds) { + try { + Pair<SecretKey, Provider> key = loadSymmetricKey(keyName); + Assert.assertNotNull("Key container is null", key); + + for(int i = 0; i < rounds; i++) { + + log.info("Starting threat: {} Round: {}", Thread.currentThread().getName(), i); + + byte[] data = RandomStringUtils.randomAlphanumeric(1024*64).getBytes(); + Pair<byte[], byte[]> enc = encryptData(key.getFirst(), data); + + byte[] checkData = decryptData(enc, key.getFirst()); + log.info("Finishing threat: {} Round: {}", Thread.currentThread().getName(), i); + + + assertArrayEquals("plaintext not match", data, checkData); + + + + } + + } catch (Exception e) { + this.error = e; + throw new RuntimeException(e); + + } + + return new AsyncResult<>("finished").completable(); + + } + + private byte[] decryptData(Pair<byte[], byte[]> enc, SecretKey secret) throws Exception { + final GCMParameterSpec iv = new GCMParameterSpec(GCM_TAG_LENGTH * 8, enc.getSecond()); + final Cipher cipher = Cipher.getInstance(CIPHER_MODE); + cipher.init(Cipher.DECRYPT_MODE, secret, iv); + return cipher.doFinal(enc.getFirst()); + + } + + + + private Pair<byte[], byte[]> encryptData(SecretKey secret, byte[] toEncrypt) throws Exception { + final byte[] nonce = Random.nextBytes(GCM_NONCE_LENGTH); + final GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce); + final Cipher cipher = Cipher.getInstance(CIPHER_MODE); + cipher.init(Cipher.ENCRYPT_MODE, secret, spec); + + final byte[] encdata = cipher.doFinal(toEncrypt); + final byte[] iv = cipher.getIV(); + + return Pair.newInstance(encdata, iv); + + } + + private Pair<SecretKey, Provider> loadSymmetricKey(String keyName) throws EaafException { + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, HSM_FACASE_HOST); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_PORT, HSM_FACASE_PORT); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_SSLTRUST, HSM_FACASE_SSL_TRUST); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME, HSM_FACASE_USERNAME); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD, HSM_FACASE_PASSWORD); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.HSMFACADE); + keyConfig.setKeyStoreName("authhandler"); + keyConfig.setKeyAlias(keyName); + + return keyStoreFactory.buildNewSymmetricKey(keyConfig); + } + + + +} diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/KeyOperationPerformanceTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/KeyOperationPerformanceTest.java new file mode 100644 index 00000000..90d878b9 --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/KeyOperationPerformanceTest.java @@ -0,0 +1,155 @@ +package at.gv.egiz.eaaf.core.test.credentials; + +import static org.junit.Assert.assertFalse; + +import java.security.Provider; +import java.security.Security; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.crypto.SecretKey; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import at.asitplus.hsmfacade.provider.HsmFacadeProvider; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap; +import lombok.extern.slf4j.Slf4j; + +@Ignore +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("/spring/test_eaaf_pvp_lazy.beans.xml") +@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD) +@Slf4j +public class KeyOperationPerformanceTest { + + private static final String HSM_FACASE_HOST = "eid.a-sit.at"; + private static final String HSM_FACASE_PORT = "9050"; + private static final String HSM_FACASE_SSL_TRUST = "src/test/resources/data/hsm_facade_trust_root.crt"; + private static final String HSM_FACASE_USERNAME = "authhandler-junit"; + private static final String HSM_FACASE_PASSWORD = "supersecret123"; + private static final String PATH_TO_SOFTWARE_KEYSTORE_JKS_WITH_TRUSTED_CERTS = + "src/test/resources/data/junit.jks"; + private static final String PATH_TO_SOFTWARE_KEYSTORE_JKS = + "src/test/resources/data/junit_without_trustcerts.jks"; + private static final String PATH_TO_SOFTWARE_KEYSTORE_PKCS12 = + "src/test/resources/data/junit_without_trustcerts.p12"; + private static final String SOFTWARE_KEYSTORE_PASSWORD = "password"; + + private static final String HSM_FACADE_KEY_ALIAS = "authhandler-sign"; + + private static final String CIPHER_MODE = "AES/GCM/NoPadding"; + private static final int GCM_NONCE_LENGTH = 12; // in bytes + private static final int GCM_TAG_LENGTH = 16; // in bytes + + protected static final String KEYNAME = "AES"; + + + private static final String AES_KEY_1 = "aes-key-1"; + private static final String AES_KEY_2 = "aes-key-2"; + + private static final List<String> ALL_AES_KEYS = Arrays.asList(AES_KEY_1, AES_KEY_2); + + @Autowired + private DummyAuthConfigMap mapConfig; + @Autowired + private ApplicationContext context; + + /** + * jUnit test set-up. + */ + @Before + public void testSetup() { + mapConfig.clearAllConfig(); + Security.removeProvider(HsmFacadeProvider.getInstance().getName()); + + } + + @Ignore + @Test + public void symmetricHsmFacadeKeyLoad() throws EaafException { + Pair<SecretKey, Provider> key = loadSymmetricKey(AES_KEY_1); + Assert.assertNotNull("Key container is null", key); + Assert.assertNotNull("Key is null", key.getFirst()); + Assert.assertNotNull("Provider is null", key.getFirst()); + + } + + + @Ignore + @Test + public void symmetricHsmFacadeKeyOperations() throws Exception { + Pair<SecretKey, Provider> key = loadSymmetricKey(AES_KEY_1); + Assert.assertNotNull("Key container is null", key); + new EncryptionTask(context, mapConfig, AES_KEY_2, 15).run(AES_KEY_2, 15); + + } + + @Test + public void symmetricHsmFacadeMultithreatKeyOperations() throws Exception { + Pair<SecretKey, Provider> key = loadSymmetricKey(AES_KEY_1); + Assert.assertNotNull("Key container is null", key); + + int threads = 30; + + ArrayList<EncryptionTask> taskList = new ArrayList<EncryptionTask>(); + ArrayList<Thread> threadList = new ArrayList<Thread>(); + for(int i=0; i < threads; i++){ + EncryptionTask task = new EncryptionTask(context, mapConfig, ALL_AES_KEYS.get(i % 2), 20); + taskList.add(task); + Thread t = new Thread(task); + threadList.add(t); + t.start(); + } + + // wait until they are all done + log.trace("Wait for mandate sources .... "); + for(int i=0; i<threadList.size(); i++){ + threadList.get(i).join(); + } + log.trace("Mandate sources collection finished "); + + + assertFalse("Find Thread with error", taskList.stream() + .filter(el -> el.getError() != null) + .findFirst() + .isPresent()); + + + } + + private Pair<SecretKey, Provider> loadSymmetricKey(String keyName) throws EaafException { + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_HOST, HSM_FACASE_HOST); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_PORT, HSM_FACASE_PORT); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_SSLTRUST, HSM_FACASE_SSL_TRUST); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME, HSM_FACASE_USERNAME); + mapConfig.putConfigValue(EaafKeyStoreFactory.CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD, HSM_FACASE_PASSWORD); + + final EaafKeyStoreFactory keyStoreFactory = context.getBean(EaafKeyStoreFactory.class); + Assert.assertTrue("HSM Facade state wrong", keyStoreFactory.isHsmFacadeInitialized()); + + SymmetricKeyConfiguration keyConfig = new SymmetricKeyConfiguration(); + keyConfig.setFriendlyName("jUnit test"); + keyConfig.setKeyType(SymmetricKeyType.HSMFACADE); + keyConfig.setKeyStoreName("authhandler"); + keyConfig.setKeyAlias(keyName); + + return keyStoreFactory.buildNewSymmetricKey(keyConfig); + } + +} diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/SymmetricKeyConfigurationTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/SymmetricKeyConfigurationTest.java new file mode 100644 index 00000000..eb4eb212 --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/credentials/SymmetricKeyConfigurationTest.java @@ -0,0 +1,162 @@ +package at.gv.egiz.eaaf.core.test.credentials; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.SymmetricKeyConfiguration.SymmetricKeyType; + +@RunWith(BlockJUnit4ClassRunner.class) +public class SymmetricKeyConfigurationTest { + + private Map<String, String> config; + + @Before + public void testSetup() { + config = new HashMap<>(); + + } + + @Test + public void emptyConfigMap() { + try { + SymmetricKeyConfiguration.buildFromConfigurationMap(config, "jUnitTest"); + Assert.fail("Wrong config not detected"); + + } catch (final EaafConfigurationException e) { + Assert.assertEquals("wrong errorCode", "internal.keystore.04", e.getErrorId()); + } + } + + @Test + public void emptyKeyType() { + try { + config.put("key.type", ""); + + SymmetricKeyConfiguration.buildFromConfigurationMap(config, "jUnitTest"); + Assert.fail("Wrong config not detected"); + + } catch (final EaafConfigurationException e) { + Assert.assertEquals("wrong errorCode", "internal.keystore.04", e.getErrorId()); + } + } + + @Test + public void unknownKeyType() { + try { + config.put("key.type", "test"); + + SymmetricKeyConfiguration.buildFromConfigurationMap(config, "jUnitTest"); + Assert.fail("Wrong config not detected"); + + } catch (final EaafConfigurationException e) { + Assert.assertEquals("wrong errorCode", "internal.keystore.01", e.getErrorId()); + } + } + + @Test + public void hsmFacadeKeyTypeMissingName() { + try { + config.put("key.type", "hsmfacade"); + + SymmetricKeyConfiguration.buildFromConfigurationMap(config, "jUnitTest"); + Assert.fail("Wrong config not detected"); + + } catch (final EaafConfigurationException e) { + Assert.assertEquals("wrong errorCode", "internal.keystore.04", e.getErrorId()); + } + } + + @Test + public void hsmFacadeKeyTypeMissingAlias() { + try { + final String keyStoreName = RandomStringUtils.randomAlphabetic(5); + config.put("key.type", "hsmfacade"); + config.put("keystore.name", keyStoreName); + + SymmetricKeyConfiguration.buildFromConfigurationMap(config, "jUnitTest"); + Assert.fail("Wrong config not detected"); + + } catch (final EaafConfigurationException e) { + Assert.assertEquals("wrong errorCode", "internal.keystore.04", e.getErrorId()); + } + } + + @Test + public void hsmFacadeKeyTypeSucces() throws EaafConfigurationException { + final String keyStoreName = RandomStringUtils.randomAlphabetic(5); + final String keyAlias = RandomStringUtils.randomAlphabetic(5); + config.put("key.type", "hsmfacade"); + config.put("keystore.name", keyStoreName); + config.put("key.alias", keyAlias); + + final SymmetricKeyConfiguration keyStoreConfig = SymmetricKeyConfiguration.buildFromConfigurationMap(config, + "jUnitTest"); + + Assert.assertNotNull("KeyStore config object", keyStoreConfig); + Assert.assertEquals("Wrong Type", SymmetricKeyType.HSMFACADE, keyStoreConfig.getKeyType()); + Assert.assertEquals("Wrong KeyStoreName", keyStoreName, keyStoreConfig.getKeyStoreName()); + Assert.assertEquals("Wrong KeyStoreName", keyAlias, keyStoreConfig.getKeyAlias()); + + + keyStoreConfig.validate(); + + } + + @Test + public void passphraseKeyTypeMissingPassphrase() { + try { + config.put("key.type", "passphrase"); + + SymmetricKeyConfiguration.buildFromConfigurationMap(config, "jUnitTest"); + Assert.fail("Wrong config not detected"); + + } catch (final EaafConfigurationException e) { + Assert.assertEquals("wrong errorCode", "internal.keystore.04", e.getErrorId()); + } + } + + @Test + public void passphraseKeyTypeMissingSalt() { + try { + final String passphrase = RandomStringUtils.randomAlphabetic(5); + config.put("key.type", "passphrase"); + config.put("key.passphrase", passphrase); + + SymmetricKeyConfiguration.buildFromConfigurationMap(config, "jUnitTest"); + Assert.fail("Wrong config not detected"); + + } catch (final EaafConfigurationException e) { + Assert.assertEquals("wrong errorCode", "internal.keystore.04", e.getErrorId()); + } + } + + @Test + public void passphraseKeyTypeSucces() throws EaafConfigurationException { + final String passphrase = RandomStringUtils.randomAlphabetic(5); + final String salt = RandomStringUtils.randomAlphabetic(5); + config.put("key.type", "passphrase"); + config.put("key.passphrase", passphrase); + config.put("key.salt", salt); + + final SymmetricKeyConfiguration keyStoreConfig = SymmetricKeyConfiguration.buildFromConfigurationMap(config, + "jUnitTest"); + + Assert.assertNotNull("KeyStore config object", keyStoreConfig); + Assert.assertEquals("Wrong Type", SymmetricKeyType.PASSPHRASE, keyStoreConfig.getKeyType()); + Assert.assertEquals("Wrong KeyStoreName", passphrase, keyStoreConfig.getSoftKeyPassphrase()); + Assert.assertEquals("Wrong KeyStoreName", salt, keyStoreConfig.getSoftKeySalt()); + + keyStoreConfig.validate(); + + } +} + diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/dummy/DummyAuthConfigMap.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/dummy/DummyAuthConfigMap.java index bf1dfd03..09301f57 100644 --- a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/dummy/DummyAuthConfigMap.java +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/dummy/DummyAuthConfigMap.java @@ -123,7 +123,7 @@ public class DummyAuthConfigMap implements IConfigurationWithSP { @Override public String validateIdpUrl(final URL authReqUrl) throws EaafException { - return null; + return authReqUrl.toExternalForm(); } public void putConfigValue(final String key, final String value) { diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryProdHostTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryProdHostTest.java new file mode 100644 index 00000000..55c17ee8 --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryProdHostTest.java @@ -0,0 +1,98 @@ +package at.gv.egiz.eaaf.core.test.http; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; + +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.MethodMode; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.http.HttpClientConfiguration; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("/spring/test_eaaf_pvp_not_lazy.beans.xml") +@DirtiesContext +public class HttpClientFactoryProdHostTest { + + @Autowired private IHttpClientFactory httpClientFactory; + @Autowired private EaafKeyStoreFactory keyStoreFactory; + + /** + * Initialize full class. + */ + @BeforeClass + public static void classInitializer() { + final Logger logger = (Logger) LoggerFactory.getLogger("org.bouncycastle.jsse"); + logger.setLevel(Level.TRACE); + + } + + /** + * JUnit test set-up. + * + */ + @Before + public void setup() { + + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void getCustomClientX509AuthWithHsmFacadeTrustStore() throws EaafException, ClientProtocolException, + IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, + CertificateEncodingException { + System.setProperty("javax.net.debug", "ssl:handshake"); + + final HttpClientConfiguration clientConfig = new HttpClientConfiguration("jUnit-client"); + clientConfig.setAuthMode("ssl"); + //clientConfig.buildKeyStoreConfig("hsmfacade", null, null, "eid-junit"); + //clientConfig.setSslKeyAlias("rsa-key-1"); + clientConfig.buildKeyStoreConfig("hsmfacade", null, null, "authhandler"); + clientConfig.setSslKeyAlias("authhandler-sign"); + clientConfig.setDisableTlsHostCertificateValidation(false); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(clientConfig); + Assert.assertNotNull("httpClient", client); + + final Pair<KeyStore, Provider> sslClientKeyStore = + keyStoreFactory.buildNewKeyStore(clientConfig.getKeyStoreConfig()); + final X509Certificate clientRootCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[1]; + final X509Certificate clientEeCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[0]; + Base64.getEncoder().encodeToString(clientEeCert.getEncoded()); + + //perform test request + final HttpUriRequest httpGet2 = new HttpGet("https://apps.egiz.gv.at//sslclientcertdemo/"); + final CloseableHttpResponse httpResp2 = client.execute(httpGet2); + Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); + + } + +} diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java index 25bd3008..c71d8352 100644 --- a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java @@ -1,42 +1,66 @@ package at.gv.egiz.eaaf.core.test.http; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; +import java.net.SocketTimeoutException; +import java.security.Key; +import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.Provider; +import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.ContentType; import org.apache.http.impl.client.CloseableHttpClient; import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.MethodMode; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.data.Triple; import at.gv.egiz.eaaf.core.impl.http.HttpClientConfiguration; +import at.gv.egiz.eaaf.core.impl.http.HttpUtils; import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; +import at.gv.egiz.eaaf.core.impl.utils.StreamUtils; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; import okhttp3.HttpUrl; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import okhttp3.mockwebserver.SocketPolicy; import okhttp3.tls.HandshakeCertificates; import okhttp3.tls.HeldCertificate; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/spring/test_eaaf_pvp_not_lazy.beans.xml") +@DirtiesContext public class HttpClientFactoryTest { @Autowired private EaafKeyStoreFactory keyStoreFactory; @@ -46,6 +70,27 @@ public class HttpClientFactoryTest { private HttpUrl mockServerUrl; /** + * Initialize full class. + */ + @BeforeClass + public static void classInitializer() { + final Logger logger = (Logger) LoggerFactory.getLogger("org.bouncycastle.jsse"); + logger.setLevel(Level.TRACE); + + } + + /** + * Reset test environment. + */ + @AfterClass + public static void classReset() { + System.clearProperty("javax.net.ssl.trustStoreType"); + System.clearProperty("javax.net.ssl.trustStore"); + System.clearProperty("javax.net.ssl.trustStorePassword"); + + } + + /** * JUnit test set-up. * */ @@ -84,6 +129,27 @@ public class HttpClientFactoryTest { } @Test + public void defaultHttpClientRetryOneTime() throws EaafException, InterruptedException, + ClientProtocolException, IOException { + final CloseableHttpClient client = httpClientFactory.getHttpClient(); + Assert.assertNotNull("No httpClient", client); + + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("GetData")); + + //request webservice + final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString()); + final CloseableHttpResponse httpResp1 = client.execute(httpGet1); + Assert.assertEquals("http statusCode", 200, httpResp1.getStatusLine().getStatusCode()); + + } + + @Test public void getCustomClientsDefault() throws EaafException { final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); Assert.assertFalse("Wrong default config - Hostnamevalidation", @@ -109,7 +175,7 @@ public class HttpClientFactoryTest { } @Test - public void getCustomClientBasicAuth() throws EaafException, ClientProtocolException, + public void getCustomClientBasicAuth() throws EaafException, ClientProtocolException, IOException, InterruptedException { final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); config.setAuthMode("password"); @@ -157,6 +223,193 @@ public class HttpClientFactoryTest { } @Test + public void httpPostRetryNotAllowed() throws EaafException, InterruptedException, + ClientProtocolException, IOException { + final HttpClientConfiguration config = + new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3)); + config.setHttpErrorRetryCount(2); + config.setHttpErrorRetryPost(false); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("No httpClient", client); + + + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("GetData")); + + //request webservice + final HttpUriRequest httpGet1 = new HttpPost(mockServerUrl.url().toString()); + try { + client.execute(httpGet1); + Assert.fail("HTTP POST retry not allowed"); + + } catch (final SocketTimeoutException e) { + Assert.assertNotNull("No errorMsg", e.getMessage()); + + } + + } + + @Test + public void httpPostRetryOneTime() throws EaafException, InterruptedException, + ClientProtocolException, IOException { + final HttpClientConfiguration config = + new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3)); + config.setHttpErrorRetryCount(2); + config.setHttpErrorRetryPost(true); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("No httpClient", client); + + + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("GetData")); + + //request webservice + final HttpUriRequest httpGet1 = new HttpPost(mockServerUrl.url().toString()); + final StatusLine httpResp1 = client.execute(httpGet1, + HttpUtils.simpleStatusCodeResponseHandler()); + Assert.assertEquals("http statusCode", 200, httpResp1.getStatusCode()); + + } + + @Test + public void testHttpClientRetryOneTime() throws EaafException, InterruptedException, + ClientProtocolException, IOException { + final HttpClientConfiguration config = + new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3)); + config.setHttpErrorRetryCount(2); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("No httpClient", client); + + + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + + String bodyData = RandomStringUtils.randomAlphanumeric(10); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody(bodyData)); + + //request webservice + final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString()); + final Triple<StatusLine, ByteArrayInputStream, ContentType> httpResp1 = client.execute(httpGet1, + HttpUtils.bodyStatusCodeResponseHandler()); + Assert.assertEquals("http statusCode", 200, httpResp1.getFirst().getStatusCode()); + Assert.assertEquals("http statusCode", bodyData, new String(StreamUtils.readStream(httpResp1.getSecond()))); + + + } + + @Test + public void testHttpClientRetryTwoTime() throws EaafException, InterruptedException, + ClientProtocolException, IOException { + final HttpClientConfiguration config = + new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3)); + config.setHttpErrorRetryCount(2); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("No httpClient", client); + + + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("GetData")); + + //request webservice + final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString()); + final CloseableHttpResponse httpResp1 = client.execute(httpGet1); + Assert.assertEquals("http statusCode", 200, httpResp1.getStatusLine().getStatusCode()); + + } + + @Test + public void testHttpClientRetryMaxReached() throws EaafException, InterruptedException, + ClientProtocolException, IOException { + final HttpClientConfiguration config = + new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3)); + config.setHttpErrorRetryCount(2); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("No httpClient", client); + + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("GetData")); + + //request webservice + final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString()); + try { + client.execute(httpGet1); + Assert.fail("Max retry failed"); + + } catch (final SocketTimeoutException e) { + Assert.assertNotNull("No errorMsg", e.getMessage()); + + } + } + + @Test + public void testHttpClientNoRetry() throws EaafException, InterruptedException, + ClientProtocolException, IOException { + final HttpClientConfiguration config = + new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3)); + config.setHttpErrorRetryCount(0); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("No httpClient", client); + + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.NO_RESPONSE) + .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("GetData")); + + //request webservice + final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString()); + try { + client.execute(httpGet1); + Assert.fail("Max retry failed"); + + } catch (final SocketTimeoutException e) { + Assert.assertNotNull("No errorMsg", e.getMessage()); + + } + } + + @Test public void getCustomClientBasicAuthNoPassword() throws EaafException { final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); config.setAuthMode("password"); @@ -283,7 +536,7 @@ public class HttpClientFactoryTest { } @Test - public void getCustomClientX509AuthWithWrongAlias() throws EaafException, KeyStoreException, + public void getCustomClientX509AuthWithWrongAlias() throws EaafException, KeyStoreException, ClientProtocolException, IOException { final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); config.setAuthMode("ssl"); @@ -311,9 +564,120 @@ public class HttpClientFactoryTest { final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder() .addTrustedCertificate( (X509Certificate) sslClientKeyStore.getFirst().getCertificate("meta")) + .addTrustedCertificate( + (X509Certificate) sslClientKeyStore.getFirst().getCertificate("sig")) + .heldCertificate(localhostCertificate) + .build(); + mockWebServer = new MockWebServer(); + mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false); + mockWebServer.requireClientAuth(); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("Successful auth!")); + mockServerUrl = mockWebServer.url("/sp/junit"); + + //perform test request + final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString()); + final CloseableHttpResponse httpResp2 = client.execute(httpGet2); + Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); + + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void getCustomClientX509AuthWithHsmFacade() throws EaafException, ClientProtocolException, + IOException, KeyStoreException { + final HttpClientConfiguration clientConfig = new HttpClientConfiguration("jUnit-client"); + clientConfig.setAuthMode("ssl"); + clientConfig.buildKeyStoreConfig("hsmfacade", null, null, "authhandler"); + clientConfig.setSslKeyAlias("authhandler-sign"); + clientConfig.setDisableTlsHostCertificateValidation(true); + + + + final CloseableHttpClient client = httpClientFactory.getHttpClient(clientConfig); + Assert.assertNotNull("httpClient", client); + + //set-up mock-up web-server with SSL client authentication + final Pair<KeyStore, Provider> sslClientKeyStore = + keyStoreFactory.buildNewKeyStore(clientConfig.getKeyStoreConfig()); + final X509Certificate clientRootCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[1]; + final X509Certificate clientEeCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[0]; + + final String localhost = InetAddress.getByName("localhost").getCanonicalHostName(); + final HeldCertificate localhostCertificate = new HeldCertificate.Builder() + .addSubjectAlternativeName(localhost) + .build(); + final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder() + .addTrustedCertificate(clientEeCert) + .addTrustedCertificate(clientRootCert) + .heldCertificate(localhostCertificate) + .build(); + mockWebServer = new MockWebServer(); + mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false); + mockWebServer.requireClientAuth(); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("Successful auth!")); + mockServerUrl = mockWebServer.url("/sp/junit"); + + //perform test request + final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString()); + final CloseableHttpResponse httpResp2 = client.execute(httpGet2); + Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); + + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void getCustomClientX509AuthWithHsmFacadeTrustStore() throws EaafException, ClientProtocolException, + IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { + + final String current = new java.io.File(".").getCanonicalPath(); + System.setProperty("javax.net.ssl.trustStoreType", "jks"); + System.setProperty("javax.net.ssl.trustStore", + current + "/src/test/resources/data/ssL_truststore.jks"); + System.setProperty("javax.net.ssl.trustStorePassword", + "password"); + + final KeyStoreConfiguration sslServerCertConfig = new KeyStoreConfiguration(); + sslServerCertConfig.setKeyStoreType(KeyStoreType.JKS); + sslServerCertConfig.setFriendlyName("SSL host cert"); + sslServerCertConfig.setSoftKeyStoreFilePath("src/test/resources/data/ssl_host.jks"); + sslServerCertConfig.setSoftKeyStorePassword("password"); + + Pair<KeyStore, Provider> sslServerHostKeyStore = + keyStoreFactory.buildNewKeyStore(sslServerCertConfig); + + + final HttpClientConfiguration clientConfig = new HttpClientConfiguration("jUnit-client"); + clientConfig.setAuthMode("ssl"); + clientConfig.buildKeyStoreConfig("hsmfacade", null, null, "authhandler"); + clientConfig.setSslKeyAlias("authhandler-sign"); + clientConfig.setDisableTlsHostCertificateValidation(false); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(clientConfig); + Assert.assertNotNull("httpClient", client); + + //set-up mock-up web-server with SSL client authentication + final Pair<KeyStore, Provider> sslClientKeyStore = + keyStoreFactory.buildNewKeyStore(clientConfig.getKeyStoreConfig()); + final X509Certificate clientRootCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[1]; + final X509Certificate clientEeCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[0]; + + Key sslKey = sslServerHostKeyStore.getFirst().getKey("ssl", "password".toCharArray()); + X509Certificate sslCert = (X509Certificate) sslServerHostKeyStore.getFirst().getCertificate("ssl"); + KeyPair keyPair = new KeyPair(sslCert.getPublicKey(), (PrivateKey) sslKey); + HeldCertificate localhostCertificate = new HeldCertificate(keyPair, sslCert); + final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder() + .addTrustedCertificate(clientEeCert) + .addTrustedCertificate(clientRootCert) .heldCertificate(localhostCertificate) .build(); mockWebServer = new MockWebServer(); + mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false); mockWebServer.requireClientAuth(); mockWebServer.enqueue(new MockResponse().setResponseCode(200) @@ -326,4 +690,5 @@ public class HttpClientFactoryTest { Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); } + } diff --git a/eaaf_core_utils/src/test/resources/data/config1.properties b/eaaf_core_utils/src/test/resources/data/config1.properties new file mode 100644 index 00000000..12209d21 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/config1.properties @@ -0,0 +1,15 @@ +security.hsmfacade.host=eid.a-sit.at +security.hsmfacade.port=9050 +security.hsmfacade.trustedsslcert=src/test/resources/data/hsm_facade_trust_root.crt +security.hsmfacade.username=authhandler-junit +security.hsmfacade.password=supersecret123 + +client.http.connection.timeout.socket=2 +client.http.connection.timeout.connection=2 +client.http.connection.timeout.request=2 + +core.pendingrequestid.maxlifetime=180 +core.pendingrequestid.digist.type=passphrase +core.pendingrequestid.digist.secret=pendingReqIdSecret +core.pendingrequestid.digist.keystore.name= +core.pendingrequestid.digist.key.alias=
\ No newline at end of file diff --git a/eaaf_core_utils/src/test/resources/data/config2.properties b/eaaf_core_utils/src/test/resources/data/config2.properties new file mode 100644 index 00000000..3a1194b4 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/config2.properties @@ -0,0 +1,15 @@ +security.hsmfacade.host=eid.a-sit.at +security.hsmfacade.port=9050 +security.hsmfacade.trustedsslcert=src/test/resources/data/hsm_facade_trust_root.crt +security.hsmfacade.username=authhandler-junit +security.hsmfacade.password=supersecret123 + +client.http.connection.timeout.socket=2 +client.http.connection.timeout.connection=2 +client.http.connection.timeout.request=2 + +core.pendingrequestid.maxlifetime=180 +core.pendingrequestid.digist.type=hsmfacade +core.pendingrequestid.digist.secret=pendingReqIdSecret +core.pendingrequestid.digist.keystore.name=authhandler +core.pendingrequestid.digist.key.alias=aes-key-1
\ No newline at end of file diff --git a/eaaf_core_utils/src/test/resources/data/hsm_ee-RSA_rootcert.crt b/eaaf_core_utils/src/test/resources/data/hsm_ee-RSA_rootcert.crt new file mode 100644 index 00000000..aa83c8d9 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/hsm_ee-RSA_rootcert.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIICDTCCAbOgAwIBAgIIVLxIFI8kRpkwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAwwHRUMtUm9vdDAeFw0yMDA2MTgwNzM2MTBaFw0yNTA2MTgwNzM2MTBaMDwxGzAZBgNVBAMMEmludC1yc2Eta2V5LTEtMDAwMTERMA8GA1UECgwIc29mdHdhcmUxCjAIBgNVBAUTATEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDrM1ocQqtch95Dm21JHi0V35nlWZibsjLqR+g8ERdD1qFgun/X0I/Rbft+KxB8QsDX7UmIjXGdavNcEjY/XcbiJxUcpv7vn/2+x3JxZO6Iye/ut001okICt3OGIqP93ZEnIaTTNhDsK7OnvD/eUjlmuHiTaFq1dZLKYDQlz9jl/9F4axfrz1V7oo60iqFIW+7tlUeh8VGDUPjQpHghzjHXTJv/OIAt752K31Tn8KR3kvkn6WTPo8eOWVaPQ480Dik0e2afTPPJNZJ7BW111IwqBAOKp586yVsQ4XVEF8H64Cq+s+b4/HBboo9TDJKTJvo2yQmcTsahbH+Rlm20ifUTAgMBAAEwCgYIKoZIzj0EAwIDSAAwRQIhANKN/N2Atb5fbeHSB2Myv/JcNf9JonxFe92AOu4f62NNAiBjOEeg4OyJZKPiDl6aqYVtz1Qroo6xzUC9UVA4qNe4LA== +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/hsm_ee_eecert.crt b/eaaf_core_utils/src/test/resources/data/hsm_ee_eecert.crt new file mode 100644 index 00000000..b4c47c78 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/hsm_ee_eecert.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCArqgAwIBAgIIMrAOP6EqywMwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAwwHRUMtUm9vdDAeFw0yMDA2MTgwNzM2MDlaFw0yNTA2MTgwNzM2MDlaMEMxIjAgBgNVBAMMGWludC1hdXRoaGFuZGxlci1zaWduLTAwMDExETAPBgNVBAoMCHNvZnR3YXJlMQowCAYDVQQFEwExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5j2vR8ZV88fOJzFHmLuPizHXk+aiE0fX8Bu63FqCNa2Vy343u2k+4HVQBWpAtyRej8nL7JQuhFa11YRtVF+Vos9cyUDk482sUlYA2LdAFIuHJP6iTRrrHYdiTskc3FQUm9m9KorVFwnfzqJ40wjn9hD0xKCE0odk9IHA5aLTul0mSMcHQ+MMu2cbelK2TWTEC0wcGum2NrTWukhf0E4/e4SuqMgEUPdHSEaNZLGKZU0kyVtXKK0HCtWF8OKGay4V504dk3qnmRHHO8ik3oyPk2yLipQAPL0bUGMZ7tlRLo5VxJvkZSsJfAzvYn1JzcQIt6bXszQGU/JS6dw7Jq99Ckzox/vk8p1YUPd4tCuUJPXzhFhI7CedHKGItwk7mQf5llgeVJy40sVrFwHLDPufY5mlm9C+gVBs71+ibT/sJ96gCueWqduzDX8fHzeaNVj2NFEx2pT46D5cbRWZBow3+Rx+hpge6SDMzaebpbkgLPX+35D952bMLjm+9T0DulGk1tLL2k4YK+1WWUprlo+rm65OA5FxnO5YxNmGk2Uizu3tSoZMxmXwEWoFfYaMsTt5Not3+CLPjfs6pE4ML9ifAOxW+pPsmanDiNaMiL0/aSddvvkv3lSMRiU9nYh3DuOu6sGIMaPdyYF0V/9Fua2SDZV3QKIbzPbJfEsXFKtjtzUCAwEAATAKBggqhkjOPQQDAgNHADBEAiBHsK819KfxVn91KAmjRjb7zx8TLLBApyaZHXi5HVrYUwIgQbF6u6bXziAWdijlJC0IOWdBuTLWmTvFqBH7uX1HL9c= +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/hsm_ee_rootcert.crt b/eaaf_core_utils/src/test/resources/data/hsm_ee_rootcert.crt new file mode 100644 index 00000000..fa7b132f --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/hsm_ee_rootcert.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIBPDCB46ADAgECAghZ0/gtbA6FrjAKBggqhkjOPQQDAjASMRAwDgYDVQQDDAdFQy1Sb290MB4XDTIwMDYxODA3MzU1M1oXDTMwMDYxODA3MzU1M1owEjEQMA4GA1UEAwwHRUMtUm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIjgL+6qiE9oj2yWCkVm6s7AaYkbDhTptYXTW92MhASiTqxL6g8tr28MlRA2P8HPrNSK9payeMe5QW9Kxn+EMPejIzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgWgMAoGCCqGSM49BAMCA0gAMEUCIDq2f4xjYD8pzr+mdzuT8wzePRnj/EatjmimGnvNt3FjAiEArezudh6G+wE+ds6S0dnFxG0o/BrbR0fiRNTQwiZA9ec= +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/hsm_facade_trust_root.crt b/eaaf_core_utils/src/test/resources/data/hsm_facade_trust_root.crt index 01be3821..204ddccf 100644 --- a/eaaf_core_utils/src/test/resources/data/hsm_facade_trust_root.crt +++ b/eaaf_core_utils/src/test/resources/data/hsm_facade_trust_root.crt @@ -1,10 +1,12 @@ -----BEGIN CERTIFICATE----- -MIIBdDCCARqgAwIBAgIEXkz1yjAKBggqhkjOPQQDAjARMQ8wDQYDVQQDDAZlY3Jv -b3QwHhcNMjAwMjE5MDg0NjAyWhcNMjEwMjE4MDg0NjAyWjARMQ8wDQYDVQQDDAZl -Y3Jvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS8yvpVIWbPj4E7Lr87hwQR -T9DZf9WY5LMV7gF6NKpnJ5JkEql/s7fqBVbrh8aSNo6gmfmSk4VYGhPJ+DCMzzQj -o2AwXjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFOXafzYpIOlu6BgNU+Ee -JWuJobgWMB0GA1UdDgQWBBTl2n82KSDpbugYDVPhHiVriaG4FjALBgNVHQ8EBAMC -AQYwCgYIKoZIzj0EAwIDSAAwRQIgRt/51PKL/bATuLCdib95Ika+h845Jo0G+Sbn -bzNwJAcCIQCVD1cxEBuUkKaiaLbTiNVsEjvQb6ti0TFbbQUH66jCGA== +MIIByzCCAXGgAwIBAgIEYC5cIjAKBggqhkjOPQQDAjA7MRMwEQYDVQQKDApBLVNJ +VCBQbHVzMRIwEAYDVQQLDAlIc21GYWNhZGUxEDAOBgNVBAMMB0VDIFJvb3QwHhcN +MjEwMjE4MTIyMjU4WhcNMzEwMjE4MTIyMjU4WjA7MRMwEQYDVQQKDApBLVNJVCBQ +bHVzMRIwEAYDVQQLDAlIc21GYWNhZGUxEDAOBgNVBAMMB0VDIFJvb3QwWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAARK1UAE+T3xYsoI0VkRcP20jPwTd2MePMkXRsSR +lpqPMQ6dPMlxPmAzWK33DWPFAFMY8+ecF0J8t2D+5RiJSSB+o2MwYTAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT1v6FCAwJIM8kv +JD7gVjdGXqhcYjAdBgNVHQ4EFgQU9b+hQgMCSDPJLyQ+4FY3Rl6oXGIwCgYIKoZI +zj0EAwIDSAAwRQIhAI+5lHyNCQfyj8c0pdBDVWY3fkCOj9ZTJ/hqgW+6TIQBAiBS +jn7uIj7tGm+f0RgXMbhcgtQhYgVwf0x8OnRwmDOwaw== -----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/server_host.crt b/eaaf_core_utils/src/test/resources/data/server_host.crt new file mode 100644 index 00000000..21d3a1e4 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/server_host.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC2TCCAcECBGB5WpEwDQYJKoZIhvcNAQELBQAwMTELMAkGA1UEBhMCQVQxDjAM +BgNVBAsMBWpVbml0MRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjEwNDE2MDkzNjE3 +WhcNMjQwMTEwMDkzNjE3WjAxMQswCQYDVQQGEwJBVDEOMAwGA1UECwwFalVuaXQx +EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAJVYLzPzq7oBGS5Wer0++rHbp+DWI7srAV1lGHdq8ST6APh/7fEVWpdZDpMY +bOXl6uIiVmMsx/jUhQwOu4rFXThiQlwyQOv57SO7WHqNPqbRs/EUVnzW35aXU/DB +CmkqKyjK/+vuq7tIahlpqrppCzBVC9/Z15U+RMTdnATrohALNJovydH3VSkdkKX0 +5BDx779/8malTgyWTUgl+p3F/91iIIl4ZvIngo2ZYQCFm1nV6jmpErGFkG6YVrO7 +oe3OlGKFiXtqCmq+NSFeXsv/SaXWNUw82pYKuK/5EFSLX49HLBBDI14eOCuVLnGA +H/kG3tGteYMBNzSMmC/kcKgRDnUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAJn2 +a/VbtXGmHe9wmtu8K3noyECfG5fbu9URUjXhCBlXGcdjfz1gzrOHcmaBndk0a566 +R2W0fLvjLpjWChrj7r34EpNYGPMLV2gp3ZkiSGl9kv8mf9iChK6+ga3SlyHJuXXu +gw6eOIAxBrE/vLw+pZtCEV9yPrIydkt19jjejf1wjs5y2G7m5r5pBIh6Wlmmc4f2 +3M6l6Dge78WVdUaU5AeAHjgGgXwULxmLGxi6yiS5HsSeb79oGz9psHbq1EAvwOVY +sLepTbDQvX/VAAG7HOJXhdGM0fRIkM7HFA5+6joTHvAKhuMlFIJ8Y4QIG2QaIBAh +eBBh91x/aB2xOKs+Kg== +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/ssL_truststore.jks b/eaaf_core_utils/src/test/resources/data/ssL_truststore.jks Binary files differnew file mode 100644 index 00000000..4d7bc2f3 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/ssL_truststore.jks diff --git a/eaaf_core_utils/src/test/resources/data/ssl_host.jks b/eaaf_core_utils/src/test/resources/data/ssl_host.jks Binary files differnew file mode 100644 index 00000000..4ca07595 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/ssl_host.jks diff --git a/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_lazy.beans.xml b/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_lazy.beans.xml index 210b88be..672efe5d 100644 --- a/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_lazy.beans.xml +++ b/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_lazy.beans.xml @@ -13,11 +13,20 @@ <bean id="dummyAuthConfigMap" class="at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap" /> - + <bean id="eaafKeyStoreFactory" class="at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory" /> <bean id="eaafUtilsMessageSource" class="at.gv.egiz.eaaf.core.impl.logging.EaafUtilsMessageSource" /> + <bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> + <property name="corePoolSize" value="5" /> + <property name="maxPoolSize" value="25" /> + <property name="queueCapacity" value="100" /> + </bean> + + <bean class="at.gv.egiz.eaaf.core.test.credentials.EncryptionTask" + scope="prototype"/> + </beans>
\ No newline at end of file diff --git a/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy.beans.xml b/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy.beans.xml index 402e07f9..92dd5928 100644 --- a/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy.beans.xml +++ b/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy.beans.xml @@ -12,7 +12,14 @@ default-lazy-init="true"> <bean id="dummyAuthConfigMap" - class="at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap" /> + class="at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap"> + <constructor-arg value="/data/config1.properties" /> + </bean> + + <bean id="encrytedTokenGenerationStrategy" + class="at.gv.egiz.eaaf.core.impl.utils.AuthenticatedEncryptionPendingRequestIdGenerationStrategy" /> + + <import resource="classpath:/spring/eaaf_utils.beans.xml"/> diff --git a/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy_with_hsm.beans.xml b/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy_with_hsm.beans.xml new file mode 100644 index 00000000..0f235e29 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy_with_hsm.beans.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xmlns:tx="http://www.springframework.org/schema/tx" + xmlns:aop="http://www.springframework.org/schema/aop" + xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" + default-lazy-init="true"> + + <bean id="dummyAuthConfigMap" + class="at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap"> + <constructor-arg value="/data/config2.properties" /> + </bean> + + <bean id="encrytedTokenGenerationStrategy" + class="at.gv.egiz.eaaf.core.impl.utils.AuthenticatedEncryptionPendingRequestIdGenerationStrategy" /> + + + + <import resource="classpath:/spring/eaaf_utils.beans.xml"/> + +</beans>
\ No newline at end of file |