diff options
author | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-06-23 08:57:18 +0200 |
---|---|---|
committer | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-06-23 08:57:18 +0200 |
commit | 234cee5815f8688d45146b792ebe3c8015a7dc74 (patch) | |
tree | 73dfacc93f4c743e19a93016fb80e207d12a605c | |
parent | 04cd51a31e54cea00d53417ceb4d82fc068c4d75 (diff) | |
download | EAAF-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
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 @@ -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> |