Brightscript为CloudFront生成的签名URL产生“访问被拒绝”

时间:2019-06-20 22:43:47

标签: amazon-s3 amazon-cloudfront roku brightscript

我正在Roku频道上工作,我们希望将文件托管在AWS S3存储桶中,并使用CloudFront分发内容。在考虑安全性之前,它运行良好。但是,既然我要注意安全问题,那么我遇到了问题。我将S3存储桶作为私有存储桶(其中没有任何公共访问权限),并且为CloudFront分发创建了原始访问身份,以便它可以访问存储桶中的内容。

问题是,我无法在通道Brightscript代码中创建过期的签名URL(或cookie),以访问内容。我可以通过命令行使用Amazon's perl script创建一个签名的URL,如果我复制/粘贴链接的签名部分,它将带我到我在brightscript中创建的URL的签名部分中(替换签名)作品。当然,那是因为关于URL的其他所有内容都是相同的,所以一旦我替换了签名,便有了另一个URL。因此,我知道(至少我想可以肯定地说)问题出在签名上。我按照AWS' documentation中指示的步骤进行操作,但始终返回“访问被拒绝”错误消息。

我遗漏的签名过程的唯一部分是base 64编码。我尝试使用base 64对Brightscript使用this site创建的签名进行编码,并更新URL并尝试使用它,但是还是没有运气。我感觉它与Brightscript哈希或标志事物的方式有关。我在Stack Overflow post中看到了openssl(这是perl脚本用来哈希/签名的东西)在签名之前也编码成ASN.1的意思。我也尝试过修补一下,看看是否可以它可以正常工作,但没有运气。

也许我做得不好,或者这不是问题所在。我知道有些人使用S3和CloudFront托管Roku频道的内容,所以我不知道为什么它不起作用。希望外面有人可以阐明...如果有人知道解决方案,我会很高兴听到它!

编辑: 我意识到我从byteArray转换为string的方式很错误!我对此进行了更改:

  //To convert from byteArray to string
  signatureString = ""
  for each byte in signature
      signatureString = signatureString + stri(byte)
  end for

对此:

  sigString = signature.ToAsciiString()
  print "sigString: ";sigString
  signatureString = signature.ToBase64String()

不幸的是,我仍然无法访问。但是,至少现在我的URL看起来像perl脚本创建的URL —在签名部分只是一堆数字之前。另外,我现在也以base 64编码签名。我觉得我可能越来越近了! :)

编辑2:我在Roku论坛上打开了一个话题,活动很简单:https://forums.roku.com/viewtopic.php?t=54797 我发现策略字符串不正确-“ Resource”之前有“ Condition”(由于JSON从roAssociativeArray对其进行了解析)。我能够获得正确的策略字符串,但是仍然无法访问。

这是我用来创建签名URL的代码:

  readInternet = createObject("roUrlTransfer")


  policy = { "Statement": [
                  {
                     "Resource":"http://XXXXXXXXXXXXX.cloudfront.net/icon_focus_sd.png",
                     "Condition": {
                         "DateLessThan": {
                               "AWS:EpochTime": 1561230905
                          }
                      }
                   }
  ]
  }

  policyString = FormatJson(policy)
  print "policyString: ";policyString

  //UPDATE: correct policy string now:
  policyString = "{" + Chr(34) + "Statement" + Chr(34) + ":[{" + Chr(34) + "Resource" + Chr(34) + ":" + Chr(34) + "http://d1uuhuldzrqhow.cloudfront.net/icon_focus_sd.png" + Chr(34) + "," + Chr(34) + "Condition" + Chr(34) + ":{" + Chr(34) + "DateLessThan" + Chr(34) + ":{" + Chr(34) + "AWS:EpochTime" + Chr(34) + ":1561230905}}}]}"
  ba.FromAsciiString(policyString)
  print "New policy string: ";policyString

  ba = CreateObject("roByteArray")
  ba.FromAsciiString(policyString)

  digest = CreateObject("roEVPDigest")
  digest.Setup("sha1")
  hashString = digest.Process(ba)
  print "hashString: ";hashString

  hashBA = CreateObject("roByteArray")
  hashBA.FromHexString(hashString)

  rsa = CreateObject("roRSA")  
  rsa.setPrivateKey("pkg:/components/key/privateKey.pem")
  rsa.SetDigestAlgorithm("sha1")

  signature = rsa.Sign(hashBA)

  //EDIT! The following 3 lines are a big development!
  sigString = signature.ToAsciiString()
  print "sigString: ";sigString
  signatureString = signature.ToBase64String()

  //To convert from byteArray to string --Commented this part out as it was WRONG!!!
  //signatureString = ""
  //for each byte in signature
  //    signatureString = signatureString + stri(byte)
  //end for

  print "Signature: ";signature
  print "SignatureString: ";signatureString

  baseURL = policy.statement[0].resource
  print "BaseURL: ";baseURL
  fixedSignatureString = signatureString.replace(" ", "").replace("=", "_").replace("/", "~").replace("+", "-")


  dateKeys = policy.statement[0].condition.datelessthan.Keys()
  print"dateKeys: ";dateKeys
  print"dateKey: ";dateKeys[0]

  epochTime = policy.statement[0].condition.dateLessThan.Lookup(dateKeys[0])

  finalURL = baseURL + "?Expires=" + stri(epochTime).Replace(" ","") + "&Signature=" + fixedSignatureString + "&Key-Pair-Id=APKXXXXXXXXXXVWQ"
  print "finalURL: ";finalURL


  readInternet.setUrl(finalURL)

  readInternet.SetCertificatesFile("common:/certs/ca-bundle.crt")
  readInternet.AddHeader("X-Roku-Reserved-Dev-Id", "")
  readInternet.InitClientCertificates()

  readInternet.RetainBodyOnError(true)
  response = ParseJson(readInternet.GetToString())

  print "response:" response

