protected def calculateCanonicalString()

in app/helpers/S3Signer.scala [163:213]


  protected def calculateCanonicalString(httpMethod:String, uriPath: String, uriQueryParams:Map[String,String],
                               headers:Map[String,String], payloadHash:Option[String]) = {
    val checksummer = MessageDigest.getInstance("SHA-256")
    logger.debug(s"uriPath is $uriPath ${uriPath.length}")
    val canonicalUrl = if(uriPath.length<=1) {
      "/"
    } else {
      //Note that the spec at https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html demands that spaces should
      //be double-encoded for all services with the EXCEPTION of s3. So we encode once and make sure that + (how the java urlencoder does a space)
      //gets replaced with %20 which is what AWS wants
      uriPath
        .replaceAll("\\+","%20")  //java encoder often encodes spaces as '+' instead of %20
        .replaceAll(":", "%3A")   //: character is not being automatically encoded (%3A)
    }
    logger.debug(s"encodedUrl: $canonicalUrl")

    val canonicalQueryString = uriQueryParams
      .map(entry=>URLEncoder.encode(entry._1,"UTF-8")+"=" + entry._2)
      .toList.sorted
      .mkString("&")
    logger.debug(s"canonicalQueryString: $canonicalQueryString")

    val updatedHeaders = if(headers.keys.exists(_=="x-amz-content-sha256")){
      headers
    } else {
      headers + ("x-amz-content-sha256"->checksummer.digest("".getBytes("UTF-8")).map("%02x".format(_)).mkString)
    }

    checksummer.reset()

    val canonicalHeaders = updatedHeaders.keys.toList.sorted.map(header=>{
      header.toLowerCase + ":" + headers(header).trim
    }).mkString("\n") + "\n"
    logger.debug(s"canonicalHeaders: $canonicalHeaders")

    val signedHeaders = headers.keys.map(_.toLowerCase).toList.sorted.mkString(";")
    logger.debug(s"signedHeaders: $signedHeaders")

    val hashedPayload = payloadHash match {
      case Some(hexDigest)=>hexDigest
      case None=>checksummer.digest("".getBytes("UTF-8")).map("%02x".format(_)).mkString
    }
    logger.debug(s"hashedPayload: $hashedPayload")

    s"""$httpMethod
       |$canonicalUrl
       |$canonicalQueryString
       |$canonicalHeaders
       |$signedHeaders
       |$hashedPayload""".stripMargin
  }