add debug routes for PEM and DER private keys
This commit is contained in:
parent
153851b41d
commit
075ade3dec
|
@ -0,0 +1 @@
|
|||
go test -mod=vendor -v ./...
|
|
@ -5,10 +5,12 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.coolaj86.com/coolaj86/go-mockid/mockid"
|
||||
"git.rootprojects.org/root/keypairs"
|
||||
|
@ -21,6 +23,8 @@ func main() {
|
|||
var port int
|
||||
var host string
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
portFlag := flag.Int("port", 0, "Port on which the HTTP server should run")
|
||||
urlFlag := flag.String("url", "", "Outward-facing address, such as https://example.com")
|
||||
prefixFlag := flag.String("jwkspath", "", "The path to the JWKs storage directory")
|
||||
|
|
|
@ -3,10 +3,13 @@ package mockid
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
mathrand "math/rand"
|
||||
|
||||
"git.rootprojects.org/root/keypairs"
|
||||
)
|
||||
|
@ -27,6 +30,75 @@ func MarshalJWKPrivateKey(privkey keypairs.PrivateKey) []byte {
|
|||
}
|
||||
}
|
||||
|
||||
// MarshalDERPrivateKey outputs the given private key as ASN.1 DER
|
||||
func MarshalDERPrivateKey(privkey keypairs.PrivateKey) ([]byte, error) {
|
||||
// thumbprint keys are alphabetically sorted and only include the necessary public parts
|
||||
switch k := privkey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return x509.MarshalPKCS1PrivateKey(k), nil
|
||||
case *ecdsa.PrivateKey:
|
||||
return x509.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)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func marshalDERPrivateKey(privkey keypairs.PrivateKey) (*pem.Block, error) {
|
||||
var typ string
|
||||
var bytes []byte
|
||||
var err error
|
||||
|
||||
switch k := privkey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
if 0 == mathrand.Intn(1) {
|
||||
typ = "Private Key"
|
||||
bytes, err = x509.MarshalPKCS8PrivateKey(k)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
typ = "RSA Private Key"
|
||||
bytes = x509.MarshalPKCS1PrivateKey(k)
|
||||
}
|
||||
return &pem.Block{
|
||||
Type: typ,
|
||||
Bytes: bytes,
|
||||
}, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
if 0 == mathrand.Intn(1) {
|
||||
typ = "Private Key"
|
||||
bytes, err = x509.MarshalPKCS8PrivateKey(k)
|
||||
} else {
|
||||
typ = "EC Private Key"
|
||||
bytes, err = x509.MarshalECPrivateKey(k)
|
||||
}
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return &pem.Block{
|
||||
Type: typ,
|
||||
Bytes: bytes,
|
||||
}, nil
|
||||
default:
|
||||
// this is unreachable because we know the types that we pass in
|
||||
log.Printf("keytype: %t, %+v\n", privkey, privkey)
|
||||
panic(keypairs.ErrInvalidPublicKey)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalPEMPrivateKey outputs the given private key as ASN.1 PEM
|
||||
func MarshalPEMPrivateKey(privkey keypairs.PrivateKey) ([]byte, error) {
|
||||
block, err := marshalDERPrivateKey(privkey)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return pem.EncodeToMemory(block), nil
|
||||
}
|
||||
|
||||
// MarshalECPrivateKey will output the given private key as JWK
|
||||
func MarshalECPrivateKey(k *ecdsa.PrivateKey) []byte {
|
||||
crv := k.Curve.Params().Name
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -21,6 +22,9 @@ import (
|
|||
//jwt "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// TestMain will overwrite this
|
||||
var rndsrc io.Reader = rand.Reader
|
||||
|
||||
type PublicJWK struct {
|
||||
Crv string `json:"crv"`
|
||||
KeyID string `json:"kid,omitempty"`
|
||||
|
@ -145,7 +149,7 @@ func JOSESign(privkey keypairs.PrivateKey, hash []byte) []byte {
|
|||
case *rsa.PrivateKey:
|
||||
panic("TODO: implement rsa sign")
|
||||
case *ecdsa.PrivateKey:
|
||||
r, s, _ := ecdsa.Sign(rand.Reader, k, hash[:])
|
||||
r, s, _ := ecdsa.Sign(rndsrc, k, hash[:])
|
||||
rb := r.Bytes()
|
||||
fmt.Println("debug:")
|
||||
fmt.Println(r, s)
|
||||
|
|
|
@ -3,22 +3,41 @@ package mockid
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
mathrand "math/rand"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.rootprojects.org/root/keypairs"
|
||||
//keypairs "github.com/big-squid/go-keypairs"
|
||||
//"github.com/big-squid/go-keypairs/keyfetch/uncached"
|
||||
)
|
||||
|
||||
var srv *httptest.Server
|
||||
|
||||
type TestReader struct{}
|
||||
|
||||
func (TestReader) Read(p []byte) (n int, err error) {
|
||||
return mathrand.Read(p)
|
||||
}
|
||||
|
||||
var testrnd = TestReader{}
|
||||
|
||||
func init() {
|
||||
rndsrc = testrnd
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
mathrand.Seed(0) // Predictable results
|
||||
|
||||
os.Setenv("SALT", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
jwksPrefix := "public-jwks"
|
||||
err := os.MkdirAll(jwksPrefix, 0755)
|
||||
|
@ -27,7 +46,7 @@ func TestMain(m *testing.M) {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
privkey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
privkey, _ := ecdsa.GenerateKey(elliptic.P256(), rndsrc)
|
||||
mux := Route(jwksPrefix, privkey)
|
||||
|
||||
srv = httptest.NewServer(mux)
|
||||
|
@ -38,7 +57,7 @@ func TestMain(m *testing.M) {
|
|||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestTest(t *testing.T) {
|
||||
func TestGenerateJWK(t *testing.T) {
|
||||
client := srv.Client()
|
||||
urlstr, _ := url.Parse(srv.URL + "/private.jwk.json")
|
||||
//fmt.Println("URL:", srv.URL, urlstr)
|
||||
|
@ -49,12 +68,14 @@ func TestTest(t *testing.T) {
|
|||
if nil != err {
|
||||
//t.Fatal(err)
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if nil != err {
|
||||
//t.Fatal(err)
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
jwk := map[string]string{}
|
||||
|
@ -62,10 +83,70 @@ func TestTest(t *testing.T) {
|
|||
if nil != err {
|
||||
//t.Fatal(err)
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if "" == jwk["d"] {
|
||||
t.Fatal("Missing key 'd' from supposed private key")
|
||||
}
|
||||
|
||||
key, err := keypairs.ParsePrivateKey(data)
|
||||
if nil != err {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
// no-op
|
||||
log.Println("is RSA")
|
||||
case *ecdsa.PrivateKey:
|
||||
// no-op
|
||||
log.Println("is EC")
|
||||
default:
|
||||
t.Fatal(errors.New("impossible key type"))
|
||||
}
|
||||
|
||||
//fmt.Printf("%#v\n", jwk)
|
||||
}
|
||||
|
||||
func TestGeneratePEM(t *testing.T) {
|
||||
client := srv.Client()
|
||||
urlstr, _ := url.Parse(srv.URL + "/priv.pem")
|
||||
//fmt.Println("URL:", srv.URL, urlstr)
|
||||
res, err := client.Do(&http.Request{
|
||||
Method: "POST",
|
||||
URL: urlstr,
|
||||
})
|
||||
if nil != err {
|
||||
//t.Fatal(err)
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if nil != err {
|
||||
//t.Fatal(err)
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := ParsePEMPrivateKey(data)
|
||||
if nil != err {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
// no-op
|
||||
log.Println("is RSA")
|
||||
case *ecdsa.PrivateKey:
|
||||
// no-op
|
||||
log.Println("is EC")
|
||||
default:
|
||||
t.Fatal(errors.New("impossible key type"))
|
||||
}
|
||||
|
||||
//fmt.Printf("%#v\n", key)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
package mockid
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.rootprojects.org/root/keypairs"
|
||||
)
|
||||
|
||||
func ParsePEMPrivateKey(block []byte) (keypairs.PrivateKey, error) {
|
||||
// TODO do not parse DER or JWK
|
||||
return keypairs.ParsePrivateKey(block)
|
||||
}
|
||||
|
||||
func parseExp(exp string) (int, error) {
|
||||
if "" == exp {
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
package mockid
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
mathrand "math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -180,17 +184,100 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
|
|||
fmt.Fprintf(w, token)
|
||||
})
|
||||
|
||||
getKty := func(r *http.Request) (string, error) {
|
||||
tok := make(map[string]interface{})
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&tok)
|
||||
if nil != err && io.EOF != err {
|
||||
log.Printf("json decode error: %s", err)
|
||||
return "", errors.New("Bad Request: invalid json body")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
kty, _ := tok["kty"].(string)
|
||||
if "" == kty {
|
||||
if 0 == mathrand.Intn(2) {
|
||||
kty = "RSA"
|
||||
} else {
|
||||
kty = "EC"
|
||||
}
|
||||
}
|
||||
return kty, nil
|
||||
}
|
||||
|
||||
http.HandleFunc("/private.jwk.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s %s", r.Method, r.URL.Path)
|
||||
if "POST" != r.Method {
|
||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
kty, err := getKty(r)
|
||||
if nil != err {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var privkey keypairs.PrivateKey
|
||||
if "RSA" == kty {
|
||||
keylen := 2048
|
||||
privkey, _ = rsa.GenerateKey(rndsrc, keylen)
|
||||
} else {
|
||||
privkey, _ = ecdsa.GenerateKey(elliptic.P256(), rndsrc)
|
||||
}
|
||||
|
||||
jwk := MarshalJWKPrivateKey(privkey)
|
||||
w.Write(jwk)
|
||||
})
|
||||
|
||||
http.HandleFunc("/priv.der", func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s %s\n", r.Method, r.URL.Path)
|
||||
if "POST" != r.Method {
|
||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
keylen := 2048
|
||||
privkey, _ := rsa.GenerateKey(rand.Reader, keylen)
|
||||
jwk := string(MarshalJWKPrivateKey(privkey))
|
||||
fmt.Fprintf(w, jwk)
|
||||
kty, err := getKty(r)
|
||||
if nil != err {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var privkey keypairs.PrivateKey
|
||||
if "RSA" == kty {
|
||||
keylen := 2048
|
||||
privkey, _ = rsa.GenerateKey(rndsrc, keylen)
|
||||
} else {
|
||||
privkey, _ = ecdsa.GenerateKey(elliptic.P256(), rndsrc)
|
||||
}
|
||||
|
||||
der, _ := MarshalDERPrivateKey(privkey)
|
||||
w.Write(der)
|
||||
})
|
||||
|
||||
http.HandleFunc("/priv.pem", func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s %s\n", r.Method, r.URL.Path)
|
||||
if "POST" != r.Method {
|
||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
kty, err := getKty(r)
|
||||
if nil != err {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var privkey keypairs.PrivateKey
|
||||
if "RSA" == kty {
|
||||
keylen := 2048
|
||||
privkey, _ = rsa.GenerateKey(rndsrc, keylen)
|
||||
} else {
|
||||
privkey, _ = ecdsa.GenerateKey(elliptic.P256(), rndsrc)
|
||||
}
|
||||
|
||||
privpem, _ := MarshalPEMPrivateKey(privkey)
|
||||
w.Write(privpem)
|
||||
})
|
||||
|
||||
http.HandleFunc("/inspect_token", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
Loading…
Reference in New Issue