javax.crypto.Mac Scala Examples

The following examples show how to use javax.crypto.Mac. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: WithMacSigningKey.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.mac.jca

import javax.crypto.{KeyGenerator, Mac, SecretKey}
import javax.crypto.spec.SecretKeySpec
import cats.Id
import cats.effect.Sync
import cats.instances.either._
import cats.syntax.either._
import tsec.keygen.symmetric.{IdKeyGen, SymmetricKeyGen}
import tsec.mac.{MAC, MacAPI}

protected[tsec] abstract class WithMacSigningKey[A](algo: String, keyLenBits: Int)
    extends MacKeyGenerator[A]
    with MacAPI[A, MacSigningKey] {

  implicit def syncMac[F[_]](implicit F: Sync[F]): JCAMessageAuth[F, A] =
    new JCAMessageAuth[F, A]() {

      def algorithm: String = algo

      protected[tsec] def genInstance: F[Mac] = F.delay(Mac.getInstance(algo))

      protected[tsec] def signInternal(m: Mac, k: SecretKey, content: Array[Byte]): F[MAC[A]] = F.delay {
        m.init(k)
        MAC[A](m.doFinal(content))
      }
    }

  implicit val macInstanceEither: JCAMessageAuth[MacErrorM, A] =
    new JCAMessageAuth[MacErrorM, A]() {

      def algorithm: String = algo

      protected[tsec] def genInstance: MacErrorM[Mac] = Either.catchNonFatal(Mac.getInstance(algo))

      protected[tsec] def signInternal(m: Mac, k: SecretKey, content: Array[Byte]): MacErrorM[MAC[A]] =
        Either.catchNonFatal {
          m.init(k)
          MAC[A](m.doFinal(content))
        }
    }

  implicit def genKeyMac[F[_]](implicit F: Sync[F]): MacKeyGen[F, A] =
    new SymmetricKeyGen[F, A, MacSigningKey] {
      def generateKey: F[MacSigningKey[A]] =
        F.delay(impl.generateKeyUnsafe())

      def build(rawKey: Array[Byte]): F[MacSigningKey[A]] =
        F.delay(MacSigningKey[A](new SecretKeySpec(rawKey, algo)))
    }

  implicit def genKeyMacError: MacKeyGen[MacErrorM, A] = new MacKeyGen[MacErrorM, A] {
    def generateKey: MacErrorM[MacSigningKey[A]] =
      Either.catchNonFatal(impl.generateKeyUnsafe())

    def build(rawKey: Array[Byte]): MacErrorM[MacSigningKey[A]] =
      Either.catchNonFatal(impl.buildKeyUnsafe(rawKey))
  }

  implicit val idKeygenMac: IdKeyGen[A, MacSigningKey] =
    new IdKeyGen[A, MacSigningKey] {
      def generateKey: Id[MacSigningKey[A]] =
        impl.generateKeyUnsafe()

      def build(rawKey: Array[Byte]): Id[MacSigningKey[A]] =
        impl.buildKeyUnsafe(rawKey)
    }

  object impl {
    def generator: KeyGenerator = KeyGenerator.getInstance(algo)

    def generateKeyUnsafe(): MacSigningKey[A] = MacSigningKey.fromJavaKey[A](generator.generateKey())

    def buildKeyUnsafe(key: Array[Byte]): MacSigningKey[A] =
      MacSigningKey.fromJavaKey[A](new SecretKeySpec(key, algo))
  }

  implicit def keyGen: MacKeyGenerator[A] = this
} 
Example 2
Source File: Hmac.scala    From incubator-toree   with Apache License 2.0 5 votes vote down vote up
package org.apache.toree.communication.security

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import org.apache.toree.communication.security.HmacAlgorithm.HmacAlgorithm

object HmacAlgorithm extends Enumeration {
  type HmacAlgorithm = Value

  def apply(key: String) = Value(key)

  val MD5     = Value("HmacMD5")
  val SHA1    = Value("HmacSHA1")
  val SHA256  = Value("HmacSHA256")
}

object Hmac {
  
