bot-connector-iadvize.24.3.5.source-code.IadvizeSecurity.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2017/2022 e-voyageurs technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.tock.bot.connector.iadvize
import ai.tock.shared.exception.rest.BadRequestException
import ai.tock.shared.jackson.mapper
import io.vertx.core.http.HttpMethod
import io.vertx.ext.web.RoutingContext
import mu.KotlinLogging
import java.math.BigInteger
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
/**
* Utility object : calculate HMAC signature (protocol used for iAdvize message authentication)
*/
class IadvizeSecurity(private val secretToken: String) {
companion object {
const val HMAC_SHA256 = "HmacSHA256"
const val HEADER_NAME = "X-iAdvize-Signature"
const val ERROR_MESSAGE = "IAdvize signature validation failed : Invalid hash"
}
private val logger = KotlinLogging.logger {}
fun validatePayloads(context: RoutingContext){
val payloads = if(HttpMethod.GET == context.request().method()){
// For GET requests, hash signature is computed by hashing the raw query string
context.request().query()
} else {
// For POST, PUT... requests, hash signature is computed by hashing the raw body string
context.body().asString()
} ?: ""
val xIAdvizeSignatureComputed = calculateHmacSha256(secretToken, payloads)
val xIAdvizeSignatureHeader = context.request().getHeader(HEADER_NAME)
if(xIAdvizeSignatureComputed != xIAdvizeSignatureHeader){
val error = mapper.writeValueAsString(
Error(secretToken, payloads, xIAdvizeSignatureComputed, xIAdvizeSignatureHeader)
)
logger.error { "$ERROR_MESSAGE $error" }
throw BadRequestException(ERROR_MESSAGE)
}
}
private fun calculateHmacSha256(secretKey: String, message: String): String {
val mac = Mac.getInstance(HMAC_SHA256)
val secretKeySpec = SecretKeySpec(secretKey.toByteArray(Charsets.UTF_8), HMAC_SHA256)
mac.init(secretKeySpec)
val hmacSha256 = mac.doFinal(message.toByteArray(Charsets.UTF_8))
return String.format("sha256=%064x", BigInteger(1, hmacSha256))
}
}
data class Error(val secretToken: String,
val payloads: String,
val xIAdvizeSignatureComputed: String,
val xIAdvizeSignatureHeader: String)