From 03a41b25d44b6b3b6f94e2515bac72645d012107 Mon Sep 17 00:00:00 2001 From: Burcu Dogan Date: Sat, 16 Aug 2014 18:04:02 -0700 Subject: [PATCH] Don't assume private key to be available on a traditional file system. --- example_test.go | 13 +++++++---- google/example_test.go | 13 +++++++---- jwt.go | 49 +++++++++++++++++------------------------- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/example_test.go b/example_test.go index d7afa38..bbb91b6 100644 --- a/example_test.go +++ b/example_test.go @@ -53,11 +53,16 @@ func Example_config() { func Example_jWTConfig() { conf, err := oauth2.NewJWTConfig(&oauth2.JWTOptions{ Email: "xxx@developer.gserviceaccount.com", - // The path to the pem file. If you have a p12 file instead, you + // The contents of your RSA private key or your PEM file + // that contains a private key. + // If you have a p12 file instead, you // can use `openssl` to export the private key into a pem file. - // $ openssl pkcs12 -in key.p12 -out key.pem -nodes - PEMFilename: "/path/to/pem/file.pem", - Scopes: []string{"SCOPE1", "SCOPE2"}, + // + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + // It only supports PEM containers with no passphrase. + PrivateKey: []byte("PRIVATE KEY CONTENTS"), + Scopes: []string{"SCOPE1", "SCOPE2"}, }, "https://provider.com/o/oauth2/token") if err != nil { diff --git a/google/example_test.go b/google/example_test.go index e492507..380bd47 100644 --- a/google/example_test.go +++ b/google/example_test.go @@ -49,10 +49,15 @@ func Example_serviceAccounts() { // Developer Console (https://console.developers.google.com). config, err := google.NewServiceAccountConfig(&oauth2.JWTOptions{ Email: "xxx@developer.gserviceaccount.com", - // PEMFilename. If you have a p12 file instead, you - // can use `openssl` to export the private key into a pem file. - // $ openssl pkcs12 -in key.p12 -out key.pem -nodes - PEMFilename: "/path/to/pem/file.pem", + // The contents of your RSA private key or your PEM file + // that contains a private key. + // If you have a p12 file instead, you + // can use `openssl` to export the private key into a PEM file. + // + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + // Supports only PEM containers without a passphrase. + PrivateKey: []byte("PRIVATE KEY CONTENTS"), Scopes: []string{ "https://www.googleapis.com/auth/bigquery", }, diff --git a/jwt.go b/jwt.go index f69f7cc..6328586 100644 --- a/jwt.go +++ b/jwt.go @@ -10,7 +10,6 @@ import ( "encoding/json" "encoding/pem" "errors" - "io/ioutil" "net/http" "net/url" "strings" @@ -31,17 +30,15 @@ type JWTOptions struct { // the configured OAuth provider. Email string `json:"email"` - // PrivateKey is an RSA private key to sign JWS payloads. - PrivateKey *rsa.PrivateKey `json:"-"` - - // The path to a PEM container that includes your private key. - // If PrivateKey is set, this field is ignored. + // PrivateKey contains the contents of an RSA private key or the + // contents of a PEM file that contains a private key. The provided + // private key is used to sign JWT payloads. + // PEM containers with a passphrase are not supported. + // Use the following command to convert a PKCS 12 file into a PEM. // - // If you have a p12 file instead, you - // can use `openssl` to export the private key into a PEM file. - // $ openssl pkcs12 -in key.p12 -out key.pem -nodes - // PEM file should contain your private key. - PEMFilename string `json:"pemfilename"` + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + PrivateKey []byte `json:"-"` // Scopes identify the level of access being requested. Scopes []string `json:"scopes"` @@ -54,14 +51,7 @@ func NewJWTConfig(opts *JWTOptions, aud string) (*JWTConfig, error) { if err != nil { return nil, err } - if opts.PrivateKey != nil { - return &JWTConfig{opts: opts, aud: audURL, key: opts.PrivateKey}, nil - } - contents, err := ioutil.ReadFile(opts.PEMFilename) - if err != nil { - return nil, err - } - parsedKey, err := parsePemKey(contents) + parsedKey, err := parseKey(opts.PrivateKey) if err != nil { return nil, err } @@ -155,25 +145,26 @@ func (c *JWTConfig) FetchToken(existing *Token) (token *Token, err error) { return } -// parsePemKey parses the pem file to extract the private key. -// It returns an error if private key is not provided or the -// provided key is invalid. -func parsePemKey(key []byte) (*rsa.PrivateKey, error) { - invalidPrivateKeyErr := errors.New("oauth2: private key is invalid") +// parseKey converts the binary contents of a private key file +// to an *rsa.PrivateKey. It detects whether the private key is in a +// PEM container or not. If so, it extracts the the private key +// from PEM container before conversion. It only supports PEM +// containers with no passphrase. +func parseKey(key []byte) (*rsa.PrivateKey, error) { block, _ := pem.Decode(key) - if block == nil { - return nil, invalidPrivateKeyErr + if block != nil { + key = block.Bytes } - parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + parsedKey, err := x509.ParsePKCS8PrivateKey(key) if err != nil { - parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) + parsedKey, err = x509.ParsePKCS1PrivateKey(key) if err != nil { return nil, err } } parsed, ok := parsedKey.(*rsa.PrivateKey) if !ok { - return nil, invalidPrivateKeyErr + return nil, errors.New("oauth2: private key is invalid") } return parsed, nil }