  def apply(key: String, algorithm: HmacAlgorithm = HmacAlgorithm.SHA256) =
    new Hmac(key, algorithm)
  
  def newMD5(key: String): Hmac     = this(key, HmacAlgorithm.MD5)
  def newSHA1(key: String): Hmac    = this(key, HmacAlgorithm.SHA1)
  def newSHA256(key: String): Hmac  = this(key, HmacAlgorithm.SHA256)
}

class Hmac(
  val key: String,
  val algorithm: HmacAlgorithm = HmacAlgorithm.SHA256
) {

  private var mac: Mac = _
  private var secretKeySpec: SecretKeySpec = _

  if (key.nonEmpty) {
    mac = Mac.getInstance(algorithm.toString)
    secretKeySpec = new SecretKeySpec(key.getBytes, algorithm.toString)
    mac.init(secretKeySpec)
  }

  def apply(items: String*): String = digest(items)

  def digest(items: Seq[String]): String = if (key.nonEmpty) {
    mac synchronized {
      items.map(_.getBytes("UTF-8")).foreach(mac.update)
      mac.doFinal().map("%02x" format _).mkString
    }
  } else ""
} 
Example 3
Source File: JwsAlgorithm.scala    From akka-http-session   with Apache License 2.0 5 votes vote down vote up
package com.softwaremill.session

import java.nio.charset.StandardCharsets.UTF_8
import java.security.spec.PKCS8EncodedKeySpec
import java.security.{KeyFactory, PrivateKey, Signature}
import java.util.Base64

import com.typesafe.config.Config
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import scala.util.{Failure, Success, Try}

sealed trait JwsAlgorithm {
  def value: String
  def sign(message: String): String

  protected def encode(bytes: Array[Byte]): String =
    Base64.getUrlEncoder.withoutPadding().encodeToString(bytes)
}

object JwsAlgorithm {

  case class Rsa(privateKey: PrivateKey) extends JwsAlgorithm {

    override val value: String = "RS256"

    override def sign(message: String): String = {
      val privateSignature: Signature = Signature.getInstance("SHA256withRSA")
      privateSignature.initSign(privateKey)
      privateSignature.update(message.getBytes(UTF_8))

      encode(privateSignature.sign())
    }
  }

  object Rsa {

    def fromConfig(jwsConfig: Config): Try[Rsa] = {

      def readKeyFromConf(): Try[String] = {
        val configKey = "rsa-private-key"
        Option(jwsConfig.hasPath(configKey))
          .filter(identity)
          .flatMap(_ => Option(jwsConfig.getString(configKey)))
          .filter(_.trim.nonEmpty)
          .map(_.replaceAll("\\s", "").replaceAll("-----[^-]+-----", ""))
          .map(Success(_))
          .getOrElse(Failure(new IllegalArgumentException(
            "akka.http.session.jws.rsa-private-key must be defined in order to use alg = RS256")))
      }

      readKeyFromConf()
        .flatMap { key =>
          Try {
            val keyFactory = KeyFactory.getInstance("RSA")
            val privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder.decode(key)))
            Rsa(privateKey)
          }.recoverWith {
            case ex => Failure(new IllegalArgumentException("Invalid RSA private key", ex))
          }
        }
    }
  }

  case class HmacSHA256(serverSecret: String) extends JwsAlgorithm {
    override val value: String = "HS256"
    override def sign(message: String): String = {
      val key = serverSecret.getBytes("UTF-8")
      val mac = Mac.getInstance("HmacSHA256")
      mac.init(new SecretKeySpec(key, "HmacSHA256"))
      encode(mac.doFinal(message.getBytes("utf-8")))
    }
  }

} 
Example 4
Source File: Crypto.scala    From akka-http-session   with Apache License 2.0 5 votes vote down vote up
package com.softwaremill.session

import java.security.MessageDigest
import java.util

import com.softwaremill.session.SessionUtil._
import javax.crypto.spec.SecretKeySpec
import javax.crypto.{Cipher, Mac}

