From b4a6308d5ae3a46e6a7937b45061f6df46623bf8 Mon Sep 17 00:00:00 2001 From: "gowthaman.b" Date: Sun, 12 Nov 2023 09:18:27 +0530 Subject: [PATCH] add tamper protection --- src/main/kotlin/com/restapi/Main.kt | 22 +++++++++++++++++++--- src/main/kotlin/com/restapi/domain/db.kt | 4 +++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/restapi/Main.kt b/src/main/kotlin/com/restapi/Main.kt index 73be2e7..f8d3d72 100644 --- a/src/main/kotlin/com/restapi/Main.kt +++ b/src/main/kotlin/com/restapi/Main.kt @@ -15,6 +15,7 @@ import com.restapi.domain.Session import com.restapi.domain.Session.objectMapper import com.restapi.domain.Session.redis import com.restapi.domain.Session.setAuthorizedUser +import com.restapi.domain.Session.signPayload import io.ebean.DataIntegrityException import io.ebean.DuplicateKeyException import io.javalin.Javalin @@ -32,9 +33,12 @@ import java.net.http.HttpRequest import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse.BodyHandlers import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import java.util.* import java.util.concurrent.TimeUnit import kotlin.jvm.optionals.getOrDefault + fun main(args: Array) { val logger = LoggerFactory.getLogger("api") val adminRole = Role.Standard(Action.ADMIN) @@ -104,6 +108,12 @@ fun main(args: Array) { it.result(atResponse.accessToken).contentType(ContentType.TEXT_PLAIN) } + get("/keys") { + //for the UI to validate signature and response + it.json( + Session.jwk() + ) + } } before("/api/*") { ctx -> @@ -121,15 +131,21 @@ fun main(args: Array) { setAuthorizedUser(validateAuthToken(authToken = authToken)) - if(appConfig.enforcePayloadEncryption()){ + if (appConfig.enforcePayloadEncryption()) { //todo: decrypt the request from user } } after("/api/*") { - it.header("X-Signature", Session.sign(it.body())) + val md = MessageDigest.getInstance("SHA-512") + md.update((it.result() ?: "").toByteArray()) + val aMessageDigest = md.digest() - if(appConfig.enforcePayloadEncryption()){ + val outEncoded: String = Base64.getEncoder().encodeToString(aMessageDigest) + it.header("X-Checksum", outEncoded) + it.header("X-Signature", signPayload(outEncoded)) + + if (appConfig.enforcePayloadEncryption()) { //todo:, encrypt and set the response back to user } diff --git a/src/main/kotlin/com/restapi/domain/db.kt b/src/main/kotlin/com/restapi/domain/db.kt index 3634249..010292b 100644 --- a/src/main/kotlin/com/restapi/domain/db.kt +++ b/src/main/kotlin/com/restapi/domain/db.kt @@ -13,6 +13,7 @@ import io.ebean.config.DatabaseConfig import io.ebean.config.TenantMode import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.util.io.pem.PemReader +import org.jose4j.jwk.JsonWebKey import org.jose4j.jwk.PublicJsonWebKey import org.jose4j.jwk.RsaJsonWebKey import org.jose4j.jwk.RsaJwkGenerator @@ -108,7 +109,7 @@ object Session { } - fun sign(payload: String): String { + fun signPayload(payload: String): String { // Create a new JsonWebSignature val jws = JsonWebSignature() @@ -161,6 +162,7 @@ object Session { fun currentUser() = currentUser.get().userName fun currentTenant() = currentUser.get().tenant fun currentRoles() = currentUser.get().roles + fun jwk() = keypair.toParams(JsonWebKey.OutputControlLevel.PUBLIC_ONLY) fun Database.findByEntityAndId(entity: String, id: String): DataModel { return find(DataModel::class.java)