ASP.NET:使用文件中的公钥和私钥对URL进行签名

Flo*_*Flo 13 asp.net ssl rsa certificate google-amp

我想更新Google AMP缓存,因此我需要按此处所述签署一个网址.

我的主要问题:我正在努力获取我的证书/密钥,以及如何将它们包含在我的代码中.我找不到任何关于Windows和IIS的覆盖说明.

我一直在阅读这些帖子:

  1. 使用/ update-cache请求更新AMP页面
  2. 如何使用RSA和SHA256与.NET签署文件?

我不想使用第二篇文章中描述的计算机证书存储区.我宁愿在磁盘上使用公钥和私钥.从我的生产服务器IIS,我将我的证书导出到.pfx文件,然后我使用此站点底部的说明从中提取私钥和证书.server.key包含-----BEGIN RSA PRIVATE KEY-----,如果我使用它加载到privateCert下面代码中的变量抛出错误Cannot find the requested object.

我已经从我的SSL提供商得到: www_example_com.crt,www_example_com.p7b,的certificate code(见下文).

我采取的步骤:

  1. test-public-key.cer通过打开www_example.com.crt并使用Copy to file向导将其复制到base64编码的.cer文件来创建.
  2. certificate code收到了来自我的SSL提供商的文件test-private-key.cer.

当我运行以下代码时,我收到错误

你调用的对象是空的.

在线 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

certificate code

-----开始证书-----
MIIF(...)DXuJ
-----结束证书-----

更新1

我可以按照此页面上的导出步骤生成一个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)
Run Code Online (Sandbox Code Playgroud)

但在那里我得到4个错误:

GetRSAPrivateKey'不是'X509Certificate2'的成员.
'SignData'不是'RSA'的成员.
未声明'HashAlgorithmName'.由于其保护级别,它可能无法访问.
'RSASignaturePadding'未声明.由于其保护级别,它可能无法访问.

更新2

谢谢@CodeFuller!我针对框架4.6.1,现在似乎更进了一步.我最终得到一个这样的网址: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?我正在检查此页面上的"生成RSA密钥"部分,但我很困惑,因为我实际上已经编写过这些步骤了吗?如何检查我现在最终使用的URL是否有效?

更新3

好的,我尝试了你的新代码.还是得到了URL signature verification error.我尝试了/amp我的文章的URL和我的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_tsamp_url_signature,它们现在分别是_ts_url_signature.

我试图编辑URL我做调用谷歌通过手动重命名参数之前_ts,并_url_signatureamp_tsamp_url_signature.但我想这会导致签名和实际URL之间的差异.难道不知我的代码搞砸的&角色,因此,当我手动重命名这些后,它总是导致签名验证?你看到我能在我的代码中修复什么吗?

顺便说一句:我在签署URL之前尝试用代码隐藏代替&,%26但后来我收到了Google 404错误:

请求的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%在此服务器上找不到26 amp_url_signature=U3lzdGVrLkJ1dGVbXQ==.我们知道的就这些.

我的代码:

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&amp_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 + "&amp_url_signature=" + AMPURLSignature

ltStatus.Text = "AMP URL:<a target='_blank' href='" + url + "'>" + url + "</a>"
Run Code Online (Sandbox Code Playgroud)

此外,我确信此页面存在于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
Run Code Online (Sandbox Code Playgroud)

我现在有文件private-key.pempublic-key.pem

我将重命名并将public-key.pemapikey.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文件?我已经签在这里.

Cod*_*ler 4

我将从 SSL 提供商收到的证书代码保存为文件 test-private-key.cer

...

证书代码

-----开始证书-----
MIIF (...) DXuJ
-----结束证书-----

文件以某种格式存储

-----BEGIN CERTIFICATE----- 
MIIF (...) DXuJ 
-----END CERTIFICATE-----
Run Code Online (Sandbox Code Playgroud)