object Crypto {
  def sign_HmacSHA1_hex(message: String, secret: String): String = {
    val key = secret.getBytes("UTF-8")
    val mac = Mac.getInstance("HmacSHA1")
    mac.init(new SecretKeySpec(key, "HmacSHA1"))
    toHexString(mac.doFinal(message.getBytes("utf-8")))
  }

  def sign_HmacSHA256_base64_v0_5_2(message: String, secret: String): String = {
    val key = secret.getBytes("UTF-8")
    val mac = Mac.getInstance("HmacSHA256")
    mac.init(new SecretKeySpec(key, "HmacSHA256"))
    SessionUtil.toBase64_v0_5_2(mac.doFinal(message.getBytes("utf-8")))
  }

  def encrypt_AES(value: String, secret: String): String = {
    val raw = util.Arrays.copyOf(secret.getBytes("utf-8"), 16)
    val skeySpec = new SecretKeySpec(raw, "AES")
    val cipher = Cipher.getInstance("AES")
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec)
    toHexString(cipher.doFinal(value.getBytes("utf-8")))
  }

  def decrypt_AES(value: String, secret: String): String = {
    val raw = util.Arrays.copyOf(secret.getBytes("utf-8"), 16)
    val skeySpec = new SecretKeySpec(raw, "AES")
    val cipher = Cipher.getInstance("AES")
    cipher.init(Cipher.DECRYPT_MODE, skeySpec)
    new String(cipher.doFinal(hexStringToByte(value)))
  }

  def hash_SHA256(value: String): String = {
    val digest = MessageDigest.getInstance("SHA-256")
    toHexString(digest.digest(value.getBytes("UTF-8")))
  }
} 
Example 5
Source File: S3UploadController.scala    From scuruto   with MIT License 5 votes vote down vote up
package controller.upload

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import _root_.controller.UploadController
import lib._
import model.Upload
import model.typebinder.UserId
import org.apache.commons.codec.binary.Base64
import org.joda.time.format.DateTimeFormat
import org.joda.time._
import skinny._

object S3UploadController extends UploadController {

  override def destination: UploadDestination = UploadDestination("s3")

  // --------------
  // sign
  val AWS_ACCESS_KEY = "AWS_ACCESS_KEY"
  val AWS_SECRET_KEY = "AWS_SECRET_KEY"
  def sign: String = {
    val userId = policiesParams.getAs[UserId]("user_id").get
    val filename = params("filename")
    val ext = filename.split('.').last
    val seed = userId.value + "_" + DateTime.now().toString + "_" + filename
    val baseDir = SkinnyConfig.stringConfigValue("upload.s3.baseDir").getOrElse("")
    val path = baseDir + new Sha1Digest(seed).digestString + "." + ext

    val bucket = SkinnyConfig.stringConfigValue("upload.s3.bucket").getOrElse(throw new IllegalArgumentException)
    val policyDocument = toJSONString(Map(
      "expiration" -> new DateTime(DateTimeZone.UTC).plusMinutes(1).toString(DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z")),
      "conditions" -> Array(
        Map("bucket" -> bucket),
        Map("key" -> path),
        Map("Content-Type" -> policiesParams.getAs("content_type")),
        Array("content-length-range", policiesParams.getAs("size"), policiesParams.getAs("size"))
      )
    ), underscoreKeys = false)
    val policy = Base64.encodeBase64String(policyDocument.getBytes("UTF-8"))
    val hmac = Mac.getInstance("HmacSHA1")
    hmac.init(new SecretKeySpec(sys.env(AWS_SECRET_KEY).getBytes("UTF-8"), "HmacSHA1"))
    val signature = Base64.encodeBase64String(hmac.doFinal(policy.getBytes("UTF-8")))

    // add to uploads table
    Upload.createWithAttributes(
      'user_id -> userId.value,
      'original_filename -> filename,
      'filename -> path
    )

    // response
    toJSONString(Map(
      "url" -> s"https://$bucket.s3.amazonaws.com/",
      "form" -> Map(
        "AWSAccessKeyId" -> sys.env(AWS_ACCESS_KEY),
        "signature" -> signature,
        "policy" -> policy,
        "key" -> path,
        "Content-Type" -> policiesParams.getAs("content_type")
      )
    ), underscoreKeys = false)
  }

  // --------------
  override def upload: Any = throw new UnsupportedOperationException

  // --------------
  override def uploadedFileBaseUrl: UploadedBaseURL = UploadedBaseURL("s3")

} 
Example 6
Source File: GameHeaderCryptTBC.scala    From wowchat   with GNU General Public License v3.0 5 votes vote down vote up
package wowchat.game

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

class GameHeaderCryptTBC extends GameHeaderCrypt {

