简体   繁体   中英

Update Google AMP cache URL signature verification error

I'm trying to update my Google AMP pages in the Google AMP Cache, but get an URL signature verification error.

My code:

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&amp_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)

Encoding 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

I try to call Google AMP cache with this URL .

Now, I get a 403 error:

Your client does not have permission to get URL /update-cache/c/s/www.example.com/articles/278/myarticle/amp?amp_action=flush&amp_ts=1523016476&amp_url_signature=U2lzdGVtLkJ5dGVbdQ. URL signature verification error. That's all we know.

I find the Google example code not clear enough: https://developers.google.com/amp/cache/update-cache

My questions are around the signature URL:

  1. do I use the article AMP URL or the regular URL?
  2. do I need to include the querystring parameters amp_action and amp_ts in my signature URL? Or do I append these later after I've signed the URL?
  3. should I prepend the ampBaseUrl above to my signatureUrl variable or is this not needed?

UPDATE 1

Based on @CodeFuller's recommendations, I checked the URL and am getting a Verified OK message. I've also taken care of step 2:

  1. APIkeys match: https://www.toptrouwen.nl/.well-known/amphtml/apikey.pub matches https://www-toptrouwen-nl.cdn.ampproject.org/r/s/www.toptrouwen.nl/.well-known/amphtml/apikey.pub
  2. Serving apikey.pub from my server as text/plain
  3. serving apikey.pub over https and publicly
  4. Added disallow in robots: User-agent: * Disallow: /.well-known/amphtml/apikey.pub

UPDATE 2

Yes, with the new code I also get Verified OK on verification.

This URL is generated: 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==

