我正在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)
答案 0 :(得分:1)
我终于解决了这个问题!我只是开始(再次,但要更加仔细),然后看看使用BrightScript创建签名URL和使用Perl脚本创建签名URL的相同点和不同点。当我添加到原始问题帖时,我以错误的方式将签名从byteArray更改为字符串。解决问题是第一步。一旦弄清楚了,我便注意到当我从命令行(而不是来自perl脚本)对URL签名时,它正与从BrightScript中获得的签名相同(不起作用)。然后我发现我使用了错误的私钥! (我知道-太好了,对吗?)切换到使用正确的键,现在一切正常!我只需要使它们全部动态,以使其对我的所有文件有效并动态设置到期时间即可。
所以这是我学到的总结:
formatJSON(roAssociativeArray)
会破坏它,因为它按字母顺序排列。.ToAsciiString()
或.ToBase64String()
。这是我的代码,现在可以正常工作了:
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())
感谢所有参与/提出建议的人。感谢您的支持。