  override def init(key: Array[Byte]): Unit = {
    super.init(key)

    val hmacSeed = Array(
      0x38, 0xA7, 0x83, 0x15, 0xF8, 0x92, 0x25, 0x30, 0x71, 0x98, 0x67, 0xB1, 0x8C, 0x04, 0xE2, 0xAA
    ).map(_.toByte)
    val md = Mac.getInstance("HmacSHA1")
    md.init(new SecretKeySpec(hmacSeed, "HmacSHA1"))
    md.update(key)
    _key = md.doFinal()
  }
} 
Example 7
Source File: GameHeaderCryptWotLK.scala    From wowchat   with GNU General Public License v3.0 5 votes vote down vote up
package wowchat.game

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

class GameHeaderCryptWotLK extends GameHeaderCrypt {

  private var clientCrypt: RC4 = _
  private var serverCrypt: RC4 = _

  protected val serverHmacSeed: Array[Byte] = Array(
    0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57
  ).map(_.toByte)

  protected val clientHmacSeed: Array[Byte] = Array(
    0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE
  ).map(_.toByte)

  override def decrypt(data: Array[Byte]): Array[Byte] = {
    if (!_initialized) {
      return data
    }

    serverCrypt.cryptToByteArray(data)
  }

  override def encrypt(data: Array[Byte]): Array[Byte] = {
    if (!_initialized) {
      return data
    }

    clientCrypt.cryptToByteArray(data)
  }

  override def init(key: Array[Byte]): Unit = {
    val md = Mac.getInstance("HmacSHA1")

    md.init(new SecretKeySpec(serverHmacSeed, "HmacSHA1"))
    md.update(key)
    val serverKey = md.doFinal()

    md.init(new SecretKeySpec(clientHmacSeed, "HmacSHA1"))
    md.update(key)
    val clientKey = md.doFinal()

    serverCrypt = new RC4(serverKey)
    serverCrypt.cryptToByteArray(new Array[Byte](1024))
    clientCrypt = new RC4(clientKey)
    clientCrypt.cryptToByteArray(new Array[Byte](1024))

    _initialized = true
  }
} 
Example 8
Source File: AuthToken.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.backend.auth

import java.time.Instant
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import cats.effect._
import cats.implicits._

import docspell.backend.Common
import docspell.backend.auth.AuthToken._
import docspell.common._

import scodec.bits.ByteVector

case class AuthToken(millis: Long, account: AccountId, salt: String, sig: String) {
  def asString = s"$millis-${b64enc(account.asString)}-$salt-$sig"

  def sigValid(key: ByteVector): Boolean = {
    val newSig = AuthToken.sign(this, key)
    AuthToken.constTimeEq(sig, newSig)
  }
  def sigInvalid(key: ByteVector): Boolean =
    !sigValid(key)

  def notExpired(validity: Duration): Boolean =
    !isExpired(validity)

  def isExpired(validity: Duration): Boolean = {
    val ends = Instant.ofEpochMilli(millis).plusMillis(validity.millis)
    Instant.now.isAfter(ends)
  }

  def validate(key: ByteVector, validity: Duration): Boolean =
    sigValid(key) && notExpired(validity)
}

object AuthToken {
  private val utf8 = java.nio.charset.StandardCharsets.UTF_8