Then I get error Required query parameter 'amp_url_signature' missing. (related to earlier issue where amp_ parameters get botched.

I then renamed URL parameters to their correct names: 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&amp_ts=1523138180&amp_url_signature=tKPO3k624ybwxoEynqN8oI3/UDxhq1TF8jX9aKeVyL0IWLUODXuMB7ansP0t1+/5Lm2V7RYZbUWxt2Whh7+LFEmfQFGJJE/iPtoBVsqrdb5356QwiIrDHOzY+3z5dASZxYlAwlfzUFdonGyDsh/UlCjjvvNahFEWzHOpB5JQxJQ1Wn0kGLQUF1v2u47abbae6cNQBm3YB/0Z1FLfTJLM1oOEOSDh9vQH1SqO/6SoYtUhSQjSrYdl/g5O0QJ7A9pKUxOPfgVJM0l8Sgb66cVeWWoWq0WIFe24RPXUMl9tIFFZ1TY2R+ZpIMvpEAPDjCsdGPo7KTWqGb4qfoTBINJmtQ==

Then I get: 404 Failed to decode amp_url_signature , I thought this was because there are + and \\ characters in the URL. When I remove those I get the error URL signature verification error again.

I don't think the UTC timestamp is currently an issue, because I've seen before that Google will throw an error if the timestamp is incorrect.

There are two most frequent reasons for URL signature verification error :

  1. Incorrect calculation of the signature.

    So the first thing to is to verify that signature is actually valid. Save signed URL and result signature to some files:

     Dim signatureUrl As String = "/update-cache/c/s/www.example.com/articles/278/myarticle/amp?amp_action=flush&amp_ts=" + tStamp ' ... Dim sig() As Byte = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1) File.WriteAllText("url.txt", signatureUrl) File.WriteAllBytes("signature.bin", sig)

    Then verify the signature with openssl tool:

    openssl.exe dgst -sha256 -signature signature.bin -verify publickey.pem url.txt

    If the output is

    Verified OK

    then your signature is OK and you should search the problem in other place (see item #2).

    If the output is

    Verification Failure

    then you should recheck the routine of signature calculation.

    publickey.pem in command above is the public key in PEM format. It should start with the following line:

    -----BEGIN PUBLIC KEY-----

    If you have certificate file (starting with -----BEGIN CERTIFICATE----- ), you could get PEM for it with the following command:

    openssl.exe x509 -pubkey -noout -in publickey.cer > publickey.pem

  2. If signature was verified successfully, then you should check whether your public key is accessible over the Web. Update AMP Content states following requirement:

    For signature verification, you must serve the public RSA key at a fixed location on the AMP document's domain (to generate the key, see Generate the RSA key). For example:

    https://example.com/.well-known/amphtml/apikey.pub

    • The public key must not be roboted.

    • The URL must be HTTPS.

    • The domain must be the exact domain that you want to update, not a sub or super domain.

    • You must publish the key in PEM format and serve the key with the content-type "text/plain".

    So if your AMP base URL is https://www.test.com , check that your public key is accessible at following URL: https://www.test.com/.well-known/amphtml/apikey.pub

Here are answers to your other questions:

do I need to include the querystring parameters amp_action and amp_ts in my signature URL? Or do I append these later after I've signed the URL?

Yes, you should include amp_action and amp_ts into the URL for which signature is calculated.

should I prepend the ampBaseUrl above to my signatureUrl variable or is this not needed?

As stated Update AMP Content article:

The AMP Cache hostname (cdn.ampproject.org) is excluded from the signature to allow submitting the same signed request to multiple AMP Cache operators.

So you should not include ampBaseUrl into the URL for which signature is calculated.

UPDATE

I have found another error in the code where you append signature to the URL:

Dim AMPURLSignature As String = EncodeTo64(sig.ToString)

sig.ToString will result to passing of "System.Byte[]" string to EncodeTo64 , instead of actual signature bytes. As result incorrect signature was appended to update cache URL. Replace above call with the following ( EncodeTo64 is not required anymore):

Dim AMPURLSignature As String = System.Convert.ToBase64String(sig)

Another possible problem is that timestamp you use does not satisfyfollowing requirement :

The value should be the current time in seconds, which must be within 1 minute before or after the current time.

You pass DateTime.Now to GetUnixTimeStampFromDateTime() but it should be UTC time, not local time. Try replacing tStamp calculation with:

Dim tStamp As String = CInt((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds).ToString

Also make sure you call update-cache URL just after it was generated, so that current time is within 1 minute interval comparing to timestamp. And your sytem time should be precise.

Please recheck whether signature verification passes after above fixes.

UPDATE 2 (Regarding + and / characters in signature)

AMP cache update signature should go in variation of base4 encoding:

Encode the binary RSA signature using the web-safe variant of base64

So another required fix is to replace + and / charcters with - and _ .

Change following call

Dim AMPURLSignature As String = System.Convert.ToBase64String(sig)

with:

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

UPDATE 3

Here is the final code that should produce correct update-cache URL (based on available documentation ):

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&amp_ts=" + tStamp

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

        Dim certificate = New X509Certificate2("d:\temp\keys\keys.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 + "&amp_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

Sample Project on GitHub

It produces following 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_action=flush&amp_ts=1523188941&amp_url_signature=bZJTE4IjlxGhlU79etivzUPpGFoyKvCxqPO1IOPHfzDKQVt-fA8Mte20SeXjTQs24Uy4RD9lmbS2aXlcCTpOYatF2l8PQ-31kR-lKVnuduSZIrg93g2YrvO7x-a6dr19hN74LywgBw4C_JfuocCuGfVvr-mD40tuwkBrsLgmI9E=

This signature is verified successfully with opensll tool however getting this URL still returns URL signature verification error. . However now everything seems fine on our side. I have found related issue on github . There is following statement from AMP project contributor :

First of all, I determined that AMP Cache does not handle HTTP refresh correctly for update-cache verification keys: if you issue an update-cache request and then swap /.well-known/amphtml/apikey.pub with a different key, we keep using the old key material indefinitely. To make things worse, 404 responses are also cached forever :-(

I filed an internal bug report, but it might take some time for the fix to roll out to production. Meanwhile, I can flush invalid keys manually. Just send me a private message either on GitHub or on amphtml.slack.com. Apologies for not discovering this sooner.

Seems like it's a root cause of current error. You have tried updating the cache while there was no public key available at https://www.toptrouwen.nl/.well-known/amphtml/apikey.pub (When I checked it first time it was resulted to 404 error). Google has cached this result and now even when certificate is avaiable it's not actually used.

Seems like the only possible workaround for this moment is to contact codewiz and ask him to flush the cached key error. Until AMP system uses valid public key, signature verification is not possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM