您如何验证 appengine.SignBytes 返回的签名?

How do you verify the signature returned by appengine.SignBytes?

Google App Engine 的 Go 运行时具有 SignBytes function, a PublicCertificates function, and a Certificate 结构。

func SignBytes

func SignBytes(c Context, bytes []byte) (keyName string, signature []byte, err error) 

SignBytes signs bytes using a private key unique to your application.

func PublicCertificates

func PublicCertificates(c Context) ([]Certificate, error) 

PublicCertificates retrieves the public certificates for the app. They can be used to verify a signature returned by SignBytes.

type Certificate

type Certificate struct {
    KeyName string
    Data    []byte // PEM-encoded X.509 certificate
}

Certificate represents a public certificate for the app.

很明显,应用程序需要遍历 public 证书来验证签名。但目前尚不清楚签名是如何生成或验证的。 Go 的 rsa package has two functions to verify signatures, VerifyPKCS1v15 and VerifyPSS, and each of those functions takes a crypto.Hash 标识符作为参数。目前,有 15 种不同的哈希标识符(例如,crypto.MD5、crypto.SHA256)给出了 2x15=30 种验证函数和哈希标识符的组合。

SignBytes 生成的签名是如何验证的?

使用 SHA256 验证 PKCS1v15

我通过尝试验证方案和哈希类型的所有组合发现了这一点;我没有找到保证这是将要使用的签名方案的文档。

但是对于勇敢的人来说,下面是一个代码示例,它可以对数据进行签名并验证签名。

package yourpackage

import (
    "crypto"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "errors"
    "google.golang.org/appengine"
    "net/http"
)

func signAndVerify(request *http.Request) error {
    c := appengine.NewContext(request)
    data := []byte("test data to sign")
    _, sig, err := appengine.SignBytes(c, data)
    if err != nil {
        return err
    }

    certs, err := appengine.PublicCertificates(c)
    if err != nil {
        return err
    }

    lastErr := errors.New("ErrNoPublicCertificates")

    for _, cert := range certs {
        block, _ := pem.Decode(cert.Data)
        if block == nil {
            lastErr = errors.New("ErrPemDecodeFailure")
            continue
        }
        x509Cert, err := x509.ParseCertificate(block.Bytes)
        if err != nil {
            lastErr = err
            continue
        }
        pubkey, ok := x509Cert.PublicKey.(*rsa.PublicKey)
        if !ok {
            lastErr = errors.New("ErrNotRSAPublicKey")
            continue
        }

        signBytesHash := crypto.SHA256
        h := signBytesHash.New()
        h.Write(data)
        hashed := h.Sum(nil)
        err = rsa.VerifyPKCS1v15(pubkey, signBytesHash, hashed, sig)
        if err != nil {
            lastErr = err
            continue
        }

        return nil
    }

    return lastErr
}

我还在 github 上的 package 中发布了验证步骤。

更新

Google 使用 SHA-256 提供一些 sample code that verifies SignBytes. In it, there is a file app-identity-samples-read-only/python/app_identity_test.py has a method named buildjwt that creates a JWT signed by SignBytes, and the JWT alg is RS256, which is defined in RFC 7518 RSASSA-PKCS1-v1_5。

注意:我使用的是 Go App Engine for Managed VMs(请参阅“google.golang.org/appengine”导入)而不是经典的 Go App Engine 运行时,尽管没有对于 SignBytes 的目的来说真的没有太大的不同。