switch to keypairs
This commit is contained in:
		
							parent
							
								
									66e0639f48
								
							
						
					
					
						commit
						3ab579ad24
					
				@ -1,4 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  "kty": "EC",
 | 
				
			||||||
  "crv": "P-256",
 | 
					  "crv": "P-256",
 | 
				
			||||||
  "d": "GYAwlBHc2mPsj1lp315HbYOmKNJ7esmO3JAkZVn9nJs",
 | 
					  "d": "GYAwlBHc2mPsj1lp315HbYOmKNJ7esmO3JAkZVn9nJs",
 | 
				
			||||||
  "x": "ToL2HppsTESXQKvp7ED6NMgV4YnwbMeONexNry3KDNQ",
 | 
					  "x": "ToL2HppsTESXQKvp7ED6NMgV4YnwbMeONexNry3KDNQ",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										28
									
								
								mockid.go
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								mockid.go
									
									
									
									
									
								
							@ -1,7 +1,6 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
@ -12,6 +11,7 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.coolaj86.com/coolaj86/go-mockid/mockid"
 | 
						"git.coolaj86.com/coolaj86/go-mockid/mockid"
 | 
				
			||||||
 | 
						"git.rootprojects.org/root/keypairs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_ "github.com/joho/godotenv/autoload"
 | 
						_ "github.com/joho/godotenv/autoload"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -44,24 +44,13 @@ func main() {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	jwkm := map[string]string{}
 | 
						privkey, err := keypairs.ParseJWKPrivateKey(jwkb)
 | 
				
			||||||
	err = json.Unmarshal(jwkb, &jwkm)
 | 
					 | 
				
			||||||
	if nil != err {
 | 
						if nil != err {
 | 
				
			||||||
		// TODO delete the bad file?
 | 
							// TODO delete the bad file?
 | 
				
			||||||
		panic(fmt.Errorf("unmarshal jwk %v: %w", string(jwkb), err))
 | 
							panic(fmt.Errorf("unmarshal jwk %v: %w", string(jwkb), err))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	jwk := &mockid.PrivateJWK{
 | 
					 | 
				
			||||||
		PublicJWK: mockid.PublicJWK{
 | 
					 | 
				
			||||||
			Crv: jwkm["crv"],
 | 
					 | 
				
			||||||
			X:   jwkm["x"],
 | 
					 | 
				
			||||||
			Y:   jwkm["y"],
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		D: jwkm["d"],
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	priv := mockid.ParseKey(jwk)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if nil != urlFlag && "" != *urlFlag {
 | 
						if nil != urlFlag && "" != *urlFlag {
 | 
				
			||||||
		host = *urlFlag
 | 
							host = *urlFlag
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@ -80,7 +69,7 @@ func main() {
 | 
				
			|||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mockid.Route(jwksPrefix, priv, jwk)
 | 
						mockid.Route(jwksPrefix, privkey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fs := http.FileServer(http.Dir("./public"))
 | 
						fs := http.FileServer(http.Dir("./public"))
 | 
				
			||||||
	http.Handle("/", fs)
 | 
						http.Handle("/", fs)
 | 
				
			||||||
@ -97,11 +86,12 @@ func main() {
 | 
				
			|||||||
		done <- true
 | 
							done <- true
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b, _ := json.Marshal(jwk)
 | 
						// TODO privB := keypairs.MarshalJWKPrivateKey(privkey)
 | 
				
			||||||
	fmt.Printf("Private Key:\n\t%s\n", string(b))
 | 
						privB := mockid.MarshalJWKPrivateKey(privkey)
 | 
				
			||||||
	b, _ = json.Marshal(jwk.PublicJWK)
 | 
						fmt.Printf("Private Key:\n\t%s\n", string(privB))
 | 
				
			||||||
	fmt.Printf("Public Key:\n\t%s\n", string(b))
 | 
						pubB := keypairs.MarshalJWKPublicKey(keypairs.NewPublicKey(privkey.Public()))
 | 
				
			||||||
	protected, payload, token := mockid.GenToken(host, priv, url.Values{})
 | 
						fmt.Printf("Public Key:\n\t%s\n", string(pubB))
 | 
				
			||||||
 | 
						protected, payload, token := mockid.GenToken(host, privkey, url.Values{})
 | 
				
			||||||
	fmt.Printf("Protected (Header):\n\t%s\n", protected)
 | 
						fmt.Printf("Protected (Header):\n\t%s\n", protected)
 | 
				
			||||||
	fmt.Printf("Payload (Claims):\n\t%s\n", payload)
 | 
						fmt.Printf("Payload (Claims):\n\t%s\n", payload)
 | 
				
			||||||
	fmt.Printf("Access Token:\n\t%s\n", token)
 | 
						fmt.Printf("Access Token:\n\t%s\n", token)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										183
									
								
								mockid/mockid.go
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								mockid/mockid.go
									
									
									
									
									
								
							@ -2,8 +2,8 @@ package mockid
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/ecdsa"
 | 
						"crypto/ecdsa"
 | 
				
			||||||
	"crypto/elliptic"
 | 
					 | 
				
			||||||
	"crypto/rand"
 | 
						"crypto/rand"
 | 
				
			||||||
 | 
						"crypto/rsa"
 | 
				
			||||||
	"crypto/sha256"
 | 
						"crypto/sha256"
 | 
				
			||||||
	"crypto/sha512"
 | 
						"crypto/sha512"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
@ -21,13 +21,9 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"git.rootprojects.org/root/keypairs"
 | 
						"git.rootprojects.org/root/keypairs"
 | 
				
			||||||
	"git.rootprojects.org/root/keypairs/keyfetch"
 | 
						"git.rootprojects.org/root/keypairs/keyfetch"
 | 
				
			||||||
 | 
						//jwt "github.com/dgrijalva/jwt-go"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PrivateJWK struct {
 | 
					 | 
				
			||||||
	PublicJWK
 | 
					 | 
				
			||||||
	D string `json:"d"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type PublicJWK struct {
 | 
					type PublicJWK struct {
 | 
				
			||||||
	Crv   string `json:"crv"`
 | 
						Crv   string `json:"crv"`
 | 
				
			||||||
	KeyID string `json:"kid,omitempty"`
 | 
						KeyID string `json:"kid,omitempty"`
 | 
				
			||||||
@ -36,15 +32,34 @@ type PublicJWK struct {
 | 
				
			|||||||
	Y     string `json:"y"`
 | 
						Y     string `json:"y"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InspectableToken struct {
 | 
				
			||||||
 | 
						Public    keypairs.PublicKey     `json:"public"`
 | 
				
			||||||
 | 
						Protected map[string]interface{} `json:"protected"`
 | 
				
			||||||
 | 
						Payload   map[string]interface{} `json:"payload"`
 | 
				
			||||||
 | 
						Signature string                 `json:"signature"`
 | 
				
			||||||
 | 
						Verified  bool                   `json:"verified"`
 | 
				
			||||||
 | 
						Errors    []string               `json:"errors"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *InspectableToken) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						pub := keypairs.MarshalJWKPublicKey(t.Public)
 | 
				
			||||||
 | 
						header, _ := json.Marshal(t.Protected)
 | 
				
			||||||
 | 
						payload, _ := json.Marshal(t.Payload)
 | 
				
			||||||
 | 
						errs, _ := json.Marshal(t.Errors)
 | 
				
			||||||
 | 
						return []byte(fmt.Sprintf(
 | 
				
			||||||
 | 
							`{"public":%s,"protected":%s,"payload":%s,"signature":%q,"verified":%t,"errors":%s}`,
 | 
				
			||||||
 | 
							pub, header, payload, t.Signature, t.Verified, errs,
 | 
				
			||||||
 | 
						)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var nonces map[string]int64
 | 
					var nonces map[string]int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	nonces = make(map[string]int64)
 | 
						nonces = make(map[string]int64)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
					func Route(jwksPrefix string, privkey keypairs.PrivateKey) {
 | 
				
			||||||
	pub := &priv.PublicKey
 | 
						pubkey := keypairs.NewPublicKey(privkey.Public())
 | 
				
			||||||
	thumbprint := thumbprintKey(pub)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	http.HandleFunc("/api/new-nonce", func(w http.ResponseWriter, r *http.Request) {
 | 
						http.HandleFunc("/api/new-nonce", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		baseURL := getBaseURL(r)
 | 
							baseURL := getBaseURL(r)
 | 
				
			||||||
@ -110,7 +125,7 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	http.HandleFunc("/access_token", func(w http.ResponseWriter, r *http.Request) {
 | 
						http.HandleFunc("/access_token", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		log.Printf("%s %s\n", r.Method, r.URL.Path)
 | 
							log.Printf("%s %s\n", r.Method, r.URL.Path)
 | 
				
			||||||
		_, _, token := GenToken(getBaseURL(r), priv, r.URL.Query())
 | 
							_, _, token := GenToken(getBaseURL(r), privkey, r.URL.Query())
 | 
				
			||||||
		fmt.Fprintf(w, token)
 | 
							fmt.Fprintf(w, token)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -135,7 +150,7 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		parts := strings.Split(token, ".")
 | 
							parts := strings.Split(token, ".")
 | 
				
			||||||
		if 3 != len(parts) {
 | 
							if 3 != len(parts) {
 | 
				
			||||||
			http.Error(w, "Bad Format: token should be in the format of <protected-header>.<body>.<signature>", http.StatusBadRequest)
 | 
								http.Error(w, "Bad Format: token should be in the format of <protected-header>.<payload>.<signature>", http.StatusBadRequest)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		protected64 := parts[0]
 | 
							protected64 := parts[0]
 | 
				
			||||||
@ -149,7 +164,7 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		dataB, err := base64.RawURLEncoding.DecodeString(data64)
 | 
							dataB, err := base64.RawURLEncoding.DecodeString(data64)
 | 
				
			||||||
		if nil != err {
 | 
							if nil != err {
 | 
				
			||||||
			http.Error(w, "Bad Format: token's body should be URL-safe base64 encoded", http.StatusBadRequest)
 | 
								http.Error(w, "Bad Format: token's payload should be URL-safe base64 encoded", http.StatusBadRequest)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// TODO verify signature
 | 
							// TODO verify signature
 | 
				
			||||||
@ -177,12 +192,12 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
				
			|||||||
		data := map[string]interface{}{}
 | 
							data := map[string]interface{}{}
 | 
				
			||||||
		err = json.Unmarshal(dataB, &data)
 | 
							err = json.Unmarshal(dataB, &data)
 | 
				
			||||||
		if nil != err {
 | 
							if nil != err {
 | 
				
			||||||
			http.Error(w, "Bad Format: token's body should be URL-safe base64-encoded JSON", http.StatusBadRequest)
 | 
								http.Error(w, "Bad Format: token's payload should be URL-safe base64-encoded JSON", http.StatusBadRequest)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		iss, issOK := data["iss"].(string)
 | 
							iss, issOK := data["iss"].(string)
 | 
				
			||||||
		if !jwkOK && !issOK {
 | 
							if !jwkOK && !issOK {
 | 
				
			||||||
			errors = append(errors, "body.iss must exist to complement header.kid")
 | 
								errors = append(errors, "payload.iss must exist to complement header.kid")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pub, err := keyfetch.OIDCJWK(kid, iss)
 | 
							pub, err := keyfetch.OIDCJWK(kid, iss)
 | 
				
			||||||
@ -193,23 +208,16 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
				
			|||||||
		fmt.Println("fetched pub key:")
 | 
							fmt.Println("fetched pub key:")
 | 
				
			||||||
		fmt.Println(pub)
 | 
							fmt.Println(pub)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		inspected := struct {
 | 
							inspected := &InspectableToken{
 | 
				
			||||||
			Public    keypairs.PublicKey     `json:"public"`
 | 
					 | 
				
			||||||
			Protected map[string]interface{} `json:"protected"`
 | 
					 | 
				
			||||||
			Body      map[string]interface{} `json:"body"`
 | 
					 | 
				
			||||||
			Signature string                 `json:"signature"`
 | 
					 | 
				
			||||||
			Verified  bool                   `json:"verified"`
 | 
					 | 
				
			||||||
			Errors    []string               `json:"errors"`
 | 
					 | 
				
			||||||
		}{
 | 
					 | 
				
			||||||
			Public:    pub,
 | 
								Public:    pub,
 | 
				
			||||||
			Protected: protected,
 | 
								Protected: protected,
 | 
				
			||||||
			Body:      data,
 | 
								Payload:   data,
 | 
				
			||||||
			Signature: signature64,
 | 
								Signature: signature64,
 | 
				
			||||||
			Verified:  false,
 | 
								Verified:  false,
 | 
				
			||||||
			Errors:    errors,
 | 
								Errors:    errors,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tokenB, err := json.Marshal(inspected)
 | 
							tokenB, err := json.MarshalIndent(inspected, "", "    ")
 | 
				
			||||||
		if nil != err {
 | 
							if nil != err {
 | 
				
			||||||
			fmt.Println("couldn't serialize inpsected token:")
 | 
								fmt.Println("couldn't serialize inpsected token:")
 | 
				
			||||||
			fmt.Println(err)
 | 
								fmt.Println(err)
 | 
				
			||||||
@ -239,13 +247,19 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
				
			|||||||
			prefix = prefixes[0]
 | 
								prefix = prefixes[0]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_, _, token := GenToken(getBaseURL(r), priv, r.URL.Query())
 | 
							_, _, token := GenToken(getBaseURL(r), privkey, r.URL.Query())
 | 
				
			||||||
		fmt.Fprintf(w, "%s: %s%s", header, prefix, token)
 | 
							fmt.Fprintf(w, "%s: %s%s", header, prefix, token)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	http.HandleFunc("/key.jwk.json", func(w http.ResponseWriter, r *http.Request) {
 | 
						http.HandleFunc("/key.jwk.json", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		log.Printf("%s %s", r.Method, r.URL.Path)
 | 
							log.Printf("%s %s", r.Method, r.URL.Path)
 | 
				
			||||||
		fmt.Fprintf(w, `{ "kty": "EC" , "crv": %q , "d": %q , "x": %q , "y": %q , "ext": true , "key_ops": ["sign"] }`, jwk.Crv, jwk.D, jwk.X, jwk.Y)
 | 
							jwk := string(MarshalJWKPrivateKey(privkey))
 | 
				
			||||||
 | 
							jwk = strings.Replace(jwk, `{"`, `{ "`, 1)
 | 
				
			||||||
 | 
							jwk = strings.Replace(jwk, `",`, `", `, -1)
 | 
				
			||||||
 | 
							jwk = jwk[0 : len(jwk)-1]
 | 
				
			||||||
 | 
							jwk = jwk + `, "ext": true , "key_ops": ["sign"] }`
 | 
				
			||||||
 | 
							// `{ "kty": "EC" , "crv": %q , "d": %q , "x": %q , "y": %q }`, jwk.Crv, jwk.D, jwk.X, jwk.Y
 | 
				
			||||||
 | 
							fmt.Fprintf(w, jwk)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	http.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
 | 
						http.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
@ -262,10 +276,14 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) {
 | 
				
			|||||||
		b, err := ioutil.ReadFile(filepath.Join(jwksPrefix, strings.ToLower(kid)+".jwk.json"))
 | 
							b, err := ioutil.ReadFile(filepath.Join(jwksPrefix, strings.ToLower(kid)+".jwk.json"))
 | 
				
			||||||
		if nil != err {
 | 
							if nil != err {
 | 
				
			||||||
			//http.Error(w, "Not Found", http.StatusNotFound)
 | 
								//http.Error(w, "Not Found", http.StatusNotFound)
 | 
				
			||||||
			jwkstr := fmt.Sprintf(
 | 
								exp := strconv.FormatInt(time.Now().Add(15*time.Minute).Unix(), 10)
 | 
				
			||||||
				`{ "keys": [ { "kty": "EC" , "crv": %q , "x": %q , "y": %q , "kid": %q , "ext": true , "key_ops": ["verify"] , "exp": %s } ] }`,
 | 
								jwk := string(keypairs.MarshalJWKPublicKey(pubkey))
 | 
				
			||||||
				jwk.Crv, jwk.X, jwk.Y, thumbprint, strconv.FormatInt(time.Now().Add(15*time.Minute).Unix(), 10),
 | 
								jwk = strings.Replace(jwk, `{"`, `{ "`, 1)
 | 
				
			||||||
			)
 | 
								jwk = strings.Replace(jwk, `",`, `" ,`, -1)
 | 
				
			||||||
 | 
								jwk = jwk[0 : len(jwk)-1]
 | 
				
			||||||
 | 
								jwk = jwk + fmt.Sprintf(`, "ext": true , "key_ops": ["verify"], "exp": %s }`, exp)
 | 
				
			||||||
 | 
								// { "kty": "EC" , "crv": %q , "x": %q , "y": %q , "kid": %q , "ext": true , "key_ops": ["verify"] , "exp": %s }
 | 
				
			||||||
 | 
								jwkstr := fmt.Sprintf(`{ "keys": [ %s ] }`, jwk)
 | 
				
			||||||
			fmt.Println(jwkstr)
 | 
								fmt.Println(jwkstr)
 | 
				
			||||||
			fmt.Fprintf(w, jwkstr)
 | 
								fmt.Fprintf(w, jwkstr)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@ -462,9 +480,15 @@ func postRSA(jwksPrefix string, tok map[string]interface{}, w http.ResponseWrite
 | 
				
			|||||||
	)))
 | 
						)))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GenToken(host string, priv *ecdsa.PrivateKey, query url.Values) (string, string, string) {
 | 
					func GenToken(host string, privkey keypairs.PrivateKey, query url.Values) (string, string, string) {
 | 
				
			||||||
	thumbprint := thumbprintKey(&priv.PublicKey)
 | 
						thumbprint := keypairs.ThumbprintPublicKey(keypairs.NewPublicKey(privkey.Public()))
 | 
				
			||||||
	protected := fmt.Sprintf(`{"typ":"JWT","alg":"ES256","kid":"%s"}`, thumbprint)
 | 
						// TODO keypairs.Alg(key)
 | 
				
			||||||
 | 
						alg := "ES256"
 | 
				
			||||||
 | 
						switch privkey.(type) {
 | 
				
			||||||
 | 
						case *rsa.PrivateKey:
 | 
				
			||||||
 | 
							alg = "RS256"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						protected := fmt.Sprintf(`{"typ":"JWT","alg":%q,"kid":"%s"}`, alg, thumbprint)
 | 
				
			||||||
	protected64 := base64.RawURLEncoding.EncodeToString([]byte(protected))
 | 
						protected64 := base64.RawURLEncoding.EncodeToString([]byte(protected))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	exp, err := parseExp(query.Get("exp"))
 | 
						exp, err := parseExp(query.Get("exp"))
 | 
				
			||||||
@ -481,7 +505,22 @@ func GenToken(host string, priv *ecdsa.PrivateKey, query url.Values) (string, st
 | 
				
			|||||||
	payload64 := base64.RawURLEncoding.EncodeToString([]byte(payload))
 | 
						payload64 := base64.RawURLEncoding.EncodeToString([]byte(payload))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hash := sha256.Sum256([]byte(fmt.Sprintf(`%s.%s`, protected64, payload64)))
 | 
						hash := sha256.Sum256([]byte(fmt.Sprintf(`%s.%s`, protected64, payload64)))
 | 
				
			||||||
	r, s, _ := ecdsa.Sign(rand.Reader, priv, hash[:])
 | 
						sig := JOSESign(privkey, hash[:])
 | 
				
			||||||
 | 
						sig64 := base64.RawURLEncoding.EncodeToString(sig)
 | 
				
			||||||
 | 
						token := fmt.Sprintf("%s.%s.%s\n", protected64, payload64, sig64)
 | 
				
			||||||
 | 
						return protected, payload, token
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: move to keypairs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func JOSESign(privkey keypairs.PrivateKey, hash []byte) []byte {
 | 
				
			||||||
 | 
						var sig []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch k := privkey.(type) {
 | 
				
			||||||
 | 
						case *rsa.PrivateKey:
 | 
				
			||||||
 | 
							panic("TODO: implement rsa sign")
 | 
				
			||||||
 | 
						case *ecdsa.PrivateKey:
 | 
				
			||||||
 | 
							r, s, _ := ecdsa.Sign(rand.Reader, k, hash[:])
 | 
				
			||||||
		rb := r.Bytes()
 | 
							rb := r.Bytes()
 | 
				
			||||||
		for len(rb) < 32 {
 | 
							for len(rb) < 32 {
 | 
				
			||||||
			rb = append([]byte{0}, rb...)
 | 
								rb = append([]byte{0}, rb...)
 | 
				
			||||||
@ -490,38 +529,9 @@ func GenToken(host string, priv *ecdsa.PrivateKey, query url.Values) (string, st
 | 
				
			|||||||
		for len(rb) < 32 {
 | 
							for len(rb) < 32 {
 | 
				
			||||||
			sb = append([]byte{0}, sb...)
 | 
								sb = append([]byte{0}, sb...)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	sig64 := base64.RawURLEncoding.EncodeToString(append(rb, sb...))
 | 
							sig = append(rb, sb...)
 | 
				
			||||||
	token := fmt.Sprintf("%s.%s.%s\n", protected64, payload64, sig64)
 | 
					 | 
				
			||||||
	return protected, payload, token
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return sig
 | 
				
			||||||
func ParseKey(jwk *PrivateJWK) *ecdsa.PrivateKey {
 | 
					 | 
				
			||||||
	xb, _ := base64.RawURLEncoding.DecodeString(jwk.X)
 | 
					 | 
				
			||||||
	xi := &big.Int{}
 | 
					 | 
				
			||||||
	xi.SetBytes(xb)
 | 
					 | 
				
			||||||
	yb, _ := base64.RawURLEncoding.DecodeString(jwk.Y)
 | 
					 | 
				
			||||||
	yi := &big.Int{}
 | 
					 | 
				
			||||||
	yi.SetBytes(yb)
 | 
					 | 
				
			||||||
	pub := &ecdsa.PublicKey{
 | 
					 | 
				
			||||||
		Curve: elliptic.P256(),
 | 
					 | 
				
			||||||
		X:     xi,
 | 
					 | 
				
			||||||
		Y:     yi,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db, _ := base64.RawURLEncoding.DecodeString(jwk.D)
 | 
					 | 
				
			||||||
	di := &big.Int{}
 | 
					 | 
				
			||||||
	di.SetBytes(db)
 | 
					 | 
				
			||||||
	priv := &ecdsa.PrivateKey{
 | 
					 | 
				
			||||||
		PublicKey: *pub,
 | 
					 | 
				
			||||||
		D:         di,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return priv
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func thumbprintKey(pub *ecdsa.PublicKey) string {
 | 
					 | 
				
			||||||
	minpub := []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, "P-256", pub.X, pub.Y))
 | 
					 | 
				
			||||||
	sha := sha256.Sum256(minpub)
 | 
					 | 
				
			||||||
	return base64.RawURLEncoding.EncodeToString(sha[:])
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func issueNonce(w http.ResponseWriter, r *http.Request) {
 | 
					func issueNonce(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
@ -567,3 +577,46 @@ func getBaseURL(r *http.Request) string {
 | 
				
			|||||||
		r.Host,
 | 
							r.Host,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MarshalJWKPrivateKey outputs the given private key as JWK
 | 
				
			||||||
 | 
					func MarshalJWKPrivateKey(privkey keypairs.PrivateKey) []byte {
 | 
				
			||||||
 | 
						// thumbprint keys are alphabetically sorted and only include the necessary public parts
 | 
				
			||||||
 | 
						switch k := privkey.(type) {
 | 
				
			||||||
 | 
						case *rsa.PrivateKey:
 | 
				
			||||||
 | 
							return MarshalRSAPrivateKey(k)
 | 
				
			||||||
 | 
						case *ecdsa.PrivateKey:
 | 
				
			||||||
 | 
							return MarshalECPrivateKey(k)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// this is unreachable because we know the types that we pass in
 | 
				
			||||||
 | 
							log.Printf("keytype: %t, %+v\n", privkey, privkey)
 | 
				
			||||||
 | 
							panic(keypairs.ErrInvalidPublicKey)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MarshalECPrivateKey will output the given private key as JWK
 | 
				
			||||||
 | 
					func MarshalECPrivateKey(k *ecdsa.PrivateKey) []byte {
 | 
				
			||||||
 | 
						crv := k.Curve.Params().Name
 | 
				
			||||||
 | 
						d := base64.RawURLEncoding.EncodeToString(k.D.Bytes())
 | 
				
			||||||
 | 
						x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
 | 
				
			||||||
 | 
						y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes())
 | 
				
			||||||
 | 
						return []byte(fmt.Sprintf(
 | 
				
			||||||
 | 
							`{"crv":%q,"d":%q,"kty":"EC","x":%q,"y":%q}`,
 | 
				
			||||||
 | 
							crv, d, x, y,
 | 
				
			||||||
 | 
						))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MarshalRSAPrivateKey will output the given private key as JWK
 | 
				
			||||||
 | 
					func MarshalRSAPrivateKey(pk *rsa.PrivateKey) []byte {
 | 
				
			||||||
 | 
						e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pk.E)).Bytes())
 | 
				
			||||||
 | 
						n := base64.RawURLEncoding.EncodeToString(pk.N.Bytes())
 | 
				
			||||||
 | 
						d := base64.RawURLEncoding.EncodeToString(pk.D.Bytes())
 | 
				
			||||||
 | 
						p := base64.RawURLEncoding.EncodeToString(pk.Primes[0].Bytes())
 | 
				
			||||||
 | 
						q := base64.RawURLEncoding.EncodeToString(pk.Primes[1].Bytes())
 | 
				
			||||||
 | 
						dp := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Dp.Bytes())
 | 
				
			||||||
 | 
						dq := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Dq.Bytes())
 | 
				
			||||||
 | 
						qi := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Qinv.Bytes())
 | 
				
			||||||
 | 
						return []byte(fmt.Sprintf(
 | 
				
			||||||
 | 
							`{"d":%q,"dp":%q,"dq":%q,"e":%q,"kty":"RSA","n":%q,"p":%q,"q":%q,"qi":%q}`,
 | 
				
			||||||
 | 
							d, dp, dq, e, n, p, q, qi,
 | 
				
			||||||
 | 
						))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user