我还尝试使用它来创建签名的cookie:

cookies = [
   {Name:"CloudFront-Policy",Value:policy,Path:"/", Domain:"XXXXXXXXXX.cloudfront.net"},
   {Name:"CloudFront-Expires",Value:"1561230905",Path:"/", Domain:"XXXXXXXXXX.cloudfront.net"},
   {Name:"CloudFront-Signature",Value:fixedSignatureString,Path:"/", Domain:"XXXXXXXXXX.cloudfront.net"},
   {Name:"CloudFront-Key-Pair-Id",Value:"APKAXXXXXXXXXXAVWQ", Path:"/", Domain:"XXXXXXXXXX.cloudfront.net"}
]

readInternet.EnableCookies()
readInternet.AddCookies(cookies)

我还尝试了以下方法,而不是以前的添加cookie的方法:

expires = "CloudFront-Expires=1561146117" //I have been careful to make sure the expire times are still good
sig = "CloudFront-Signature=" + fixedSignatureString
pairid = "CloudFront-Key-Pair-Id=APKAXXXXXXXXXXVWQ"
readInternet.AddHeader("Cookie",expires + "; " + sig + "; " + pairid)

1 个答案:

答案 0 :(得分:1)

我终于解决了这个问题!我只是开始(再次,但要更加仔细),然后看看使用BrightScript创建签名URL和使用Perl脚本创建签名URL的相同点和不同点。当我添加到原始问题帖时,我以错误的方式将签名从byteArray更改为字符串。解决问题是第一步。一旦弄清楚了,我便注意到当我从命令行(而不是来自perl脚本)对URL签名时,它正与从BrightScript中获得的签名相同(不起作用)。然后我发现我使用了错误的私钥! (我知道-太好了,对吗?)切换到使用正确的键,现在一切正常!我只需要使它们全部动态,以使其对我的所有文件有效并动态设置到期时间即可。

所以这是我学到的总结:

  • 确保策略字符串正确-顺序很重要!使用formatJSON(roAssociativeArray)会破坏它,因为它按字母顺序排列。
  • 对roByteArray中的每个字节进行字符串化处理不会正确地将其转换为字符串!!而是根据需要使用.ToAsciiString().ToBase64String()
  • 使用正确的密钥非常重要-谁知道。 *插入facepalm *

这是我的代码,现在可以正常工作了:

    readInternet = createObject("roUrlTransfer")

    ba = CreateObject("roByteArray")
    policyString = "{" + Chr(34) + "Statement" + Chr(34) + ":[{" + Chr(34) + "Resource" + Chr(34) + ":" + Chr(34) + "http://XXXXXXXXX.cloudfront.net/icon_focus_sd.png" + Chr(34) + "," + Chr(34) + "Condition" + Chr(34) + ":{" + Chr(34) + "DateLessThan" + Chr(34) + ":{" + Chr(34) + "AWS:EpochTime" + Chr(34) + ":1561230905}}}]}"
    ba.FromAsciiString(policyString)
    print "New policy string: ";policyString

    digest = CreateObject("roEVPDigest")
    digest.Setup("sha1")

    hashString = digest.Process(ba)
    print "hashString: ";hashString
    hashBA = CreateObject("roByteArray")
    hashBA.FromHexString(hashString)

    rsa = CreateObject("roRSA")
    rsa.setPrivateKey("pkg:/components/key/privateKey.pem")
    rsa.SetDigestAlgorithm("sha1")

    signature = rsa.Sign(hashBA)
    signatureString = signature.ToBase64String()

    print "Signature: ";signature
    print "SignatureString: ";signatureString

    baseURL = "http://XXXXXXXXXXX.cloudfront.net/icon_focus_sd.png"
    print "BaseURL: ";baseURL
    fixedSignatureString = signatureString.replace(" ", "").replace("=", "_").replace("/", "~").replace("+", "-")

    epochTime = 1561230905

    finalURL = baseURL + "?Expires=" + stri(epochTime).Replace(" ","") + "&Signature=" + fixedSignatureString + "&Key-Pair-Id=APKAXXXXXXXXXXXVWQ"
    print "finalURL: ";finalURL

    readInternet.setUrl(finalURL)

    readInternet.SetCertificatesFile("common:/certs/ca-bundle.crt")
    readInternet.AddHeader("X-Roku-Reserved-Dev-Id", "")
    readInternet.InitClientCertificates()

    readInternet.RetainBodyOnError(true)
    response = ParseJson(readInternet.GetToString())


感谢所有参与/提出建议的人。感谢您的支持。