我正在尝试更新 Google AMP 缓存中的 Google AMP 网页,但收到网址签名验证错误。
我的代码:
Dim tStamp As String = GetUnixTimeStampFromDateTime(DateTime.Now).ToString
Dim ampBaseUrl As String = "https://www-example-com.cdn.ampproject.org"
Dim signatureUrl As String = "/update-cache/c/s/www.example.com/articles/278/myarticle/amp?amp_action=flush&_ts=" + tStamp
Dim rsa As RSA = certificate.GetRSAPrivateKey()
Dim data() As Byte = System.Text.Encoding.Unicode.GetBytes(signatureUrl)
Dim sig() As Byte = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
Dim AMPURLSignature As String = EncodeTo64(sig.ToString)
编码功能:
Public Shared Function EncodeTo64(ByVal toEncode As String) As String
Dim toEncodeAsBytes As Byte() = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode)
Dim returnValue As String = System.Convert.ToBase64String(toEncodeAsBytes)
Return returnValue
End Function
我尝试使用此网址调用谷歌AMP缓存。
现在,我收到一个 403 错误:
您的客户端无权获取 URL/update-cache/c/s/www.example.com/articles/278/myarticle/amp?amp_action=flush&_ts=1523016476&_url_signature=U2lzdGVtLkJ5dGVbdQ。网址签名验证错误。这就是我们所知道的。
我发现谷歌示例代码不够清晰:https://developers.google.com/amp/cache/update-cache
我的问题与签名 URL 有关:
- 我使用的是文章 AMP 网址还是常规网址?
- 是否需要在签名 URL 中包含
amp_action
和amp_ts
的查询字符串参数?还是在对 URL 进行签名后再附加这些内容? - 我应该将上述
ampBaseUrl
附加到我的signatureUrl
变量中还是不需要?
更新 1
根据@CodeFuller的建议,我检查了 URL,并收到了一条Verified OK
消息。我还处理了第 2 步:
- API密钥匹配:https://www.toptrouwen.nl/.well-known/amphtml/apikey.pub 匹配 https://www-toptrouwen-nl.cdn.ampproject.org/r/s/www.toptrouwen.nl/.well-known/amphtml/apikey.pub
- 从我的服务器以文本/纯文本形式提供 apikey.pub
- 通过HTTPS和公开提供APIKEY.pub
- 在机器人中添加了禁止:用户代理:* 禁止:/.well-known/amphtml/apikey.pub
更新 2
是的,使用新代码,我也得到了验证Verified OK
。
此网址生成:
https://www-toptrouwen-nl.cdn.ampproject.org/update-cache/c/s/www.toptrouwen.nl/artikelen/132/het-uitwisselen-van-de-trouwringen-hoe-voorkom-je-bloopers/amp?amp_action=flush&_ts=1523138180&_url_signature=tKPO3k624ybwxoEynqN8oI3/UDxhq1TF8jX9aKeVyL0IWLUODXuMB7ansP0t1+/5Lm2V7RYZbUWxt2Whh7+LFEmfQFGJJE/iPtoBVsqrdb5356QwiIrDHOzY+3z5dASZxYlAwlfzUFdonGyDsh/UlCjjvvNahFEWzHOpB5JQxJQ1Wn0kGLQUF1v2u47abbae6cNQBm3YB/0Z1FLfTJLM1oOEOSDh9vQH1SqO/6SoYtUhSQjSrYdl/g5O0QJ7A9pKUxOPfgVJM0l8Sgb66cVeWWoWq0WIFe24RPXUMl9tIFFZ1TY2R+ZpIMvpEAPDjCsdGPo7KTWqGb4qfoTBINJmtQ==
然后我得到错误Required query parameter 'amp_url_signature' missing.
(与amp_
参数拙劣的早期问题有关。
然后,我将 URL 参数重命名为正确的名称:https://www-toptrouwen-nl.cdn.ampproject.org/update-cache/c/s/www.toptrouwen.nl/artikelen/132/het-uitwisselen-van-de-trouwringen-hoe-voorkom-je-bloopers/amp?amp_action=flush&_ts=1523138180&_url_signature=tKPO3k624ybwxoEynqN8oI3/UDxhq1TF8jX9aKeVyL0IWLUODXuMB7ansP0t1+/5Lm2V7RYZbUWxt2Whh7+LFEmfQFGJJE/iPtoBVsqrdb5356QwiIrDHOzY+3z5dASZxYlAwlfzUFdonGyDsh/UlCjjvvNahFEWzHOpB5JQxJQ1Wn0kGLQUF1v2u47abbae6cNQBm3YB/0Z1FLfTJLM1oOEOSDh9vQH1SqO/6SoYtUhSQjSrYdl/g5O0QJ7A9pKUxOPfgVJM0l8Sgb66cVeWWoWq0WIFe24RPXUMl9tIFFZ1TY2R+ZpIMvpEAPDjCsdGPo7KTWqGb4qfoTBINJmtQ==
然后我得到:404 Failed to decode amp_url_signature
,我认为这是因为URL中有+
和字符。当我删除它们时,我再次出现错误
URL signature verification error
。
我不认为 UTC 时间戳目前是一个问题,因为我之前已经看到如果时间戳不正确,谷歌会抛出错误。
有两个最常见的URL signature verification error
原因:
-
签名计算不正确。
因此,首先要验证签名是否确实有效。将签名的 URL 和结果签名保存到某些文件:
Dim signatureUrl As String = "/update-cache/c/s/www.example.com/articles/278/myarticle/amp?amp_action=flush&_ts=" + tStamp ' ... Dim sig() As Byte = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1) File.WriteAllText("url.txt", signatureUrl) File.WriteAllBytes("signature.bin", sig)
然后使用 openssl 工具验证签名:
OpenSSL.exe DGST -SHA256 -签名签名.bin -验证 公钥.pem 网址.txt
如果输出为
已验证正常
那么你的签名是可以的,你应该在其他地方搜索问题(见项目#2)。
如果输出为
验证失败
然后,您应该重新检查签名计算的例程。
上面命令中的公钥.PEM是PEM格式的公钥。它应以以下行开头:
-----BEGIN PUBLIC KEY-----
如果您有证书文件(以
-----BEGIN CERTIFICATE-----
开头),则可以使用以下命令获取它的 PEM:openSSL.exe x509 -pubkey -noout -in publickey.cer> publickey.pem
-
如果签名验证成功,则应检查您的公钥是否可通过 Web 访问。更新 AMP 内容符合以下要求:
要进行签名验证,您必须在 AMP 文档网域上的固定位置提供公共 RSA 密钥(要生成密钥,请参阅生成 RSA 密钥)。例如:
https://example.com/.well-known/amphtml/apikey.pub
-
公钥不得是机器人。
-
网址必须是 HTTPS。
-
该域必须是您要更新的确切域,而不是子域或超级域。
-
您必须以 PEM 格式发布密钥,并使用内容类型"文本/纯文本"提供密钥。
因此,如果您的 AMP 基本网址
https://www.test.com
,请检查您的公钥是否可通过以下网址访问:https://www.test.com/.well-known/amphtml/apikey.pub
-
以下是您其他问题的答案:
我是否需要在签名 URL 中包含查询字符串参数amp_action和amp_ts?还是在对 URL 进行签名后再附加这些内容?
是的,您应该在计算签名的 URL 中包含amp_action
和amp_ts
。
我应该将上面的ampBaseUrl附加到我的signatureUrl变量中,还是不需要这样做?
如前所述,更新AMP内容一文:
AMP 缓存主机名 (cdn.ampproject.org) 已从 签名,允许向多个 AMP 提交相同的签名请求 缓存运算符。
因此,不应将ampBaseUrl
包含在计算签名的 URL 中。
更新
我在将签名附加到 URL 的代码中发现了另一个错误:
Dim AMPURLSignature As String = EncodeTo64(sig.ToString)
sig.ToString
将导致"System.Byte[]"
字符串传递给EncodeTo64
,而不是实际的签名字节。因此,在更新缓存 URL 时附加了不正确的签名。将上述调用替换为以下内容(不再需要EncodeTo64
):
Dim AMPURLSignature As String = System.Convert.ToBase64String(sig)
另一个可能的问题是您使用的时间戳不满足以下要求:
该值应为当前时间(以秒为单位),该时间必须在当前时间之前或之后 1 分钟。
您将DateTime.Now
传递给GetUnixTimeStampFromDateTime()
但它应该是 UTC 时间,而不是本地时间。尝试将tStamp
计算替换为:
Dim tStamp As String = CInt((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds).ToString
此外,请确保在生成 URL 后立即调用update-cache
以便当前时间与时间戳相比在 1 分钟间隔内。而且您的系统时间应该精确。
请在上述修复后重新检查签名验证是否通过。
更新 2(关于签名中的+
和/
字符)
AMP 缓存更新签名应采用 base4 编码的变体:
使用 base64 的 Web 安全变体对二进制 RSA 签名进行编码
因此,另一个必需的修复方法是将+
和/
字符替换为-
和_
.
更改以下调用
Dim AMPURLSignature As String = System.Convert.ToBase64String(sig)
跟:
Dim AMPURLSignature As String = ToWebSafeBase64(sig)
' ...
Private Function ToWebSafeBase64([data]() As Byte) As String
Dim base64 = System.Convert.ToBase64String(data)
base64 = base64.Replace("+", "-")
base64 = base64.Replace("/", "_")
Return base64
End Function
更新 3
以下是应生成正确update-cache
URL 的最终代码(基于可用文档):
Module MainModule
Sub Main()
Dim tStamp As String = CInt((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds).ToString
Dim ampBaseUrl = "https://www-toptrouwen-nl.cdn.ampproject.org"
Dim signatureUrl As String = "/update-cache/c/s/www.toptrouwen.nl/artikelen/132/het-uitwisselen-van-de-trouwringen-hoe-voorkom-je-bloopers?amp_action=flush&_ts=" + tStamp
Dim data() As Byte = System.Text.Encoding.ASCII.GetBytes(signatureUrl)
Dim certificate = New X509Certificate2("d:tempkeyskeys.pfx", "12345")
Dim rsa As RSA = certificate.GetRSAPrivateKey()
Dim sig() As Byte = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
Dim ampUrlSignature As String = ToWebSafeBase64(sig)
Dim url As String = ampBaseUrl + signatureUrl + "&_url_signature=" + ampUrlSignature
Console.WriteLine(url)
End Sub
Private Function ToWebSafeBase64([data]() As Byte) As String
Dim base64 = System.Convert.ToBase64String(data)
base64 = base64.Replace("+", "-")
base64 = base64.Replace("/", "_")
Return base64
End Function
End Module
GitHub 上的示例项目
它生成以下网址:
https://www-toptrouwen-nl.cdn.ampproject.org/update-cache/c/s/www.toptrouwen.nl/artikelen/132/het-uitwisselen-van-de-trouwringen-hoe-voorkom-je-bloopers?amp_action=flush&_ts=1523188941&_url_signature=bZJTE4IjlxGhlU79etivzUPpGFoyKvCxqPO1IOPHfzDKQVt-fA8Mte20SeXjTQs24Uy4RD9lmbS2aXlcCTpOYatF2l8PQ-31kR-lKVnuduSZIrg93g2YrvO7x-a6dr19hN74LywgBw4C_JfuocCuGfVvr-mD40tuwkBrsLgmI9E=
此签名已使用 opensll 工具成功验证,但获取此 URL 仍返回URL signature verification error.
。然而,现在我们这边似乎一切都很好。我在 github 上发现了相关问题。AMP项目参与者有以下声明:
首先,我确定AMP缓存不处理HTTP刷新 正确用于更新缓存验证密钥:如果您发出 update-cache request,然后交换/.well-known/amphtml/apikey.pub 使用不同的密钥,我们会无限期地继续使用旧的密钥材料。 更糟糕的是,404 个响应也被永久缓存:-(
我提交了内部错误报告,但可能需要一些时间 修复以推出到生产环境。同时,我可以刷新无效密钥 手动地。只需在GitHub上或上给我发送私人消息 amphtml.slack.com。很抱歉没有早点发现这一点。
似乎是当前错误的根本原因。您尝试在https://www.toptrouwen.nl/.well-known/amphtml/apikey.pub
没有可用的公钥的情况下更新缓存(当我第一次检查它时,它导致 404 错误)。谷歌已经缓存了这个结果,现在即使证书可用,它也没有被实际使用。
似乎目前唯一可能的解决方法是联系codewiz并要求他刷新缓存的密钥错误。在 AMP 系统使用有效的公钥之前,无法进行签名验证。