1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
//
// gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta
//
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
//
package auth
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"net/url"
"strings"
)
const (
// Authorization Headers
SdcSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s"
MantaSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\",signature=\"%s\""
)
type Endpoint struct {
URL string
}
type Auth struct {
User string
PrivateKey PrivateKey
Algorithm string
}
type Credentials struct {
UserAuthentication *Auth
SdcKeyId string
SdcEndpoint Endpoint
MantaKeyId string
MantaEndpoint Endpoint
}
type PrivateKey struct {
key *rsa.PrivateKey
}
// NewAuth creates a new Auth.
func NewAuth(user, privateKey, algorithm string) (*Auth, error) {
block, _ := pem.Decode([]byte(privateKey))
if block == nil {
return nil, fmt.Errorf("invalid private key data: %s", privateKey)
}
rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("An error occurred while parsing the key: %s", err)
}
return &Auth{user, PrivateKey{rsakey}, algorithm}, nil
}
// The CreateAuthorizationHeader returns the Authorization header for the give request.
func CreateAuthorizationHeader(headers http.Header, credentials *Credentials, isMantaRequest bool) (string, error) {
if isMantaRequest {
signature, err := GetSignature(credentials.UserAuthentication, "date: "+headers.Get("Date"))
if err != nil {
return "", err
}
return fmt.Sprintf(MantaSignature, credentials.UserAuthentication.User, credentials.MantaKeyId,
credentials.UserAuthentication.Algorithm, signature), nil
}
signature, err := GetSignature(credentials.UserAuthentication, headers.Get("Date"))
if err != nil {
return "", err
}
return fmt.Sprintf(SdcSignature, credentials.UserAuthentication.User, credentials.SdcKeyId,
credentials.UserAuthentication.Algorithm, signature), nil
}
// The GetSignature method signs the specified key according to http://apidocs.joyent.com/cloudapi/#issuing-requests
// and http://apidocs.joyent.com/manta/api.html#authentication.
func GetSignature(auth *Auth, signing string) (string, error) {
hashFunc := getHashFunction(auth.Algorithm)
hash := hashFunc.New()
hash.Write([]byte(signing))
digest := hash.Sum(nil)
signed, err := rsa.SignPKCS1v15(rand.Reader, auth.PrivateKey.key, hashFunc, digest)
if err != nil {
return "", fmt.Errorf("An error occurred while signing the key: %s", err)
}
return base64.StdEncoding.EncodeToString(signed), nil
}
// Helper method to get the Hash function based on the algorithm
func getHashFunction(algorithm string) (hashFunc crypto.Hash) {
switch strings.ToLower(algorithm) {
case "rsa-sha1":
hashFunc = crypto.SHA1
case "rsa-sha224", "rsa-sha256":
hashFunc = crypto.SHA256
case "rsa-sha384", "rsa-sha512":
hashFunc = crypto.SHA512
default:
hashFunc = crypto.SHA256
}
return
}
func (cred *Credentials) Region() string {
parsedUrl, err := url.Parse(cred.SdcEndpoint.URL)
if err != nil {
// Bogus URL - no region.
return ""
}
if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.1") {
return "some-region"
}
host := parsedUrl.Host
firstDotIdx := strings.Index(host, ".")
if firstDotIdx >= 0 {
return host[:firstDotIdx]
}
return host
}
|