Ask Your Question
2

How can an HMAC string be generated for creating a SAS token in Kotlin Service Bus?

asked 2023-06-06 18:12:09 +0000

devzero gravatar image

edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
2

answered 2023-06-06 18:31:02 +0000

djk gravatar image

To generate an HMAC string for creating a SAS token in Kotlin Service Bus, you can use the following code:

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.util.*
import kotlin.experimental.and

fun generateSasToken(uri: String, keyName: String, key: String, expiry: Long): String {
    val stringToSign = "$uri\n$expiry"
    val signature = encodeToBase64(hmacSha256(key, stringToSign))
    return "SharedAccessSignature sr=${UriEncoder.encode(uri)}&sig=${UriEncoder.encode(signature)}&se=$expiry&skn=$keyName"
}

private fun hmacSha256(key: String, input: String): ByteArray {
    val keyBytes = key.toByteArray()
    val secretKeySpec = SecretKeySpec(keyBytes, "HmacSHA256")
    val mac = Mac.getInstance("HmacSHA256")
    mac.init(secretKeySpec)
    return mac.doFinal(input.toByteArray())
}

private fun encodeToBase64(bytes: ByteArray): String {
    return Base64.getEncoder().encodeToString(bytes).replace("\r\n", "")
}

object UriEncoder {
    private val hexCharLookup = charArrayOf(
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    )

    fun encode(input: String): String {
        val result = StringBuilder()
        for (c in input) {
            if (c.isLetterOrDigit()) {
                result.append(c)
            } else {
                val bytes = c.toUtf8Bytes()
                for (b in bytes) {
                    result.append('%')
                    result.append(hexCharLookup[(b and 0xF0.toByte()).toInt() shr 4])
                    result.append(hexCharLookup[(b and 0x0F).toInt()])
                }
            }
        }
        return result.toString()
    }

    private fun Char.toUtf8Bytes(): ByteArray {
        val charAsInt = this.toInt()
        return when {
            charAsInt <= 0x7f -> byteArrayOf(charAsInt.toByte())
            charAsInt <= 0x7ff -> byteArrayOf((0xc0 or (charAsInt shr 6)).toByte(), (0x80 or (charAsInt and 0x3f)).toByte())
            charAsInt <= 0xffff -> byteArrayOf(
                (0xe0 or (charAsInt shr 12)).toByte(),
                (0x80 or ((charAsInt shr 6) and 0x3f)).toByte(),
                (0x80 or (charAsInt and 0x3f)).toByte()
            )
            else -> byteArrayOf(
                (0xf0 or (charAsInt shr 18)).toByte(),
                (0x80 or ((charAsInt shr 12) and 0x3f)).toByte(),
                (0x80 or ((charAsInt shr 6) and 0x3f)).toByte(),
                (0x80 or (charAsInt and 0x3f)).toByte()
            )
        }
    }
}

Then, you can use the generateSasToken function to generate a SAS token for your Service Bus namespace or entity. You need to pass in the URI of the namespace or entity, the key name and key value for the Shared Access Policy that you want to use, and the expiry time in seconds since the Unix epoch. The function will return a string in the format of "SharedAccessSignature sr={URI}&sig={HMACSHA256SIGNATURE}&se={EXPIRYTIME}&skn={KEYNAME}".

edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account. This space is reserved only for answers. If you would like to engage in a discussion, please instead post a comment under the question or an answer that you would like to discuss

Add Answer


Question Tools

Stats

Asked: 2023-06-06 18:12:09 +0000

Seen: 17 times

Last updated: Jun 06 '23