ASP.NET:使用文件中的公钥和私钥对URL进行签名 [英] ASP.NET: Sign URL with public and private keys from files

查看:229
本文介绍了ASP.NET:使用文件中的公钥和私钥对URL进行签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想更新Google AMP缓存,因此我需要按照这里.

I want to update Google AMP cache, so I need to sign an URL as described here.

我的主要问题:我在如何获取证书/密钥以及如何将它们包含在下面的代码中而苦苦挣扎. 我只是找不到适用于Windows和IIS的所有涵盖说明.

My main issue: I'm struggling massively with how I should get my certificates/keys and how to include them in my code below. I just can't find any all covering instructions for Windows and IIS.

我一直在阅读这些帖子:

I have been reading these posts:

  1. 使用/update-cache请求来更新AMP页面
  2. 如何签名使用RSA和SHA256和.NET的文件?
  1. Using /update-cache requests to update AMP pages
  2. How can I sign a file using RSA and SHA256 with .NET?

我不想使用第二篇文章中所述的计算机证书存储.我宁愿使用磁盘上的文件作为公钥和私钥. 从生产服务器IIS,我将证书导出到.pfx文件,然后使用

I don't want to use my computer's certificate store as described in the second post. I'd rather use files on disk for both public and private keys. From my production server IIS, I exported my certificate to a .pfx file, from which I then extracted the private key and certificate using the instructions on the bottom of this site. The server.key contains -----BEGIN RSA PRIVATE KEY-----, which If I use that to load into the privateCert variable in the code below throws error Cannot find the requested object.

我从SSL提供商那里得到了什么: www_example_com.crtwww_example_com.p7bcertificate code(请参见下文).

What I have gotten from my SSL provider: www_example_com.crt, www_example_com.p7b, the certificate code (see below).

我已采取的步骤:

  1. 我通过打开www_example.com.crt并使用Copy to file向导将其复制到base64编码的.cer文件中来创建test-public-key.cer.
  2. 我保存了从SSL提供程序收到的certificate code作为文件test-private-key.cer.
  1. I created test-public-key.cer by opening www_example.com.crt and using the Copy to file wizard to copy it to a base64 encoded .cer file.
  2. I saved certificate code I received from my SSL provider as file test-private-key.cer.

运行以下代码时出现错误

When I run the following code I get error

对象引用未设置为对象的实例.

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函数

EncodeTo64 function

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证书-----
MIIF(...)DXuJ
-----结束证书-----

-----BEGIN CERTIFICATE-----
MIIF (...) DXuJ
-----END CERTIFICATE-----

更新1

通过执行

I was able to generate a mysite.pfx file by following the export steps on this page. In the wizard I made sure to select "Yes, export the private key" and I added a password. The rest of the steps I followed verbatim.

然后我还运行了以下命令:
openssl pkcs12 -in mysite.pfx -nocerts -out private-key-VPS.pem
penssl pkcs12 -in mysite.pfx -clcerts -nokeys -out certificate-VPS.pem

I then also ran these commands:
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文件

I ended up with private-key-VPS.pem and a certificate-VPS.pem files

我知道获取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个错误:

But there I get 4 errors:

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

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框架,现在似乎又往前迈了一步.我最终得到一个像这样的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? 我正在检查此页面上的生成RSA密钥"部分,但是我很困惑,因为我实际上已经对这些步骤进行了编码?如何检查我现在结尾的URL是否有效?

Thanks @CodeFuller! I targeted framework 4.6.1 and now seem to be 1 step further. I end up with an URL like this: 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==. How can I now check if it's a valid URL? I'm checking section "Generate the RSA key" on this page, but I'm confused, since I actually already just coded these steps or not? How can I check whether the URL I now end up with is valid?

更新3

好的,我尝试了您的新代码.仍然获取URL signature verification error.我同时尝试了文章的/amp URL和URL中的/amp部分.两者都会导致相同的URL签名验证错误.

Ok, I tried your new code. Still get the URL signature verification error. I tried with both the /amp URL of my article and without the /amp part in my URL. Both result in the same URL signature verification error.

当我将最终URL打印到我的网站时(请参见下面的代码),我注意到URL如下:

I noticed when I print the final URL to my website (see code below), the URL reads:

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.

