我想更新 Google AMP 缓存,因此我需要按照 here 中的说明对 URL 进行签名.
我的主要问题是:我在如何获取证书/ key 以及如何将它们包含在下面的代码中苦苦挣扎。
我只是找不到任何适用于 Windows 和 IIS 的所有涵盖说明。
我一直在阅读这些帖子:
我不想使用我的计算机的证书存储,如第二篇文章中所述。我宁愿使用磁盘上的文件作为公钥和私钥。
从我的生产服务器 IIS,我将我的证书导出到一个 .pfx 文件,然后我使用 this site 底部的说明从中提取私钥和证书。 .
server.key 包含
-----BEGIN RSA PRIVATE KEY-----
,如果我使用它加载到 privateCert
下面代码中的变量抛出错误 Cannot find the requested object.
我从我的 SSL 提供商那里得到了什么:
www_example_com.crt
, www_example_com.p7b
, certificate code
(见下文)。我采取的步骤:
test-public-key.cer
通过打开 www_example.com.crt
并使用 Copy to file
向导将其复制到 base64 编码的 .cer 文件。 certificate code
我从 SSL 提供商处收到文件 test-private-key.cer
. 当我运行以下代码时,出现错误
Object reference not set to an instance of an object.
在线
key.FromXmlString(privateCert.PrivateKey.ToXmlString(True))
Dim publicCert As X509Certificate2 = New X509Certificate2("C:\temp\_certificates\test-public-key.cer")
Dim privateCert As X509Certificate2 = New X509Certificate2("C:\temp\_certificates\test-private-key.cer")
'Round-trip the key to XML and back, there might be a better way but this works
Dim key As RSACryptoServiceProvider = New RSACryptoServiceProvider
key.FromXmlString(privateCert.PrivateKey.ToXmlString(True))
'Create some data to sign
Dim data() As Byte = System.Text.Encoding.Unicode.GetBytes(signatureUrl)
'Sign the data
Dim sig() As Byte = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"))
Dim AMPURLSignature As String = EncodeTo64(sig.ToString)
'Lastly, the verification can be done directly with the certificate's public key without need for the reconstruction as we did with the private key:
key = CType(publicCert.PublicKey.Key, RSACryptoServiceProvider)
If Not key.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), sig) Then
Throw New CryptographicException
End If
EncodeTo64 函数
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
certificate code
-----BEGIN CERTIFICATE-----
MIIF (...) DXuJ
-----END CERTIFICATE-----
更新 1
按照 this page 上的导出步骤,我能够生成 mysite.pfx 文件。 .
在向导中,我确保选择“是,导出私钥”并添加了密码。其余的步骤我一字不差。
然后我还运行了这些命令:
openssl pkcs12 -in mysite.pfx -nocerts -out private-key-VPS.pem
penssl pkcs12 -in mysite.pfx -clcerts -nokeys -out certificate-VPS.pem
我最终得到了
private-key-VPS.pem
和一个 certificate-VPS.pem
文件我知道获取 mysite.pfx 的步骤与 @CodeFuller 描述的略有不同,但到目前为止还好吗?
然后我添加了代码:
Dim certificate As X509Certificate2 = New X509Certificate2("C:\temp\_certificates\prodserverV2\mysite.pfx", "mypwd")
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)
但是我有 4 个错误:
GetRSAPrivateKey' is not a member of 'X509Certificate2'.
'SignData' is not a member of 'RSA'.
'HashAlgorithmName' is not declared. It may be inaccessible due to its protection level.
'RSASignaturePadding' is not declared. It may be inaccessible due to its protection level.
更新 2
感谢@CodeFuller!我的目标是框架 4.6.1,现在似乎更进了 1 步。我最终得到这样的 URL:
https://www-mysite-com.cdn.ampproject.org/update-cache/c/s/www.mysite.com/articles/270/newarticle1/amp?amp_action=flush&_ts=1522939248&_url_signature=U30zdGVtLkJ5uGVbRQ==
.我现在如何检查它是否是一个有效的 URL?我正在检查 this page 上的“生成 RSA key ”部分,但我很困惑,因为我实际上已经对这些步骤进行了编码?如何检查我现在得到的 URL 是否有效?
更新 3
好的,我试过你的新代码。还是得到了
URL signature verification error
.我试过 /amp
我文章的 URL 没有 /amp
加入我的网址。两者都会导致相同的 URL 签名验证错误。我注意到当我将最终 URL 打印到我的网站时(请参阅下面的代码),该 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=1523094395&_url_signature=U3lzdGVrLkn5dGVbXQ==
注意参数应该在哪里
amp_ts
和 amp_url_signature
,他们现在是 _ts
和 _url_signature
分别。在调用 Google 之前,我尝试通过手动重命名参数来编辑 URL
_ts
和 _url_signature
至 amp_ts
和 amp_url_signature
.但我想这会导致签名和实际 URL 之间存在差异。难道我的代码以某种方式搞砸了 &
字符,因此当我稍后手动重命名它们时,它总是会导致签名验证?你看到我可以在我的代码中修复什么吗?顺便说一句:我尝试更换
&
与 %26
在签署 URL 之前的代码隐藏中,但随后我收到了 Google 404 错误:The requested URL /update-cache/c/s/www.toptrouwen.nl/artikelen/132/het-uitwisselen-van-de-trouwringen-hoe-voorkom-je-bloopers/amp?amp_action=flush%26amp_ts=1523094395%26amp_url_signature=U3lzdGVrLkJ1dGVbXQ== was not found on this server. That’s all we know.
我的代码:
Dim ampBaseUrl As String = "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?amp_action=flush&_ts=" + tStamp
Dim rsa As RSA = certificate.GetRSAPrivateKey()
Dim data() As Byte = System.Text.Encoding.ASCII.GetBytes(signatureUrl)
Dim sig() As Byte = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
Dim AMPURLSignature As String = EncodeTo64(sig.ToString)
Dim url As String = ampBaseUrl + signatureUrl + "&_url_signature=" + AMPURLSignature
ltStatus.Text = "AMP URL:<a target='_blank' href='" + url + "'>" + url + "</a>"
此外,我确定此页面存在于 Google AMP 缓存中,因为我可以在我的移动设备上的 Google 搜索结果中查看和请求它。
更新 4
我想我正在接近并获得一些额外的帮助,请参阅此处:https://github.com/ampproject/amphtml/issues/14483#issuecomment-380549060
我现在正在尝试使其他人也更容易测试:我现在运行以下命令来获取公钥和私钥,而不是依赖我的 SSL
openssl genrsa 2048 > private-key.pem
openssl rsa -in private-key.pem -pubout >public-key.pem
我现在有文件
private-key.pem
和 public-key.pem
我要重命名
public-key.pem
至 apikey.pub
并将其放在 https://example.com/.well-known/amphtml/apikey.pub
上我想采用@CodeFuller 推荐的最简单的方法并创建一个
.pfx
然后我可以将其加载到类型为 X509Certificate2
的变量中的文件.当我运行此命令时:
openssl pkcs12 -export -out keys.pfx -inkey private-key.pem -in public-key.pem
我收到错误:
unable to load certificates
但这次我没有
.crt
文件,只有一个 public-key.pem
.我怎样才能得到一个 .pfx
文件?我已经查过 here .
最佳答案
I saved certificate code I received from my SSL provider as file test-private-key.cer
...
certificate code
-----BEGIN CERTIFICATE-----
MIIF (...) DXuJ
-----END CERTIFICATE-----
以某种格式存储的文件
-----BEGIN CERTIFICATE-----
MIIF (...) DXuJ
-----END CERTIFICATE-----
是一个基本上包含公钥的证书。它 does not contain 私钥。这就是为什么当你从这样的文件创建
X509Certificate2
的实例时,它的 HasPrivateKey
属性被设置为 False
并且 PrivateKey
返回 Nothing
,并且以下语句预计会抛出 NullReferenceException
:privateCert.PrivateKey.ToXmlString(True)
为了对数据进行签名,您需要一个私钥。私钥具有以下格式
-----BEGIN RSA PRIVATE KEY-----
MIICXQ...B7Bou+
-----END RSA PRIVATE KEY-----
此类私钥通常存储在 *.key 或 *.pem(隐私增强邮件)文件中。 There is no 从 pem 文件加载
X509Certificate2
实例的内置方法。有很多代码示例可用如何做到这一点,您可以在上面链接的问题中找到它们。然而,最简单的解决方案是创建 pfx 文件(包含私钥和公钥)。然后,您可以使用 X509Certificate2
的相应构造函数轻松加载 pfx。pfx 文件的创建是 very easy with SSL tool 。如果
private.key
包含私钥( -----BEGIN RSA PRIVATE KEY-----
)并且 public.crt
包含公钥( -----BEGIN CERTIFICATE-----
),您可以使用以下命令创建 pfx 文件:openssl pkcs12 -export -out keys.pfx -inkey private.key -in public.crt
您将被要求输入密码。当您将 key 加载到
X509Certificate2
时,也将使用此密码:Dim certificate As X509Certificate2 = New X509Certificate2("d:\CodeFuller\_days\2018.04.05\keys.pfx", "Password here")
现在
HasPrivateKey
属性设置为 True
并且 PrivateKey
返回 RSACryptoServiceProvider
的实例。更新
关于此代码:
'Round-trip the key to XML and back, there might be a better way but this works
Dim key As RSACryptoServiceProvider = New
RSACryptoServiceProvider
key.FromXmlString(privateCert.PrivateKey.ToXmlString(True))
RSACryptoServiceProvider
的实例实际上存储在 certificate.PrivateKey
中,因此您可以避免使用上述代码并将其替换为:Dim provider As RSACryptoServiceProvider = certificate.PrivateKey
但是,您当前的
SignData()
调用将不起作用:Dim sig() As Byte = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"))
这将引发以下异常:
System.Security.Cryptography.CryptographicException: 'Invalid algorithm specified.'
根本原因是
RSACryptoServiceProvider
does not support SHA256 。这就是为什么我建议通过以下方式用 RSACng
替换它: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)
方法
GetRSAPrivateKey
仅从 .NET Framework 4.6 开始添加到 X509Certificate2
类,因此如果出现以下错误,请考虑升级:error BC30456: 'GetRSAPrivateKey' is not a member of 'X509Certificate2'
更新 2 (关于 URL 验证)
您引用的 The page 包含用于验证签名的
openssl
命令:openssl dgst -sha256 -signature signature.bin -verify public-key.pem url.txt
但是,在您的情况下,这只是一次完整性检查,因为您刚刚使用有效程序生成了签名。所以回答你的问题:
How can I check whether the URL I now end up with is valid?
最好的检查就是使用签名 URL 向 AMP Cache 发送请求并检查响应。我之前没有使用过 AMP Cache,但我相信如果签名无效,它会响应一些 HTTP 错误。
UPDATE 3 (关于签名验证失败)
Update AMP Content page 包含以下用于签署 URL 的命令行:
echo -n >url.txt '/update-cache/c/s/example.com/article?amp_action=flush&_ts=1484941817' cat url.txt | openssl dgst -sha256 -sign private-key.pem >signature.bin
我已将此命令构建的结果签名与我的答案中的代码计算出的签名进行了比较。原来他们是有区别的。我研究了可能的根本原因,发现问题是由我们获取 URL 字节的方式引起的。目前是:
Dim data() As Byte = System.Text.Encoding.Unicode.GetBytes(signatureUrl)
但是,我们应该对以 ASCII 表示的 URL 进行签名。因此,将上面的行替换为:
Dim data() As Byte = System.Text.Encoding.ASCII.GetBytes(signatureUrl)
现在来自 openssl 实用程序和上面代码的两个签名都匹配。如果修复后您仍然从 Google AMP 获得
URL signature verification error
,那么问题将出在为签名传递的输入 URL 上。UPDATE 4 (从私钥和公钥获取 PFX)
生成私钥:
openssl genrsa 2048 > private-key.pem
生成公钥:
openssl rsa -in private-key.pem -pubout > public-key.pem
创建证书签名请求:
openssl req -new -key private-key.pem -out certificate.csr
创建证书:
openssl x509 -req -days 365 -in certificate.csr -signkey private-key.pem -out public.crt
您将在此处被要求提供一些证书字段,例如国家名称、组织名称等。您使用哪个值并不重要,因为您需要此证书用于测试目的。
创建 pfx 文件:
openssl pkcs12 -export -out keys.pfx -inkey private-key.pem -in public.crt
关于ASP.NET:使用文件中的公钥和私钥对 URL 进行签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49477892/