diff --git a/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/request/EciesEncryptedRequest.java b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/request/EciesEncryptedRequest.java index fadd11dd..827b7b9b 100644 --- a/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/request/EciesEncryptedRequest.java +++ b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/request/EciesEncryptedRequest.java @@ -30,6 +30,11 @@ @Data public class EciesEncryptedRequest { + /** + * Identifier of the temporary key. + */ + private String temporaryKeyId; + /** * Base64 encoded ephemeral public key. */ diff --git a/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/request/TemporaryKeyRequest.java b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/request/TemporaryKeyRequest.java new file mode 100644 index 00000000..5df0ebd4 --- /dev/null +++ b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/request/TemporaryKeyRequest.java @@ -0,0 +1,37 @@ +/* + * PowerAuth integration libraries for RESTful API applications, examples and + * related software components + * + * Copyright (C) 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.rest.api.model.request; + +import lombok.Data; + +/** + * Request class with temporary public key. + * + * @author Petr Dvorak, petr@wultra.com + */ +@Data +public class TemporaryKeyRequest { + + /** + * JWT with encoded temporary key request. + */ + private String jwt; + +} \ No newline at end of file diff --git a/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/response/ServerStatusResponse.java b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/response/ServerStatusResponse.java index 16cb7eff..291c4db4 100644 --- a/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/response/ServerStatusResponse.java +++ b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/response/ServerStatusResponse.java @@ -25,5 +25,12 @@ * @param serverTime Server time. * @author Roman Strobl, roman.strobl@wultra.com */ -public record ServerStatusResponse(long serverTime) { +public record ServerStatusResponse(long serverTime, Application application) { + /** + * Record for information about the application. + * @param name Application name, if present in BuildProperties. + * @param version Application version, if present in BuildProperties. + */ + public record Application(String name, String version) { + } } \ No newline at end of file diff --git a/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/response/TemporaryKeyResponse.java b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/response/TemporaryKeyResponse.java new file mode 100644 index 00000000..8dd1f85e --- /dev/null +++ b/powerauth-restful-model/src/main/java/io/getlime/security/powerauth/rest/api/model/response/TemporaryKeyResponse.java @@ -0,0 +1,37 @@ +/* + * PowerAuth integration libraries for RESTful API applications, examples and + * related software components + * + * Copyright (C) 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.rest.api.model.response; + +import lombok.Data; + +/** + * Response class with temporary key. + * + * @author Petr Dvorak, petr@wultra.com + */ +@Data +public class TemporaryKeyResponse { + + /** + * JWT with encoded temporary key response. + */ + private String jwt; + +} diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/encryption/EncryptionContext.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/encryption/EncryptionContext.java index c919dbea..ce60e171 100644 --- a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/encryption/EncryptionContext.java +++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/encryption/EncryptionContext.java @@ -44,6 +44,7 @@ public class EncryptionContext { * Protocol version. */ private final String version; + /** * PowerAuth HTTP header used for deriving ECIES encryption context. */ diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthTemporaryKeyException.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthTemporaryKeyException.java new file mode 100644 index 00000000..02db78a3 --- /dev/null +++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthTemporaryKeyException.java @@ -0,0 +1,55 @@ +/* + * PowerAuth integration libraries for RESTful API applications, examples and + * related software components + * + * Copyright (C) 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.rest.api.spring.exception; + +/** + * Exception raised in case PowerAuth fails to return temporary keys. + * + * @author Petr Dvorak, petr@wultra.com + */ +public class PowerAuthTemporaryKeyException extends Exception { + + private static final String DEFAULT_CODE = "ERR_TEMPORARY_KEY"; + private static final String DEFAULT_ERROR = "POWER_AUTH_TEMPORARY_KEY_FAILURE"; + + /** + * Default constructor. + */ + public PowerAuthTemporaryKeyException() { + super(DEFAULT_ERROR); + } + + /** + * Get the default error code, used for example in REST response. + * @return Default error code. + */ + public String getDefaultCode() { + return DEFAULT_CODE; + } + + /** + * Get default error message, used for example in the REST response. + * @return Default error message. + */ + public String getDefaultError() { + return DEFAULT_ERROR; + } + +} diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java index 53e05116..c887770e 100644 --- a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java +++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProvider.java @@ -58,11 +58,12 @@ public PowerAuthEncryptionProvider(PowerAuthClient powerAuthClient, HttpCustomiz } @Override - public @Nonnull PowerAuthEncryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String ephemeralPublicKey, @Nonnull String version, String nonce, Long timestamp) throws PowerAuthEncryptionException { + public @Nonnull PowerAuthEncryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String temporaryKeyId, @Nonnull String ephemeralPublicKey, @Nonnull String version, String nonce, Long timestamp) throws PowerAuthEncryptionException { try { final GetEciesDecryptorRequest eciesDecryptorRequest = new GetEciesDecryptorRequest(); eciesDecryptorRequest.setActivationId(activationId); eciesDecryptorRequest.setApplicationKey(applicationKey); + eciesDecryptorRequest.setTemporaryKeyId(temporaryKeyId); eciesDecryptorRequest.setEphemeralPublicKey(ephemeralPublicKey); eciesDecryptorRequest.setProtocolVersion(version); eciesDecryptorRequest.setNonce(nonce); diff --git a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java index bab6972e..48a3b692 100644 --- a/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java +++ b/powerauth-restful-security-spring-annotation/src/main/java/io/getlime/security/powerauth/rest/api/spring/provider/PowerAuthEncryptionProviderBase.java @@ -80,7 +80,7 @@ public abstract class PowerAuthEncryptionProviderBase { * @throws PowerAuthEncryptionException In case PowerAuth server call fails. */ public abstract @Nonnull - PowerAuthEncryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String ephemeralPublicKey, @Nonnull String version, String nonce, Long timestamp) throws PowerAuthEncryptionException; + PowerAuthEncryptorParameters getEciesDecryptorParameters(@Nullable String activationId, @Nonnull String applicationKey, @Nonnull String temporaryKeyId, @Nonnull String ephemeralPublicKey, @Nonnull String version, String nonce, Long timestamp) throws PowerAuthEncryptionException; /** * Decrypt HTTP request body and construct object with ECIES data. Use the requestType parameter to specify @@ -136,6 +136,7 @@ public void decryptRequest(@Nonnull HttpServletRequest request, @Nonnull Type re // Prepare and validate EncryptedRequest object final EncryptedRequest encryptedRequest = new EncryptedRequest( + eciesRequest.getTemporaryKeyId(), eciesRequest.getEphemeralPublicKey(), eciesRequest.getEncryptedData(), eciesRequest.getMac(), @@ -155,6 +156,7 @@ public void decryptRequest(@Nonnull HttpServletRequest request, @Nonnull Type re final PowerAuthEncryptorParameters encryptorParameters = getEciesDecryptorParameters( activationId, applicationKey, + encryptedRequest.getTemporaryKeyId(), encryptedRequest.getEphemeralPublicKey(), version, encryptedRequest.getNonce(), @@ -165,7 +167,7 @@ public void decryptRequest(@Nonnull HttpServletRequest request, @Nonnull Type re final byte[] sharedInfo2Base = Base64.getDecoder().decode(encryptorParameters.sharedInfo2()); final ServerEncryptor serverEncryptor = encryptorFactory.getServerEncryptor( encryptorData.getEncryptorId(), - new EncryptorParameters(version, applicationKey, activationId), + new EncryptorParameters(version, applicationKey, activationId, encryptedRequest.getTemporaryKeyId()), new ServerEncryptorSecrets(secretKeyBytes, sharedInfo2Base) ); diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ActivationController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ActivationController.java index 19613619..78660447 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ActivationController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ActivationController.java @@ -60,6 +60,9 @@ *

PowerAuth protocol versions: *

    *
  • 3.0
  • + *
  • 3.1
  • + *
  • 3.2
  • + *
  • 3.3
  • *
* * @author Roman Strobl, roman.strobl@wultra.com diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/KeyStoreController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/KeyStoreController.java new file mode 100644 index 00000000..76a706b4 --- /dev/null +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/KeyStoreController.java @@ -0,0 +1,85 @@ +/* + * PowerAuth integration libraries for RESTful API applications, examples and + * related software components + * + * Copyright (C) 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.rest.api.spring.controller; + +import io.getlime.core.rest.model.base.request.ObjectRequest; +import io.getlime.core.rest.model.base.response.ObjectResponse; +import io.getlime.security.powerauth.rest.api.model.request.TemporaryKeyRequest; +import io.getlime.security.powerauth.rest.api.model.response.TemporaryKeyResponse; +import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthTemporaryKeyException; +import io.getlime.security.powerauth.rest.api.spring.service.KeyStoreService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +/** + * Controller for obtaining temporary encryption keys. + * + *

PowerAuth protocol versions: + *

    + *
  • 3.3
  • + *
+ * + * @author Petr Dvorak, petr@wultra.com + */ +@RestController("keyStoreControllerV3") +@RequestMapping(value = "/pa/v3/keystore") +public class KeyStoreController { + + private static final Logger logger = LoggerFactory.getLogger(KeyStoreController.class); + + private final KeyStoreService service; + + /** + * Default autowiring constructor. + * @param service Keystore service. + */ + @Autowired + public KeyStoreController(KeyStoreService service) { + this.service = service; + } + + /** + * Create a new temporary key. + * @param request Request for temporary key. + * @return Response with temporary key. + * @throws PowerAuthTemporaryKeyException In case temporary key cannot be returned. + */ + @PostMapping("create") + public ObjectResponse fetchTemporaryKey(@RequestBody ObjectRequest request) throws PowerAuthTemporaryKeyException { + if (request == null) { + logger.warn("Null request while fetching temporary key"); + throw new PowerAuthTemporaryKeyException(); + } + final TemporaryKeyRequest requestObject = request.getRequestObject(); + if (requestObject == null) { + logger.warn("Null request object while fetching temporary key"); + throw new PowerAuthTemporaryKeyException(); + } + if (!StringUtils.hasLength(requestObject.getJwt())) { + logger.warn("Invalid request object with empty JWT while fetching temporary key"); + throw new PowerAuthTemporaryKeyException(); + } + return new ObjectResponse<>(service.fetchTemporaryKey(requestObject)); + } + +} diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/RecoveryController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/RecoveryController.java index 0e97c72c..caa99019 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/RecoveryController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/RecoveryController.java @@ -44,6 +44,9 @@ *

PowerAuth protocol versions: *

    *
  • 3.0
  • + *
  • 3.1
  • + *
  • 3.2
  • + *
  • 3.3
  • *
* * @author Roman Strobl, roman.strobl@wultra.com @@ -85,8 +88,7 @@ public EciesEncryptedResponse confirmRecoveryCode(@RequestBody EciesEncryptedReq PowerAuthAuthenticationUtil.checkAuthentication(auth); PowerAuthVersionUtil.checkUnsupportedVersion(auth.getVersion()); - PowerAuthVersionUtil.checkMissingRequiredNonce(auth.getVersion(), request.getNonce()); - PowerAuthVersionUtil.checkMissingRequiredTimestamp(auth.getVersion(), request.getTimestamp()); + PowerAuthVersionUtil.checkEciesParameters(auth.getVersion(), request); return recoveryService.confirmRecoveryCode(request, auth); } diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SecureVaultController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SecureVaultController.java index 875c6cc6..f37845b6 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SecureVaultController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SecureVaultController.java @@ -44,6 +44,9 @@ *

PowerAuth protocol versions: *

    *
  • 3.0
  • + *
  • 3.1
  • + *
  • 3.2
  • + *
  • 3.3
  • *
* * @author Roman Strobl, roman.strobl@wultra.com @@ -100,9 +103,7 @@ public EciesEncryptedResponse unlockVault( } PowerAuthVersionUtil.checkUnsupportedVersion(header.getVersion()); - PowerAuthVersionUtil.checkMissingRequiredNonce(header.getVersion(), request.getNonce()); - PowerAuthVersionUtil.checkMissingRequiredTimestamp(header.getVersion(), request.getTimestamp()); - + PowerAuthVersionUtil.checkEciesParameters(header.getVersion(), request); return secureVaultServiceV3.vaultUnlock(header, request, httpServletRequest); } diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ServerStatusController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ServerStatusController.java index 9e422a91..7bc046f1 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ServerStatusController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/ServerStatusController.java @@ -22,6 +22,8 @@ import io.getlime.core.rest.model.base.response.ObjectResponse; import io.getlime.security.powerauth.rest.api.model.response.ServerStatusResponse; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.info.BuildProperties; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -35,6 +37,7 @@ *
  • 3.0
  • *
  • 3.1
  • *
  • 3.2
  • + *
  • 3.3
  • * * * @author Petr Dvorak, petr@wultra.com @@ -44,6 +47,13 @@ @Slf4j public class ServerStatusController { + private BuildProperties buildProperties; + + @Autowired(required = false) + public void setBuildProperties(BuildProperties buildProperties) { + this.buildProperties = buildProperties; + } + /** * Obtain server status. * @return Server status. @@ -51,7 +61,17 @@ public class ServerStatusController { @PostMapping("status") public ObjectResponse getServerStatus() { final long serverTime = new Date().getTime(); - final ServerStatusResponse response = new ServerStatusResponse(serverTime); + final String version; + final String name; + if (buildProperties != null) { + version = buildProperties.getVersion(); + name = buildProperties.getName(); + } else { + name = "UNKNOWN"; + version = "UNKNOWN"; + } + final ServerStatusResponse.Application application = new ServerStatusResponse.Application(name, version); + final ServerStatusResponse response = new ServerStatusResponse(serverTime, application); return new ObjectResponse<>(response); } diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SignatureController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SignatureController.java index c54a8645..6bbbba87 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SignatureController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/SignatureController.java @@ -36,6 +36,9 @@ *

    PowerAuth protocol versions: *

      *
    • 3.0
    • + *
    • 3.1
    • + *
    • 3.2
    • + *
    • 3.3
    • *
    * * @author Roman Strobl, roman.strobl@wultra.com diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/TokenController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/TokenController.java index 8498a691..3d492be8 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/TokenController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/TokenController.java @@ -47,6 +47,9 @@ *

    PowerAuth protocol versions: *

      *
    • 3.0
    • + *
    • 3.1
    • + *
    • 3.2
    • + *
    • 3.3
    • *
    * * @author Petr Dvorak, petr@wultra.com @@ -92,8 +95,7 @@ public EciesEncryptedResponse createToken(@RequestBody EciesEncryptedRequest req PowerAuthAuthenticationUtil.checkAuthentication(auth); PowerAuthVersionUtil.checkUnsupportedVersion(auth.getVersion()); - PowerAuthVersionUtil.checkMissingRequiredNonce(auth.getVersion(), request.getNonce()); - PowerAuthVersionUtil.checkMissingRequiredTimestamp(auth.getVersion(), request.getTimestamp()); + PowerAuthVersionUtil.checkEciesParameters(auth.getVersion(), request); return tokenServiceV3.createToken(request, auth); } diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UpgradeController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UpgradeController.java index 4fb94ea8..8c3105c8 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UpgradeController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UpgradeController.java @@ -46,6 +46,10 @@ *

    PowerAuth protocol versions: *

      *
    • 3.0
    • + *
    • 3.1
    • + *
    • 3.2
    • + *
    • 3.3
    • + * *
    * * @author Roman Strobl, roman.strobl@wultra @@ -98,8 +102,7 @@ public EciesEncryptedResponse upgradeStart(@RequestBody EciesEncryptedRequest re } PowerAuthVersionUtil.checkUnsupportedVersion(header.getVersion()); - PowerAuthVersionUtil.checkMissingRequiredNonce(header.getVersion(), request.getNonce()); - PowerAuthVersionUtil.checkMissingRequiredTimestamp(header.getVersion(), request.getTimestamp()); + PowerAuthVersionUtil.checkEciesParameters(header.getVersion(), request); return upgradeService.upgradeStart(request, header); diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UserInfoController.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UserInfoController.java index f922524b..965da52a 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UserInfoController.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/controller/UserInfoController.java @@ -42,6 +42,7 @@ *
  • 3.0
  • *
  • 3.1
  • *
  • 3.2
  • + *
  • 3.3
  • * * * @author Petr Dvorak, petr@wultra.com diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java index 20635bd2..cbc99f6f 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/exception/PowerAuthExceptionHandler.java @@ -132,4 +132,16 @@ public class PowerAuthExceptionHandler { return new ErrorResponse(ex.getDefaultCode(), ex.getMessage()); } + /** + * Handle PowerAuthTemporaryKeyException exceptions. + * @param ex Exception instance. + * @return Error response. + */ + @ExceptionHandler(value = PowerAuthTemporaryKeyException.class) + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + public @ResponseBody ErrorResponse handlePowerAuthTemporaryKeyException(PowerAuthTemporaryKeyException ex) { + logger.warn(ex.getMessage(), ex); + return new ErrorResponse(ex.getDefaultCode(), ex.getDefaultError()); + } + } diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/ActivationService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/ActivationService.java index 9613d9ac..267a558f 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/ActivationService.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/ActivationService.java @@ -127,6 +127,7 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request final String applicationKey = eciesContext.getApplicationKey(); final EciesEncryptedRequest activationData = request.getActivationData(); + final String temporaryKeyId = activationData.getTemporaryKeyId(); final String ephemeralPublicKey = activationData.getEphemeralPublicKey(); final String encryptedData = activationData.getEncryptedData(); final String mac = activationData.getMac(); @@ -161,6 +162,7 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request prepareRequest.setActivationCode(activationCode); prepareRequest.setApplicationKey(applicationKey); prepareRequest.setGenerateRecoveryCodes(shouldGenerateRecoveryCodes(identity, customAttributes, context)); + prepareRequest.setTemporaryKeyId(temporaryKeyId); prepareRequest.setEphemeralPublicKey(ephemeralPublicKey); prepareRequest.setEncryptedData(encryptedData); prepareRequest.setMac(mac); @@ -285,6 +287,7 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request createRequest.setGenerateRecoveryCodes(shouldGenerateRecoveryCodes); createRequest.setMaxFailureCount(maxFailedCount); createRequest.setApplicationKey(applicationKey); + createRequest.setTemporaryKeyId(temporaryKeyId); createRequest.setEphemeralPublicKey(ephemeralPublicKey); createRequest.setEncryptedData(encryptedData); createRequest.setMac(mac); @@ -393,6 +396,7 @@ public ActivationLayer1Response createActivation(ActivationLayer1Request request recoveryRequest.setGenerateRecoveryCodes(shouldGenerateRecoveryCodes); recoveryRequest.setApplicationKey(applicationKey); recoveryRequest.setMaxFailureCount(maxFailedCount); + recoveryRequest.setTemporaryKeyId(temporaryKeyId); recoveryRequest.setEphemeralPublicKey(ephemeralPublicKey); recoveryRequest.setEncryptedData(encryptedData); recoveryRequest.setMac(mac); diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/KeyStoreService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/KeyStoreService.java new file mode 100644 index 00000000..a0db61a8 --- /dev/null +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/KeyStoreService.java @@ -0,0 +1,84 @@ +/* + * PowerAuth integration libraries for RESTful API applications, examples and + * related software components + * + * Copyright (C) 2024 Wultra s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package io.getlime.security.powerauth.rest.api.spring.service; + +import com.wultra.security.powerauth.client.PowerAuthClient; +import com.wultra.security.powerauth.client.model.error.PowerAuthClientException; +import com.wultra.security.powerauth.client.model.request.TemporaryPublicKeyRequest; +import com.wultra.security.powerauth.client.model.response.TemporaryPublicKeyResponse; +import io.getlime.security.powerauth.rest.api.model.request.TemporaryKeyRequest; +import io.getlime.security.powerauth.rest.api.model.response.TemporaryKeyResponse; +import io.getlime.security.powerauth.rest.api.spring.exception.PowerAuthTemporaryKeyException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Key store service for obtaining temporary encryption keys. + * + * @author Petr Dvorak, petr@wultra.com + */ +@Service +public class KeyStoreService { + + private static final Logger logger = LoggerFactory.getLogger(KeyStoreService.class); + + private final PowerAuthClient powerAuthClient; + private final HttpCustomizationService httpCustomizationService; + + /** + * Default autowiring constructor. + * @param powerAuthClient PowerAuth Client + * @param httpCustomizationService Customization service. + */ + @Autowired + public KeyStoreService(PowerAuthClient powerAuthClient, HttpCustomizationService httpCustomizationService) { + this.powerAuthClient = powerAuthClient; + this.httpCustomizationService = httpCustomizationService; + } + + /** + * Fetch a temporary public key with provided parameters. + * @param request Temporary public key request. + * @return Response with temporary public key. + * @throws PowerAuthTemporaryKeyException In case internal API call fails. + */ + public TemporaryKeyResponse fetchTemporaryKey(TemporaryKeyRequest request) throws PowerAuthTemporaryKeyException { + try { + final TemporaryPublicKeyRequest publicKeyRequest = new TemporaryPublicKeyRequest(); + publicKeyRequest.setJwt(request.getJwt()); + + final TemporaryPublicKeyResponse temporaryPublicKeyResponse = powerAuthClient.fetchTemporaryPublicKey( + publicKeyRequest, + httpCustomizationService.getQueryParams(), + httpCustomizationService.getHttpHeaders() + ); + + final TemporaryKeyResponse response = new TemporaryKeyResponse(); + response.setJwt(temporaryPublicKeyResponse.getJwt()); + return response; + } catch (PowerAuthClientException ex) { + logger.warn("PowerAuth fetching temporary key failed, error: {}", ex.getMessage()); + logger.debug(ex.getMessage(), ex); + throw new PowerAuthTemporaryKeyException(); + } + } +} diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/RecoveryService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/RecoveryService.java index 0ea79a6f..a8ede082 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/RecoveryService.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/RecoveryService.java @@ -83,6 +83,7 @@ public EciesEncryptedResponse confirmRecoveryCode(EciesEncryptedRequest request, final ConfirmRecoveryCodeRequest confirmRequest = new ConfirmRecoveryCodeRequest(); confirmRequest.setActivationId(activationId); confirmRequest.setApplicationKey(applicationKey); + confirmRequest.setTemporaryKeyId(request.getTemporaryKeyId()); confirmRequest.setEphemeralPublicKey(request.getEphemeralPublicKey()); confirmRequest.setEncryptedData(request.getEncryptedData()); confirmRequest.setMac(request.getMac()); diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/SecureVaultService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/SecureVaultService.java index f0304ba6..98ed816b 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/SecureVaultService.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/SecureVaultService.java @@ -112,6 +112,7 @@ public EciesEncryptedResponse vaultUnlock(PowerAuthSignatureHttpHeader header, unlockRequest.setSignatureType(signatureType); unlockRequest.setSignatureVersion(signatureVersion); unlockRequest.setSignedData(data); + unlockRequest.setTemporaryKeyId(request.getTemporaryKeyId()); unlockRequest.setEphemeralPublicKey(request.getEphemeralPublicKey()); unlockRequest.setEncryptedData(request.getEncryptedData()); unlockRequest.setMac(request.getMac()); diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/TokenService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/TokenService.java index 56312032..cd409ff7 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/TokenService.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/TokenService.java @@ -101,6 +101,7 @@ public EciesEncryptedResponse createToken(EciesEncryptedRequest request, final CreateTokenRequest tokenRequest = new CreateTokenRequest(); tokenRequest.setActivationId(activationId); tokenRequest.setApplicationKey(applicationKey); + tokenRequest.setTemporaryKeyId(request.getTemporaryKeyId()); tokenRequest.setEphemeralPublicKey(request.getEphemeralPublicKey()); tokenRequest.setEncryptedData(request.getEncryptedData()); tokenRequest.setMac(request.getMac()); diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UpgradeService.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UpgradeService.java index c8bfa948..2e34f45f 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UpgradeService.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/service/UpgradeService.java @@ -97,6 +97,7 @@ public EciesEncryptedResponse upgradeStart(EciesEncryptedRequest request, PowerA final StartUpgradeRequest upgradeRequest = new StartUpgradeRequest(); upgradeRequest.setActivationId(activationId); upgradeRequest.setApplicationKey(applicationKey); + upgradeRequest.setTemporaryKeyId(request.getTemporaryKeyId()); upgradeRequest.setEphemeralPublicKey(request.getEphemeralPublicKey()); upgradeRequest.setEncryptedData(request.getEncryptedData()); upgradeRequest.setMac(request.getMac()); diff --git a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/util/PowerAuthVersionUtil.java b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/util/PowerAuthVersionUtil.java index cad9ecf7..9537df46 100644 --- a/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/util/PowerAuthVersionUtil.java +++ b/powerauth-restful-security-spring/src/main/java/io/getlime/security/powerauth/rest/api/spring/util/PowerAuthVersionUtil.java @@ -19,6 +19,7 @@ */ package io.getlime.security.powerauth.rest.api.spring.util; +import io.getlime.security.powerauth.rest.api.model.request.EciesEncryptedRequest; import io.getlime.security.powerauth.rest.api.spring.exception.authentication.PowerAuthInvalidRequestException; import lombok.extern.slf4j.Slf4j; @@ -47,7 +48,7 @@ private PowerAuthVersionUtil() { /** * Set containing all the supported versions of PowerAuth. */ - private static final Set SUPPORTED_VERSIONS = Set.of("3.0", "3.1", "3.2"); + private static final Set SUPPORTED_VERSIONS = Set.of("3.0", "3.1", "3.2", "3.3"); /** * Check if the provided version string is "3.0". @@ -69,6 +70,16 @@ private static boolean isVersion3_1(final String version) { return "3.1".equals(version); } + /** + * Check if the provided version string is "3.2". + * + * @param version Version string to be checked. + * @return true if the version is "3.2", false otherwise. + */ + private static boolean isVersion3_2(final String version) { + return "3.2".equals(version); + } + /** * Checks if the provided PowerAuth protocol version is unsupported. * Throws an exception if the version is unsupported. @@ -113,6 +124,35 @@ public static void checkMissingRequiredTimestamp(String version, Long timestamp) } } + /** + * Checks if temporary key ID is missing for the provided PowerAuth protocol version. + * Throws an exception if the temporary key ID is required and missing. + * + * @param version Version string to be checked. + * @param temporaryKeyId Temporary key ID value to be verified. + * @throws PowerAuthInvalidRequestException If timestamp is required and missing. + */ + public static void checkMissingRequiredTemporaryKeyId(String version, String temporaryKeyId) throws PowerAuthInvalidRequestException { + if (isMissingRequiredTemporaryKeyId(version, temporaryKeyId)) { + logger.warn("Missing temporary key ID in ECIES request data for version {}", version); + throw new PowerAuthInvalidRequestException("Missing temporary kdy ID in ECIES request data for version " + version); + } + } + + /** + * Checks if required ECIES parameters are missing for the provided PowerAuth protocol version. + * Throws an exception if the required parameter is missing. + * + * @param version Version string to be checked. + * @param request Request to be verified. + * @throws PowerAuthInvalidRequestException If timestamp is required and missing. + */ + public static void checkEciesParameters(String version, EciesEncryptedRequest request) throws PowerAuthInvalidRequestException { + checkMissingRequiredNonce(version, request.getNonce()); + checkMissingRequiredTimestamp(version, request.getTimestamp()); + checkMissingRequiredTemporaryKeyId(version, request.getTemporaryKeyId()); + } + /** * Checks if the provided PowerAuth protocol version is unsupported. * @@ -146,4 +186,19 @@ private static boolean isMissingRequiredTimestamp(String version, Long timestamp !isVersion3_0(version) && !isVersion3_1(version); } + + /** + * Checks if temporary key ID is missing for the provided PowerAuth protocol version. + * + * @param version Version string to be checked. + * @param temporaryKeyId Temporary key ID + * @return true if temporary key ID is required and missing, false otherwise. + */ + private static boolean isMissingRequiredTemporaryKeyId(String version, String temporaryKeyId) { + return temporaryKeyId == null && + !isVersion3_0(version) && + !isVersion3_1(version) && + !isVersion3_2(version); + } + } diff --git a/powerauth-restful-security-spring/src/test/java/io/getlime/security/powerauth/rest/api/spring/PowerAuthVersionUtilTest.java b/powerauth-restful-security-spring/src/test/java/io/getlime/security/powerauth/rest/api/spring/PowerAuthVersionUtilTest.java index 6d15f81a..73b67fb3 100644 --- a/powerauth-restful-security-spring/src/test/java/io/getlime/security/powerauth/rest/api/spring/PowerAuthVersionUtilTest.java +++ b/powerauth-restful-security-spring/src/test/java/io/getlime/security/powerauth/rest/api/spring/PowerAuthVersionUtilTest.java @@ -23,6 +23,8 @@ import io.getlime.security.powerauth.rest.api.spring.util.PowerAuthVersionUtil; import org.junit.jupiter.api.Test; +import java.util.UUID; + import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -62,4 +64,12 @@ void testMissingRequiredTimestamp() { assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredTimestamp("3.1", null)); assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredTimestamp("3.2", 1630234567890L)); } + + @Test + void testMissingRequiredTemporaryKeyId() { + assertThrows(PowerAuthInvalidRequestException.class, () -> PowerAuthVersionUtil.checkMissingRequiredTemporaryKeyId("3.3", null)); + assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredTemporaryKeyId("3.1", null)); + assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredTemporaryKeyId("3.2", null)); + assertDoesNotThrow(() -> PowerAuthVersionUtil.checkMissingRequiredTemporaryKeyId("3.3", UUID.randomUUID().toString())); + } } \ No newline at end of file