Notice that where the parameters should be amp_ts and amp_url_signature, they now are _ts and _url_signature respectively.

在我致电Google之前,我曾尝试通过手动将参数_ts_url_signature重命名为amp_tsamp_url_signature来编辑URL.但是我想这会导致签名和实际URL之间的差异.难道是我的代码以某种方式破坏了&字符,因此当我以后手动重命名这些字符时,总是会导致签名验证吗?您看到我的代码中可以解决的问题吗?

I tried editing the URL before I do the call to Google by manually renaming parameters _ts and _url_signature to amp_ts and amp_url_signature. But I guess that would result in a difference between the signature and the actual URL. Could it be that somehow my code botches the & character and therefore when I rename these manually later it always result in a signature verification? Do you see what I could fix in my code?

顺便说一句:我在签名URL之前尝试用代码隐藏的&替换&,但是随后出现Google 404错误:

BTW: I tried replacing & with %26 in code-behind before signing the URL but then I get a Google 404 error:

请求的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 ==.这就是我们所知道的.

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&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>"

此外,我确定此页面存在于Google AMP缓存中,因为我可以在移动设备上的Google搜索结果中看到并请求它.

Also, I'm sure this page exists in Google AMP cache, since I can see and request it in Google's search results on my mobile device.

更新4

我想我正在接近,并且也获得了一些其他帮助,请参见此处:

I'm getting close I think and also getting some extra help, see here: https://github.com/ampproject/amphtml/issues/14483#issuecomment-380549060

我现在正在尝试使其他人也更容易进行测试:现在,我不再依赖我的SSL,而是运行以下命令来获取公钥和私钥

What I'm trying now to make it easier for others to test as well: Instead of depending on my SSL I now ran the following commands to get a public and private key

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

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

我将public-key.pem重命名为apikey.pub并将其放置在https://example.com/.well-known/amphtml/apikey.pub

I'll rename public-key.pem to apikey.pub and place that on 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

I want to take the easiest approach recommended by @CodeFuller and create a .pfx file that I can then load into a variable of type X509Certificate2. When I run this command:
openssl pkcs12 -export -out keys.pfx -inkey private-key.pem -in public-key.pem

我收到错误:unable to load certificates

但是这次我没有.crt文件,只有public-key.pem.如何获取.pfx文件?我已经在此处选中了.

But this time I don't have a .crt file, only a public-key.pem. How can I get a .pfx file? I already checked here.

推荐答案

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

I saved certificate code I received from my SSL provider as file test-private-key.cer

...

证书代码

----- BEGIN证书-----
MIIF(...)DXuJ
-----结束证书-----

-----BEGIN CERTIFICATE-----
MIIF (...) DXuJ
-----END CERTIFICATE-----

以某种格式存储的文件

-----BEGIN CERTIFICATE----- 
MIIF (...) DXuJ 
-----END CERTIFICATE-----

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

is a certificate which basically contains a public key. It does not contain private key. That's why when you create instance of X509Certificate2 from such file, it's HasPrivateKey property is set to False and PrivateKey returns Nothing, and following statement expectedly throws NullReferenceException:

privateCert.PrivateKey.ToXmlString(True)

要对数据进行签名,您需要一个私钥.私钥具有以下格式

In order to sign the data, you need a private key. Private keys have the following format

-----BEGIN RSA PRIVATE KEY-----
MIICXQ...B7Bou+
-----END RSA PRIVATE KEY-----

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

Such private keys are usually stored in *.key or *.pem (Privacy Enhanced Mail) files. There is no built-in way to load instance of X509Certificate2 from pem file. There are a lot of code samples available how to do it, you will find them in the question linked above. However the easiest solution will be to create pfx file (containing both private and public keys). Then you could easily load pfx with corresponding constructor of X509Certificate2.

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

Creation of pfx file is very easy with SSL tool. If private.key contains private key (-----BEGIN RSA PRIVATE KEY-----) and public.crt contains public key (-----BEGIN CERTIFICATE-----), they you could create pfx file with the following command:

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

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

将要求您输入密码.当您将密钥加载到X509Certificate2时,也会使用该密码:

You will be asked to enter the password. This password will also be used when you load the key to X509Certificate2:

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

现在HasPrivateKey属性设置为True,并且PrivateKey返回RSACryptoServiceProvider的实例.

