Skip to content

Instantly share code, notes, and snippets.

@yfuruyama
Created December 10, 2018 06:13
Show Gist options
  • Save yfuruyama/57ae9d7a8d8f6ace07551ee06ff1f1d3 to your computer and use it in GitHub Desktop.
Save yfuruyama/57ae9d7a8d8f6ace07551ee06ff1f1d3 to your computer and use it in GitHub Desktop.
Generate Signed URL (Usage: `go run gcs_sign_url.go </path/to/service_account.json> </bucket/object>`)
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
)
type ServiceAccount struct {
ClientEmail string `json:"client_email"`
PrivateKey string `json:"private_key"`
}
func main() {
if len(os.Args) != 3 {
fmt.Printf("Usage: %s <SERVICE_ACCOUNT_KEY.json> </BUCKET/OBJECT>\n", os.Args[0])
os.Exit(-1)
}
serviceAccountPath := os.Args[1]
objectPath := os.Args[2]
serviceAccountJson, err := ioutil.ReadFile(serviceAccountPath)
if err != nil {
panic(err)
}
var serviceAccount ServiceAccount
err = json.Unmarshal(serviceAccountJson, &serviceAccount)
if err != nil {
panic(err)
}
expire := time.Now().Unix() + 600 // 10 minutes
var stringToSign string
stringToSign += "GET\n" // HTTP method
stringToSign += "\n" // content md5
stringToSign += "\n" // content type
stringToSign += fmt.Sprintf("%d\n", expire) // content type
stringToSign += objectPath // resource
block, _ := pem.Decode([]byte(serviceAccount.PrivateKey))
if block == nil {
panic(errors.New("invalid private key"))
}
keyInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
privateKey, ok := keyInterface.(*rsa.PrivateKey)
if !ok {
panic(errors.New("not RSA private key"))
}
privateKey.Precompute()
stringToSignHash := sha256.Sum256([]byte(stringToSign))
stringToSignHashSlice := stringToSignHash[:]
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, stringToSignHashSlice)
if err != nil {
panic(err)
}
encodedSignature := base64.RawStdEncoding.EncodeToString(signature)
encodedSignature = strings.Replace(encodedSignature, `+`, `%2B`, -1)
encodedSignature = strings.Replace(encodedSignature, `/`, `%2F`, -1)
signedUrl := fmt.Sprintf("https://storage.googleapis.com%s?GoogleAccessId=%s&Expires=%d&Signature=%s", objectPath, serviceAccount.ClientEmail, expire, encodedSignature)
fmt.Println(signedUrl)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment