Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.pubnub.internal.PubNubUtil.kt Maven / Gradle / Ivy
package com.pubnub.internal
import com.pubnub.api.PubNubException
import com.pubnub.api.v2.BasePNConfiguration
import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid
import okhttp3.Request
import okio.Buffer
import org.slf4j.LoggerFactory
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import java.net.URLEncoder
import java.nio.charset.Charset
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.util.Locale
import java.util.TreeSet
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
internal object PubNubUtil {
private val log = LoggerFactory.getLogger("PubNubUtil")
private const val CHARSET = "UTF-8"
const val SIGNATURE_QUERY_PARAM_NAME = "signature"
const val TIMESTAMP_QUERY_PARAM_NAME = "timestamp"
const val AUTH_QUERY_PARAM_NAME = "auth"
fun replaceLast(
string: String,
toReplace: String,
replacement: String,
): String {
val pos = string.lastIndexOf(toReplace)
return if (pos > -1) {
string.substring(0, pos) + replacement +
string.substring(
pos + toReplace.length,
string.length,
)
} else {
string
}
}
/**
* Returns decoded String
*
* @param stringToEncode , input string
* @return , decoded string
*/
fun urlDecode(stringToEncode: String?): String? {
return try {
URLDecoder.decode(stringToEncode, CHARSET)
} catch (e: UnsupportedEncodingException) {
null
}
}
fun signRequest(
originalRequest: Request,
pnConfiguration: BasePNConfiguration,
timestamp: Int,
): Request {
// only sign if we have a secret key in place.
if (!pnConfiguration.secretKey.isValid()) {
return originalRequest
}
val signature = generateSignature(
originalRequest,
timestamp,
pnConfiguration.subscribeKey,
pnConfiguration.publishKey,
pnConfiguration.secretKey,
)
val rebuiltUrl =
originalRequest.url.newBuilder()
.addQueryParameter("timestamp", timestamp.toString())
.addQueryParameter("signature", signature)
.build()
return originalRequest.newBuilder().url(rebuiltUrl).build()
}
fun shouldSignRequest(configuration: BasePNConfiguration): Boolean {
return configuration.secretKey.isValid()
}
fun generateSignature(
requestURL: String,
queryParams: MutableMap,
method: String,
requestBody: String?,
timestamp: Int,
subscribeKey: String,
publishKey: String,
secretKey: String,
): String {
val signatureBuilder = StringBuilder()
queryParams["timestamp"] = timestamp.toString()
val classic = true
val encodedQueryString =
if (classic) {
preparePamArguments(queryParams)
} else {
preparePamArguments("$requestURL×tamp=$timestamp")
}
val isV2Signature: Boolean = !(requestURL.startsWith("/publish") && method.equals("post", ignoreCase = true))
if (!isV2Signature) {
signatureBuilder.append(subscribeKey).append("\n")
signatureBuilder.append(publishKey).append("\n")
signatureBuilder.append(requestURL).append("\n")
signatureBuilder.append(encodedQueryString)
} else {
signatureBuilder.append(method.uppercase(Locale.getDefault())).append("\n")
signatureBuilder.append(publishKey).append("\n")
signatureBuilder.append(requestURL).append("\n")
signatureBuilder.append(encodedQueryString).append("\n")
signatureBuilder.append(requestBody)
}
var signature = ""
try {
signature = signSHA256(secretKey, signatureBuilder.toString())
if (isV2Signature) {
signature = removeTrailingEqualSigns(signature)
signature = "v2.$signature"
}
} catch (e: PubNubException) {
log.warn("signature failed on SignatureInterceptor: $e")
} catch (e: UnsupportedEncodingException) {
log.warn("signature failed on SignatureInterceptor: $e")
}
return signature
}
internal fun signSHA256(
key: String,
data: String,
): String {
val sha256HMAC: Mac
val hmacData: ByteArray
val secretKey = SecretKeySpec(key.toByteArray(charset(CHARSET)), "HmacSHA256")
sha256HMAC =
try {
Mac.getInstance("HmacSHA256")
} catch (e: NoSuchAlgorithmException) {
throw com.pubnub.internal.vendor.Crypto.newCryptoError(0, e)
}
try {
sha256HMAC.init(secretKey)
} catch (e: InvalidKeyException) {
throw com.pubnub.internal.vendor.Crypto.newCryptoError(0, e)
}
hmacData = sha256HMAC.doFinal(data.toByteArray(charset(CHARSET)))
val signed =
String(
com.pubnub.internal.vendor.Base64.encode(hmacData, com.pubnub.internal.vendor.Base64.NO_WRAP),
Charset.forName(CHARSET),
)
.replace('+', '-')
.replace('/', '_')
return signed
}
private fun generateSignature(
request: Request,
timestamp: Int,
subscribeKey: String,
publishKey: String,
secretKey: String,
): String {
val queryParams: MutableMap = mutableMapOf()
for (queryKey: String in request.url.queryParameterNames) {
val value = request.url.queryParameter(queryKey)
if (value != null) {
queryParams[queryKey] = value
}
}
return generateSignature(
request.url.encodedPath,
queryParams,
request.method,
requestBodyToString(request),
timestamp,
subscribeKey,
publishKey,
secretKey,
)
}
fun removeTrailingEqualSigns(signature: String): String {
var cleanSignature = signature
while (cleanSignature[cleanSignature.length - 1] == '=') {
cleanSignature = cleanSignature.substring(0, cleanSignature.length - 1)
}
return cleanSignature
}
internal fun requestBodyToString(request: Request): String? {
if (request.body == null) {
return ""
}
try {
val buffer = Buffer()
request.body!!.writeTo(buffer)
return buffer.readUtf8()
} catch (e: IOException) {
e.printStackTrace()
}
return ""
}
internal fun preparePamArguments(pamArgs: Map): String {
val pamKeys: Set = TreeSet(pamArgs.keys)
var stringifiedArguments = ""
var i = 0
for (pamKey in pamKeys) {
if (i != 0) {
stringifiedArguments = "$stringifiedArguments&"
}
stringifiedArguments = stringifiedArguments + pamKey + "=" + pamEncode(pamArgs[pamKey]!!)
i += 1
}
return stringifiedArguments
}
private fun preparePamArguments(encodedQueryString: String): String {
return encodedQueryString.split("&")
.toSortedSet()
.map { pamEncode(it, true) }
.joinToString("&")
}
/**
* Returns encoded String
*
* @param stringToEncode , input string
* @return , encoded string
*/
internal fun pamEncode(
stringToEncode: String,
alreadyPercentEncoded: Boolean = false,
): String {
// !'()*~
return if (alreadyPercentEncoded) {
stringToEncode
} else {
URLEncoder.encode(stringToEncode, "UTF-8")
.replace("+", "%20")
}.run {
replace("*", "%2A")
}
}
internal fun maybeAddEeQueryParam(queryParams: MutableMap) {
queryParams["ee"] = ""
}
}
internal fun List.toCsv(): String {
if (this.isNotEmpty()) {
return this.joinToString(",")
}
return ","
}