summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lenz <thomas.lenz@egiz.gv.at>2020-06-23 08:57:18 +0200
committerThomas Lenz <thomas.lenz@egiz.gv.at>2020-06-23 08:57:18 +0200
commit234cee5815f8688d45146b792ebe3c8015a7dc74 (patch)
tree73dfacc93f4c743e19a93016fb80e207d12a605c
parent04cd51a31e54cea00d53417ceb4d82fc068c4d75 (diff)
downloadEAAF-Components-234cee5815f8688d45146b792ebe3c8015a7dc74.tar.gz
EAAF-Components-234cee5815f8688d45146b792ebe3c8015a7dc74.tar.bz2
EAAF-Components-234cee5815f8688d45146b792ebe3c8015a7dc74.zip
add new pendingRequestId generation-strategy that uses authenticated-encryption protect the internal pendingRequesttId
-rw-r--r--eaaf_core_utils/pom.xml6
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/AuthenticatedEncryptionPendingRequestIdGenerationStrategy.java291
-rw-r--r--eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyTest.java484
-rw-r--r--eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyWithHsmTest.java42
-rw-r--r--eaaf_core_utils/src/test/resources/data/config1.properties8
-rw-r--r--eaaf_core_utils/src/test/resources/data/config2.properties15
-rw-r--r--eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy.beans.xml5
-rw-r--r--eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy_with_hsm.beans.xml26
-rw-r--r--pom.xml2
9 files changed, 876 insertions, 3 deletions
diff --git a/eaaf_core_utils/pom.xml b/eaaf_core_utils/pom.xml
index 02c7839b..181bce1a 100644
--- a/eaaf_core_utils/pom.xml
+++ b/eaaf_core_utils/pom.xml
@@ -81,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>
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..ebfe7500
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/AuthenticatedEncryptionPendingRequestIdGenerationStrategy.java
@@ -0,0 +1,291 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.io.UnsupportedEncodingException;
+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");
+
+ 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("UTF-8"));
+
+ } catch (final JoseException | UnsupportedEncodingException e) {
+ throw new EaafException("internal.99", 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, "PendingReqId has an unvalid format");
+
+ }
+
+ final String[] tokenElements =
+ StringUtils.split(stringToken, TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS);
+ return tokenElements[1];
+
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+
+ } catch (JoseException 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);
+
+ }
+ }
+
+ @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.warn("PendingRequestId has an unvalid format");
+ log.debug("PendingRequestId: {}", stringToken);
+ throw new PendingReqIdValidationException(null, "PendingReqId has an unvalid format");
+
+ }
+
+ 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.warn("Token exceeds the valid period");
+ log.debug("Token: {} | Now: {}", timeStamp, now);
+ throw new PendingReqIdValidationException(internalPendingReqId,
+ "PendingRequestId exceeds the valid period");
+
+ }
+ log.debug("Token valid-period check successful");
+
+ return internalPendingReqId;
+
+ } catch (JoseException e) {
+ log.warn("Token is NOT a valid encrypt. Msg: {}", e.getMessage());
+ log.debug("TokenValue: {}", externalPendingReqId);
+ throw new PendingReqIdValidationException(null, "PendingReqId is NOT a valid encrypted", e);
+
+ } catch (final IllegalArgumentException 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);
+
+ }
+ }
+
+ @Nonnull
+ private String getDecryptedExternalPendingRequestId(String externalPendingReqId)
+ throws JoseException, PendingReqIdValidationException, UnsupportedEncodingException {
+ if (StringUtils.isEmpty(externalPendingReqId)) {
+ log.info("PendingReqId is 'null' or empty");
+ throw new PendingReqIdValidationException(null, "PendingReqId is 'null' or empty");
+
+ }
+
+ log.trace("RAW external pendingReqId: {}", externalPendingReqId);
+ final byte[] externalPendingReqIdBytes = Base64.getUrlDecoder().decode(externalPendingReqId);
+
+ if (externalPendingReqIdBytes.length > maxPendingReqIdSize) {
+ log.warn("pendingReqId size exceeds {}", maxPendingReqIdSize);
+ throw new PendingReqIdValidationException(null,
+ "pendingReqId exceeds max.size: " + maxPendingReqIdSize);
+
+ }
+
+
+ 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, "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/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..34f4a3b1
--- /dev/null
+++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyTest.java
@@ -0,0 +1,484 @@
+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.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")
+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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, PendingReqId is 'null' or empty]",
+ e.getMessage());
+
+ }
+ }
+
+ @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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, PendingReqId is 'null' or empty]",
+ e.getMessage());
+
+ }
+ }
+
+ @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", "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());
+
+ }
+ }
+
+ @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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, "
+ + "pendingReqId exceeds max.size: 1024]",
+ e.getMessage());
+
+ }
+ }
+
+ @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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, "
+ + "PendingReqId has an unvalid format]",
+ e.getMessage());
+
+ }
+ }
+
+ @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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, "
+ + "PendingReqId has an unvalid format]",
+ e.getMessage());
+
+ }
+ }
+
+ @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", "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());
+
+ }
+ }
+
+ @Test
+ public void wrongFormatWrongDate() 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");
+
+ 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", "process.99", e.getErrorId());
+ Assert.assertTrue("Wrong errorMsg", e.getMessage().contains("PendingRequestId exceeds the valid period"));
+
+ }
+ }
+
+ @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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, "
+ + "PendingReqId has an unvalid format]",
+ e.getMessage());
+
+ }
+ }
+
+ @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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, "
+ + "PendingReqId has an unvalid format]",
+ e.getMessage());
+
+ }
+ }
+
+ @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", "process.99", e.getErrorId());
+ Assert.assertEquals("Wrong errorMsg",
+ "No StatusMessager-Backend available! StatusCode:process.99 Params:[null, "
+ + "PendingReqId is NOT a valid encrypted]",
+ e.getMessage());
+
+ }
+ }
+
+ @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..95a2b7a7
--- /dev/null
+++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/impl/utils/test/AuthenticatedEncryptionPendingRequestIdGenerationStrategyWithHsmTest.java
@@ -0,0 +1,42 @@
+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.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")
+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/resources/data/config1.properties b/eaaf_core_utils/src/test/resources/data/config1.properties
index 6192002e..12209d21 100644
--- a/eaaf_core_utils/src/test/resources/data/config1.properties
+++ b/eaaf_core_utils/src/test/resources/data/config1.properties
@@ -6,4 +6,10 @@ security.hsmfacade.password=supersecret123
client.http.connection.timeout.socket=2
client.http.connection.timeout.connection=2
-client.http.connection.timeout.request=2 \ No newline at end of file
+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/spring/test_eaaf_pvp_not_lazy.beans.xml b/eaaf_core_utils/src/test/resources/spring/test_eaaf_pvp_not_lazy.beans.xml
index dc520086..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
@@ -16,6 +16,11 @@
<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"/>
</beans> \ No newline at end of file
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
diff --git a/pom.xml b/pom.xml
index 82d4d978..14497582 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,7 +70,7 @@
<httpcore.version>4.4.11</httpcore.version>
<com.fasterxml.jackson.core.version>2.9.8</com.fasterxml.jackson.core.version>
- <org.bitbucket.b_c.jose4j.version>0.6.5</org.bitbucket.b_c.jose4j.version>
+ <org.bitbucket.b_c.jose4j.version>0.7.1</org.bitbucket.b_c.jose4j.version>
<jaxen.jaxen.version>1.1.6</jaxen.jaxen.version>
<xerces.version>2.11.0</xerces.version>