func createToken(ctx context.Context, channelID string) (string, error) { iss, err := appengine.ServiceAccount(ctx) if err != nil { return "", err } iat := time.Now().Unix() jwt := map[string]interface{}{ "iss": iss, "sub": iss, "aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit", "iat": iat, "exp": iat + 3600, // 1 hour "uid": channelID, } body, err := json.Marshal(jwt) if err != nil { return "", err } header := base64.StdEncoding.EncodeToString([]byte(`{"typ":"JWT","alg":"RS256"}`)) payload := append([]byte(header), byte('.')) payload = append(payload, []byte(base64.StdEncoding.EncodeToString(body))...) _, sig, err := appengine.SignBytes(ctx, payload) if err != nil { return "", err } return fmt.Sprintf("%s.%s", payload, base64.StdEncoding.EncodeToString(sig)), nil }
// Taken from http://stackoverflow.com/a/26579165/196964 and // https://cloud.google.com/storage/docs/access-control#Signed-URLs func generateSignedURLs(c context.Context, host, resource string, expiry time.Time, httpVerb, contentMD5, contentType string) (string, error) { sa, err := appengine.ServiceAccount(c) if err != nil { return "", err } expiryStr := strconv.FormatInt(expiry.Unix(), 10) // The optional components should be the empty string. // https://cloud.google.com/storage/docs/access-control#Construct-the-String components := []string{ httpVerb, // PUT, GET, DELETE (but not POST) contentMD5, // Optional. The MD5 digest value in base64. Client must provide same value if present. contentType, // Optional. Client must provide same value if present. expiryStr, // Unix timestamp resource, // /bucket/objectname } unsigned := strings.Join(components, "\n") _, b, err := appengine.SignBytes(c, []byte(unsigned)) if err != nil { return "", err } sig := base64.StdEncoding.EncodeToString(b) p := url.Values{ "GoogleAccessId": {sa}, "Expires": {expiryStr}, "Signature": {sig}, } return fmt.Sprintf("%s%s?%s", host, resource, p.Encode()), err }
// Sign signs the request parameters and sets the Signature field. // c must be an App Engine context created with appengine.NewContext. func (p *SignedRequest) Sign(c context.Context) error { _, sig, err := appengine.SignBytes(c, []byte(p.signingString())) if err != nil { return err } p.Signature = base64.StdEncoding.EncodeToString(sig) return nil }
// Implements the Sign method from SigningMethod // For this signing method, a valid appengine.Context must be // passed as the key. func (s *SigningMethodAppEngine) Sign(signingString string, key interface{}) (string, error) { var ctx context.Context switch k := key.(type) { case context.Context: ctx = k default: return "", jwt.ErrInvalidKey } _, signature, err := appengine.SignBytes(ctx, []byte(signingString)) if err != nil { return "", err } return jwt.EncodeSegment(signature), nil }
func TestSignatureVerification(t *testing.T) { c, closer, err := aetest.NewContext() if err != nil { t.Fatal(err) } defer closer() data := []byte("hello, world!") _, sig, err := appengine.SignBytes(c, data) if err != nil { t.Fatalf("Error signing data. %v", err) } if err := VerifyBytes(c, data, sig); err != nil { t.Fatalf("Expected verification to succeed, but if failed. %v", err) } data2 := []byte("hello, world!!") if err := VerifyBytes(c, data2, sig); err == nil { t.Fatalf("Expected verification to fail, but if succeeded") } }
// Sign returns a signing key name and a signature. func Sign(c context.Context, blob []byte) (keyName string, signature []byte, err error) { d512 := sha512.Sum512(blob) return appengine.SignBytes(c, d512[:]) }
func (g giImpl) SignBytes(bytes []byte) (keyName string, signature []byte, err error) { return appengine.SignBytes(g.aeCtx, bytes) }