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 \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) }