是一个基本上包含公钥的证书。它不包含私钥。这就是为什么当您从此类文件创建 的实例时X509Certificate2,它的HasPrivateKey属性设置为FalsePrivateKey返回Nothing,并且以下语句预期会抛出NullReferenceException

privateCert.PrivateKey.ToXmlString(True)
Run Code Online (Sandbox Code Playgroud)

为了对数据进行签名,您需要私钥。私钥具有以下格式

-----BEGIN RSA PRIVATE KEY-----
MIICXQ...B7Bou+
-----END RSA PRIVATE KEY-----
Run Code Online (Sandbox Code Playgroud)

此类私钥通常存储在 *.key 或 *.pem(隐私增强邮件)文件中。没有内置方法可以X509Certificate2从 pem 文件加载实例。有很多可用的代码示例如何执行此操作,您可以在上面链接的问题中找到它们。然而,最简单的解决方案是创建 pfx 文件(包含私钥和公钥)。然后你可以轻松地使用相应的构造函数加载 pfx X509Certificate2

使用 SSL 工具创建 pfx 文件非常容易。如果private.key包含私钥 ( -----BEGIN RSA PRIVATE KEY-----) 并public.crt包含公钥 ( -----BEGIN CERTIFICATE-----),则可以使用以下命令创建 pfx 文件:

openssl pkcs12 -export -out keys.pfx -inkey private.key -in public.crt

系统将要求您输入密码。当您将密钥加载到以下位置时,也会使用此密码X509Certificate2

Dim certificate As X509Certificate2 = New X509Certificate2("d:\CodeFuller\_days\2018.04.05\keys.pfx", "Password here")
Run Code Online (Sandbox Code Playgroud)

现在HasPrivateKey属性设置为TruePrivateKey返回 的实例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))
Run Code Online (Sandbox Code Playgroud)

的实例RSACryptoServiceProvider实际上存储在中certificate.PrivateKey,因此您可以避免上面的代码并将其替换为:

Dim provider As RSACryptoServiceProvider = certificate.PrivateKey
Run Code Online (Sandbox Code Playgroud)

但是您当前的SignData()通话将不起作用:

Dim sig() As Byte = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"))
Run Code Online (Sandbox Code Playgroud)

这将引发以下异常:

System.Security.Cryptography.CryptographicException:“指定的算法无效。”

根本原因是RSACryptoServiceProvider 不支持SHA256RSACng这就是为什么我建议用以下方式替换它:

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)
Run Code Online (Sandbox Code Playgroud)

该方法仅从 .NET Framework 4.6 起GetRSAPrivateKey添加到类中,因此如果出现以下错误,请考虑升级:X509Certificate2

错误 BC30456:“GetRSAPrivateKey”不是“X509Certificate2”的成员

更新 2(关于 URL 验证)

您引用的页面openssl包含用于验证签名的命令:

openssl dgst -sha256 -signature signature.bin -verify public-key.pem url.txt

然而,在您的情况下,这只是一个健全性检查,因为您刚刚使用有效的程序生成了签名。所以回答你的问题:

如何检查我现在得到的 URL 是否有效?

最好的检查是将请求发送到带有签名 URL 的 AMP 缓存并检查响应。我以前没有使用过 AMP 缓存,但我相信如果签名无效,它会响应一些 HTTP 错误。

更新3(关于签名验证失败)

更新 AMP 内容页面包含以下用于对 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 实用程序和上面代码的两个签名都匹配。如果修复后您仍然URL signature verification error从 Google AMP 获得信息,则问题将出在用于签名的输入 URL 上。

更新 4(从私钥和公钥获取 PFX)

生成私钥:

openssl genrsa 2048 > 私钥.pem

生成公钥:

openssl rsa -in private-key.pem -pubout > public-key.pem

创建证书签名请求:

openssl req -new -key private-key.pem -out 证书.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


归档时间:

查看次数:

649 次

最近记录:

7 年,4 月 前