can now self-sign JWS and JWT
This commit is contained in:
parent
9b250c8cbb
commit
a1b4ad1202
|
@ -13,12 +13,16 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Object = map[string]interface{}
|
||||||
|
|
||||||
// options are the things that we may need to know about a request to fulfill it properly
|
// options are the things that we may need to know about a request to fulfill it properly
|
||||||
type options struct {
|
type options struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
KeyType string `json:"kty"`
|
KeyType string `json:"kty"`
|
||||||
Seed int64 `json:"-"`
|
Seed int64 `json:"-"`
|
||||||
SeedStr string `json:"seed"`
|
SeedStr string `json:"seed"`
|
||||||
|
Claims Object `json:"claims"`
|
||||||
|
Header Object `json:"header"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// this shananigans is only for testing and debug API stuff
|
// this shananigans is only for testing and debug API stuff
|
||||||
|
@ -55,6 +59,9 @@ func getOpts(r *http.Request) (*options, error) {
|
||||||
Key: key,
|
Key: key,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts.Claims, _ = tok["claims"].(Object)
|
||||||
|
opts.Header, _ = tok["header"].(Object)
|
||||||
|
|
||||||
var n int
|
var n int
|
||||||
if 0 != seed {
|
if 0 != seed {
|
||||||
n = opts.nextReader().(*mathrand.Rand).Intn(2)
|
n = opts.nextReader().(*mathrand.Rand).Intn(2)
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.coolaj86.com/coolaj86/go-mockid/xkeypairs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SignJWS will create an uncompressed JWT with the given payload
|
||||||
|
func SignJWS(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sign(w, r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignJWT will create an compressed JWS (JWT) with the given payload
|
||||||
|
func SignJWT(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sign(w, r, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sign(w http.ResponseWriter, r *http.Request, jwt bool) {
|
||||||
|
if "POST" != r.Method {
|
||||||
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
opts, err := getOpts(r)
|
||||||
|
if nil != err {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
privkey, err := getPrivKey(opts)
|
||||||
|
if nil != err {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
header := opts.Header
|
||||||
|
if 0 != opts.Seed {
|
||||||
|
header["_seed"] = opts.Seed
|
||||||
|
}
|
||||||
|
|
||||||
|
jws, err := xkeypairs.SignClaims(privkey, header, opts.Claims)
|
||||||
|
if nil != err {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
if jwt {
|
||||||
|
s := xkeypairs.JWSToJWT(jws)
|
||||||
|
w.Write(append([]byte(s), '\n'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b, _ = json.Marshal(jws)
|
||||||
|
w.Write(append(b, '\n'))
|
||||||
|
}
|
|
@ -115,6 +115,29 @@ func GenToken(host string, privkey keypairs.PrivateKey, query url.Values) (strin
|
||||||
return protected, payload, token
|
return protected, payload, token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(rndsrc, k, hash[:])
|
||||||
|
rb := r.Bytes()
|
||||||
|
fmt.Println("debug:")
|
||||||
|
fmt.Println(r, s)
|
||||||
|
for len(rb) < 32 {
|
||||||
|
rb = append([]byte{0}, rb...)
|
||||||
|
}
|
||||||
|
sb := s.Bytes()
|
||||||
|
for len(rb) < 32 {
|
||||||
|
sb = append([]byte{0}, sb...)
|
||||||
|
}
|
||||||
|
sig = append(rb, sb...)
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: move to keypairs
|
// TODO: move to keypairs
|
||||||
|
|
||||||
func JOSEVerify(pubkey keypairs.PublicKey, hash []byte, sig []byte) bool {
|
func JOSEVerify(pubkey keypairs.PublicKey, hash []byte, sig []byte) bool {
|
||||||
|
@ -143,29 +166,6 @@ func JOSEVerify(pubkey keypairs.PublicKey, hash []byte, sig []byte) bool {
|
||||||
return verified
|
return verified
|
||||||
}
|
}
|
||||||
|
|
||||||
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(rndsrc, k, hash[:])
|
|
||||||
rb := r.Bytes()
|
|
||||||
fmt.Println("debug:")
|
|
||||||
fmt.Println(r, s)
|
|
||||||
for len(rb) < 32 {
|
|
||||||
rb = append([]byte{0}, rb...)
|
|
||||||
}
|
|
||||||
sb := s.Bytes()
|
|
||||||
for len(rb) < 32 {
|
|
||||||
sb = append([]byte{0}, sb...)
|
|
||||||
}
|
|
||||||
sig = append(rb, sb...)
|
|
||||||
}
|
|
||||||
return sig
|
|
||||||
}
|
|
||||||
|
|
||||||
func issueNonce(w http.ResponseWriter, r *http.Request) {
|
func issueNonce(w http.ResponseWriter, r *http.Request) {
|
||||||
b := make([]byte, 16)
|
b := make([]byte, 16)
|
||||||
_, _ = rand.Read(b)
|
_, _ = rand.Read(b)
|
||||||
|
|
|
@ -61,6 +61,40 @@ func TestMain(m *testing.M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//func TestSelfSignWithoutExp(t *testing.T)
|
||||||
|
//func TestSelfSignWithJTIWithoutExp(t *testing.T)
|
||||||
|
|
||||||
|
func TestSelfSign(t *testing.T) {
|
||||||
|
client := srv.Client()
|
||||||
|
//urlstr, _ := url.Parse(srv.URL + "/jose.jws.json")
|
||||||
|
urlstr, _ := url.Parse(srv.URL + "/jose.jws.jwt")
|
||||||
|
|
||||||
|
//fmt.Println("URL:", srv.URL, urlstr)
|
||||||
|
tokenRequest := []byte(`{"seed":"test","header":{"_jwk":true},"claims":{"sub":"bananas","exp":"10m"}}`)
|
||||||
|
res, err := client.Do(&http.Request{
|
||||||
|
Method: "POST",
|
||||||
|
URL: urlstr,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader(tokenRequest)),
|
||||||
|
})
|
||||||
|
if nil != err {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(res.Body)
|
||||||
|
if nil != err {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("TODO: verify, and verify non-self-signed")
|
||||||
|
log.Printf(string(data))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGenerateJWK(t *testing.T) {
|
func TestGenerateJWK(t *testing.T) {
|
||||||
client := srv.Client()
|
client := srv.Client()
|
||||||
urlstr, _ := url.Parse(srv.URL + "/private.jwk.json")
|
urlstr, _ := url.Parse(srv.URL + "/private.jwk.json")
|
||||||
|
@ -74,6 +108,10 @@ func TestGenerateJWK(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
data, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
@ -128,6 +166,11 @@ func TestGenWithSeed(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dataA, err := ioutil.ReadAll(res.Body)
|
dataA, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
//t.Fatal(err)
|
//t.Fatal(err)
|
||||||
|
@ -150,6 +193,11 @@ func TestGenWithSeed(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dataB, err := ioutil.ReadAll(res.Body)
|
dataB, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
//t.Fatal(err)
|
//t.Fatal(err)
|
||||||
|
@ -180,6 +228,11 @@ func TestGenWithRand(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dataA, err := ioutil.ReadAll(res.Body)
|
dataA, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
//t.Fatal(err)
|
//t.Fatal(err)
|
||||||
|
@ -200,6 +253,11 @@ func TestGenWithRand(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dataB, err := ioutil.ReadAll(res.Body)
|
dataB, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
//t.Fatal(err)
|
//t.Fatal(err)
|
||||||
|
@ -226,6 +284,10 @@ func TestGeneratePEM(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
data, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
@ -266,6 +328,10 @@ func TestPublicJWKWithKey(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
data, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
@ -319,6 +385,10 @@ func TestPublicPEMWithSeed(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if 200 != res.StatusCode {
|
||||||
|
t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
data, err := ioutil.ReadAll(res.Body)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
|
|
@ -181,6 +181,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
|
||||||
fmt.Fprintf(w, token)
|
fmt.Fprintf(w, token)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO add /debug prefix
|
||||||
http.HandleFunc("/private.jwk.json", api.GeneratePrivateJWK)
|
http.HandleFunc("/private.jwk.json", api.GeneratePrivateJWK)
|
||||||
http.HandleFunc("/priv.der", api.GeneratePrivateDER)
|
http.HandleFunc("/priv.der", api.GeneratePrivateDER)
|
||||||
http.HandleFunc("/priv.pem", api.GeneratePrivatePEM)
|
http.HandleFunc("/priv.pem", api.GeneratePrivatePEM)
|
||||||
|
@ -189,6 +190,9 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
|
||||||
http.HandleFunc("/pub.der", api.GeneratePublicDER)
|
http.HandleFunc("/pub.der", api.GeneratePublicDER)
|
||||||
http.HandleFunc("/pub.pem", api.GeneratePublicPEM)
|
http.HandleFunc("/pub.pem", api.GeneratePublicPEM)
|
||||||
|
|
||||||
|
http.HandleFunc("/jose.jws.json", api.SignJWS)
|
||||||
|
http.HandleFunc("/jose.jws.jwt", api.SignJWT)
|
||||||
|
|
||||||
http.HandleFunc("/inspect_token", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/inspect_token", func(w http.ResponseWriter, r *http.Request) {
|
||||||
token := r.Header.Get("Authorization")
|
token := r.Header.Get("Authorization")
|
||||||
log.Printf("%s %s %s\n", r.Method, r.URL.Path, token)
|
log.Printf("%s %s %s\n", r.Method, r.URL.Path, token)
|
||||||
|
|
|
@ -16,6 +16,7 @@ func ParseDuration(exp string) (int, error) {
|
||||||
if "" == exp {
|
if "" == exp {
|
||||||
exp = "15m"
|
exp = "15m"
|
||||||
}
|
}
|
||||||
|
|
||||||
mult := 1
|
mult := 1
|
||||||
switch exp[len(exp)-1] {
|
switch exp[len(exp)-1] {
|
||||||
case 'w':
|
case 'w':
|
||||||
|
@ -37,9 +38,11 @@ func ParseDuration(exp string) (int, error) {
|
||||||
exp += "s"
|
exp += "s"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 15m => num=15, mult=1*60
|
||||||
num, err := strconv.Atoi(exp[:len(exp)-1])
|
num, err := strconv.Atoi(exp[:len(exp)-1])
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return num * mult, nil
|
return num * mult, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
package xkeypairs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.rootprojects.org/root/keypairs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RandomReader = rand.Reader
|
||||||
|
|
||||||
|
type JWS struct {
|
||||||
|
Header Object `json:"header"` // JSON
|
||||||
|
Claims Object `json:"claims"` // JSON
|
||||||
|
Protected string `json:"protected"` // base64
|
||||||
|
Payload string `json:"payload"` // base64
|
||||||
|
Signature string `json:"signature"` // base64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Object = map[string]interface{}
|
||||||
|
|
||||||
|
func SignClaims(privkey keypairs.PrivateKey, header Object, claims Object) (*JWS, error) {
|
||||||
|
var randsrc io.Reader = RandomReader
|
||||||
|
seed, _ := header["_seed"].(int64)
|
||||||
|
if 0 != seed {
|
||||||
|
randsrc = mathrand.New(mathrand.NewSource(seed))
|
||||||
|
//delete(header, "_seed")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected, err := headerToProtected(keypairs.NewPublicKey(privkey.Public()), header)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
protected64 := base64.RawURLEncoding.EncodeToString(protected)
|
||||||
|
|
||||||
|
payload, err := claimsToPayload(claims)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload64 := base64.RawURLEncoding.EncodeToString(payload)
|
||||||
|
|
||||||
|
hash := sha256.Sum256([]byte(fmt.Sprintf(
|
||||||
|
`%s.%s`,
|
||||||
|
protected64,
|
||||||
|
payload64,
|
||||||
|
)))
|
||||||
|
|
||||||
|
sig := Sign(randsrc, privkey, hash[:])
|
||||||
|
sig64 := base64.RawURLEncoding.EncodeToString(sig)
|
||||||
|
|
||||||
|
return &JWS{
|
||||||
|
Header: header,
|
||||||
|
Claims: claims,
|
||||||
|
Protected: protected64,
|
||||||
|
Payload: payload64,
|
||||||
|
Signature: sig64,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func headerToProtected(pub keypairs.PublicKey, header Object) ([]byte, error) {
|
||||||
|
if nil == header {
|
||||||
|
header = Object{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only supporting 2048-bit and P256 keys right now
|
||||||
|
// because that's all that's practical and well-supported.
|
||||||
|
// No security theatre here.
|
||||||
|
alg := "ES256"
|
||||||
|
switch pub.Key().(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
alg = "RS256"
|
||||||
|
}
|
||||||
|
|
||||||
|
if selfSign, _ := header["_jwk"].(bool); selfSign {
|
||||||
|
delete(header, "_jwk")
|
||||||
|
any := Object{}
|
||||||
|
_ = json.Unmarshal(keypairs.MarshalJWKPublicKey(pub), &any)
|
||||||
|
header["jwk"] = any
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO what are the acceptable values? JWT. JWS? others?
|
||||||
|
header["typ"] = "JWT"
|
||||||
|
if _, ok := header["jwk"]; !ok {
|
||||||
|
thumbprint := keypairs.ThumbprintPublicKey(pub)
|
||||||
|
kid, _ := header["kid"].(string)
|
||||||
|
if "" != kid && thumbprint != kid {
|
||||||
|
return nil, errors.New("'kid' should be the key's thumbprint")
|
||||||
|
}
|
||||||
|
header["kid"] = thumbprint
|
||||||
|
}
|
||||||
|
header["alg"] = alg
|
||||||
|
|
||||||
|
protected, err := json.Marshal(header)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return protected, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func claimsToPayload(claims Object) ([]byte, error) {
|
||||||
|
if nil == claims {
|
||||||
|
claims = Object{}
|
||||||
|
}
|
||||||
|
|
||||||
|
jti, _ := claims["jti"].(string)
|
||||||
|
exp, _ := claims["exp"].(int64)
|
||||||
|
dur, _ := claims["exp"].(string)
|
||||||
|
insecure, _ := claims["insecure"].(bool)
|
||||||
|
|
||||||
|
// parse if exp is actually a duration, such as "15m"
|
||||||
|
if 0 == exp && "" != dur {
|
||||||
|
s, err := ParseDuration(dur)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exp = time.Now().Add(time.Duration(s) * time.Second).Unix()
|
||||||
|
claims["exp"] = exp
|
||||||
|
}
|
||||||
|
if "" == jti && 0 == exp && !insecure {
|
||||||
|
return nil, errors.New("token must have jti or exp as to be expirable / cancellable")
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(claims)
|
||||||
|
}
|
||||||
|
|
||||||
|
func JWSToJWT(jwt *JWS) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%s.%s.%s",
|
||||||
|
jwt.Protected,
|
||||||
|
jwt.Payload,
|
||||||
|
jwt.Signature,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sign(rand io.Reader, privkey keypairs.PrivateKey, hash []byte) []byte {
|
||||||
|
var sig []byte
|
||||||
|
|
||||||
|
if len(hash) != 32 {
|
||||||
|
panic("only 256-bit hashes for 2048-bit and 256-bit keys are supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch k := privkey.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
sig, _ = rsa.SignPKCS1v15(rand, k, crypto.SHA256, hash)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
r, s, _ := ecdsa.Sign(rand, k, hash[:])
|
||||||
|
rb := r.Bytes()
|
||||||
|
fmt.Println("debug:")
|
||||||
|
fmt.Println(r, s)
|
||||||
|
for len(rb) < 32 {
|
||||||
|
rb = append([]byte{0}, rb...)
|
||||||
|
}
|
||||||
|
sb := s.Bytes()
|
||||||
|
for len(rb) < 32 {
|
||||||
|
sb = append([]byte{0}, sb...)
|
||||||
|
}
|
||||||
|
sig = append(rb, sb...)
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
}
|
Loading…
Reference in New Issue