  def fromString(s: String): Either[String, AuthToken] =
    s.split("\\-", 4) match {
      case Array(ms, as, salt, sig) =>
        for {
          millis <- asInt(ms).toRight("Cannot read authenticator data")
          acc    <- b64dec(as).toRight("Cannot read authenticator data")
          accId  <- AccountId.parse(acc)
        } yield AuthToken(millis, accId, salt, sig)

      case _ =>
        Left("Invalid authenticator")
    }

  def user[F[_]: Sync](accountId: AccountId, key: ByteVector): F[AuthToken] =
    for {
      salt <- Common.genSaltString[F]
      millis = Instant.now.toEpochMilli
      cd     = AuthToken(millis, accountId, salt, "")
      sig    = sign(cd, key)
    } yield cd.copy(sig = sig)

  private def sign(cd: AuthToken, key: ByteVector): String = {
    val raw = cd.millis.toString + cd.account.asString + cd.salt
    val mac = Mac.getInstance("HmacSHA1")
    mac.init(new SecretKeySpec(key.toArray, "HmacSHA1"))
    ByteVector.view(mac.doFinal(raw.getBytes(utf8))).toBase64
  }

  private def b64enc(s: String): String =
    ByteVector.view(s.getBytes(utf8)).toBase64

  private def b64dec(s: String): Option[String] =
    ByteVector.fromValidBase64(s).decodeUtf8.toOption

  private def asInt(s: String): Option[Long] =
    Either.catchNonFatal(s.toLong).toOption

  private def constTimeEq(s1: String, s2: String): Boolean =
    s1.zip(s2)
      .foldLeft(true)({ case (r, (c1, c2)) => r & c1 == c2 }) & s1.length == s2.length

} 
Example 9
Source File: Crypto.scala    From tepkin   with Apache License 2.0 5 votes vote down vote up
package net.fehmicansaglam.tepkin.util

import java.security.MessageDigest
import javax.crypto.spec.{PBEKeySpec, SecretKeySpec}
import javax.crypto.{Mac, SecretKeyFactory}

import net.fehmicansaglam.bson.util.Codec

trait Crypto extends Codec {

  def sha1(value: String): String = {
    val digest = sha1(value.getBytes("UTF-8"))
    encodeBase64(digest)
  }

  def sha1(value: Array[Byte]): Array[Byte] = {
    MessageDigest.getInstance("SHA-1").digest(value)
  }

  def hmac(value: Array[Byte], key: String): Array[Byte] = {
    val signingKey = new SecretKeySpec(value, "HmacSHA1")
    val mac = Mac.getInstance("HmacSHA1")
    mac.init(signingKey)
    mac.doFinal(decodeUtf8(key))
  }

  def keyDerive(password: String, salt: Array[Byte], iterations: Int): Array[Byte] = {
    val spec = new PBEKeySpec(password.toCharArray, salt, iterations, 20 * 8 )
    val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
    keyFactory.generateSecret(spec).getEncoded
  }

  def xor(as: Array[Byte], bs: Array[Byte]): Array[Byte] = {
    as.zip(bs).map { case (a, b) => (a ^ b).toByte }
  }
}

object Crypto extends Crypto 
Example 10
Source File: AuthUtil.scala    From shield   with MIT License 5 votes vote down vote up
package shield.aws

import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import org.apache.commons.codec.binary.Hex
import org.joda.time.format.DateTimeFormat
import org.joda.time.{DateTime, DateTimeZone}
import spray.http.HttpHeaders.RawHeader
import spray.http.HttpRequest


object HexBytesUtil {

  def hex2bytes(hex: String): Array[Byte] = {
    hex.replaceAll("[^0-9A-Fa-f]", "").sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte)
  }

  def bytes2hex(bytes: Array[Byte], sep: Option[String] = None): String = {
    sep match {
      case None => bytes.map("%02x".format(_)).mkString
      case _ => bytes.map("%02x".format(_)).mkString(sep.get)
    }
  }
} 
Example 11
Source File: Hmac.scala    From incubator-toree   with Apache License 2.0 5 votes vote down vote up
package org.apache.toree.communication.security

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import org.apache.toree.communication.security.HmacAlgorithm.HmacAlgorithm

object HmacAlgorithm extends Enumeration {
  type HmacAlgorithm = Value