Now HasPrivateKey property is set to True and PrivateKey returns the instance of 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中,因此您可以避免上面的代码并将其替换为:

The instance of RSACryptoServiceProvider is actually stored in certificate.PrivateKey so you could avoid above code and replace it with:

Dim provider As RSACryptoServiceProvider = certificate.PrivateKey

但是您当前的SignData()通话将无法正常工作

However your current SignData() call will not work:

Dim sig() As Byte = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"))

这将引发以下异常:

System.Security.Cryptography.CryptographicException:'无效 指定的算法.

System.Security.Cryptography.CryptographicException: 'Invalid algorithm specified.'

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

The root cause is that RSACryptoServiceProvider does not support SHA256. That's why I suggest replacing it with RSACng in the following way:

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类,因此,如果出现以下错误,请考虑升级:

Method GetRSAPrivateKey was added to X509Certificate2 class only since .NET Framework 4.6, so consider upgrading if you get following error:

错误BC30456:"GetRSAPrivateKey"不是以下成员 'X509Certificate2'

error BC30456: 'GetRSAPrivateKey' is not a member of 'X509Certificate2'

更新2 (关于URL验证)

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

The page you referenced contains openssl command for verifying the signature:

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

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

但是,在您的情况下,这只是一个健全性检查,因为您刚刚使用有效过程生成了签名.因此,回答您的问题:

However in your case it will be just a sanity check, because you have just generated the signature with a valid procedure. So answering your question:

如何检查我现在结尾的URL是否有效?

How can I check whether the URL I now end up with is valid?

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

The best check is just to send request to AMP Cache with signed URL and check the response. I haven't used AMP Cache before but I believe it will respond with some HTTP error if the signature is invalid.

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

UPDATE 3 (regarding failed signature verification)

更新AMP内容"页面包含用于签名URL的以下命令行:

Update AMP Content page contains following command line for signing the URL:

echo -n> url.txt '/update-cache/c/s/example.com/article?amp_action=flush&amp_ts=1484941817' 猫url.txt | openssl dgst -sha256 -sign private-key.pem> signature.bin

echo -n >url.txt '/update-cache/c/s/example.com/article?amp_action=flush&amp_ts=1484941817' cat url.txt | openssl dgst -sha256 -sign private-key.pem >signature.bin

我已将此命令构建的结果签名与答案中的代码计算出的签名进行了比较.事实证明它们是不同的.我研究了可能的根本原因,发现问题是由我们获取URL字节的方式引起的.当前是:

I have compared result signature built by this command with the signature calculated by the code from my answer. It turned out that they differ. I have researched the possible root cause and found that the problem is caused by the way we get URL bytes. Currently it's:

Dim data() As Byte = System.Text.Encoding.Unicode.GetBytes(signatureUrl)

但是,我们应该对以ASCII表示的URL进行签名.因此,将上面的行替换为:

However we should sign the URL represented in ASCII. So replace above line with:

Dim data() As Byte = System.Text.Encoding.ASCII.GetBytes(signatureUrl)

现在,openssl实用程序和上面的代码中的两个签名都匹配.如果在修复后仍从Google AMP获得URL signature verification error,则问题出在传递用于签名的输入URL.

Now both signatures, from openssl utility and the code above, matches. If after the fix you still get URL signature verification error from Google AMP, then the problem will be with the input URL passed for signing.

UPDATE 4 (从私钥和公钥获取PFX)

UPDATE 4 (Getting PFX from private and public keys)

生成私钥:

openssl genrsa 2048> private-key.pem

openssl genrsa 2048 > private-key.pem

生成公共密钥:

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

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

创建证书签名请求:

openssl req -new -key private-key.pem -out certificate.csr

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

openssl x509 -req -days 365 -in certificate.csr -signkey private-key.pem -out public.crt

此处将要求您输入一些证书字段,例如国家名称,组织名称等.使用哪个值并不重要,因为出于测试目的需要此证书.

You will be asked here for some certificate fields, e.g. Country Name, Organization Name, etc. It does not really matter which values you use, since you need this certificate for test purposes.

创建pfx文件:

openssl pkcs12-导出-out keys.pfx -inkey private-key.pem -in public.crt

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

这篇关于ASP.NET:使用文件中的公钥和私钥对URL进行签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