  def apply(key: String) = Value(key)

  val MD5     = Value("HmacMD5")
  val SHA1    = Value("HmacSHA1")
  val SHA256  = Value("HmacSHA256")
}

object Hmac {
  
  def apply(key: String, algorithm: HmacAlgorithm = HmacAlgorithm.SHA256) =
    new Hmac(key, algorithm)
  
  def newMD5(key: String): Hmac     = this(key, HmacAlgorithm.MD5)
  def newSHA1(key: String): Hmac    = this(key, HmacAlgorithm.SHA1)
  def newSHA256(key: String): Hmac  = this(key, HmacAlgorithm.SHA256)
}

class Hmac(
  val key: String,
  val algorithm: HmacAlgorithm = HmacAlgorithm.SHA256
) {

  private var mac: Mac = _
  private var secretKeySpec: SecretKeySpec = _

  if (key.nonEmpty) {
    mac = Mac.getInstance(algorithm.toString)
    secretKeySpec = new SecretKeySpec(key.getBytes, algorithm.toString)
    mac.init(secretKeySpec)
  }

  def apply(items: String*): String = digest(items)

  def digest(items: Seq[String]): String = if (key.nonEmpty) {
    mac synchronized {
      items.map(_.getBytes("UTF-8")).foreach(mac.update)
      mac.doFinal().map("%02x" format _).mkString
    }
  } else ""
} 
Example 12
Source File: Encoder.scala    From twitter4s   with Apache License 2.0 5 votes vote down vote up
package com.danielasfregola.twitter4s.util

import java.security.MessageDigest
import java.util.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

private[twitter4s] trait Encoder {

  def toHmacSha1(base: String, secret: String): String = {
    val HMAC_SHA1 = "HmacSHA1"
    val UTF8 = "UTF-8"
    val secretKeySpec = new SecretKeySpec(secret.getBytes(UTF8), HMAC_SHA1)
    val mac = Mac.getInstance(HMAC_SHA1)
    mac.init(secretKeySpec)
    val bytesToSign = base.getBytes(UTF8)
    val digest = mac.doFinal(bytesToSign)
    Base64.getEncoder.encodeToString(digest)
  }

  def toBase64(data: Array[Byte]): String =
    Base64.getEncoder.encodeToString(data)

  def toSha1(base: String): String = {
    val SHA1 = "SHA-1"
    val messageDigest = MessageDigest.getInstance(SHA1)
    val bytes = messageDigest.digest(base.getBytes)

    val stringBuffer = new StringBuffer
    bytes.foreach { byte =>
      stringBuffer.append(Integer.toString((byte & 0xff) + 0x100, 16).substring(1))
    }
    stringBuffer.toString
  }

} 
Example 13
Source File: Utils.scala    From akka-pusher   with MIT License 5 votes vote down vote up
package com.github.dtaniwaki.akka_pusher

import java.math.BigInteger
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import akka.http.scaladsl.model.Uri

object Utils {
  val HEX = 16
  val LENGTH = 32

  def byteArrayToString(data: Array[Byte]): String = {
    val bigInteger = new BigInteger(1, data)
    var hash = bigInteger.toString(HEX)

    while (hash.length() < LENGTH) {
      hash = "0" + hash
    }

    hash
  }

  def md5(string: String): String = {
    val bytesOfMessage = string.getBytes("UTF-8")
    val md = MessageDigest.getInstance("MD5")
    val digest = md.digest(bytesOfMessage)
    byteArrayToString(digest)
  }

  def sha256(secret: String, string: String): String = {
    val signingKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256")

    val mac = Mac.getInstance("HmacSHA256")
    mac.init(signingKey)

    val digest = mac.doFinal(string.getBytes("UTF-8"))

    val bigInteger = new BigInteger(1, digest)
    String.format("%0" + (digest.length << 1) + "x", bigInteger)
  }

  def normalizeQuery(query: Uri.Query): Uri.Query = {
    Uri.Query(query.map { case (k, v) => (k.toString.toLowerCase, v) }.sortBy(_._1): _*)
  }
}