partial public key support
This commit is contained in:
parent
fe05e7b2aa
commit
5695a0d0e7
103
keypairs.go
103
keypairs.go
|
@ -16,6 +16,13 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var EInvalidPrivateKey = errors.New("PrivateKey must be of type rsa.PrivateKey or ecdsa.PrivateKey")
|
||||||
|
var EInvalidPublicKey = errors.New("PublicKey must be of type rsa.PublicKey or ecdsa.PublicKey")
|
||||||
|
var EParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK")
|
||||||
|
var EParseJWK = errors.New("JWK is missing required base64-encoded JSON fields")
|
||||||
|
var EInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'")
|
||||||
|
var EInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'")
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Private KeyPrivacy = 1 << iota
|
Private KeyPrivacy = 1 << iota
|
||||||
Public
|
Public
|
||||||
|
@ -57,23 +64,13 @@ func (p *PublicJWK) JWK() string {
|
||||||
type thumbstr string
|
type thumbstr string
|
||||||
type jwkstr string
|
type jwkstr string
|
||||||
|
|
||||||
func FromPublic(key crypto.PublicKey) (pub PublicJWK) {
|
func PackPublicJWK(key crypto.PublicKey) (pub PublicJWK) {
|
||||||
// thumbprint keys are alphabetically sorted and only include the necessary public parts
|
// thumbprint keys are alphabetically sorted and only include the necessary public parts
|
||||||
switch k := key.(type) {
|
switch k := key.(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(k.E)).Bytes())
|
pub = packPublicRSAJWK(k)
|
||||||
n := base64.RawURLEncoding.EncodeToString(k.N.Bytes())
|
|
||||||
thumbprintable := fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n)
|
|
||||||
sha := sha256.Sum256([]byte(thumbprintable))
|
|
||||||
pub.thumbprint = thumbstr(base64.RawURLEncoding.EncodeToString(sha[:]))
|
|
||||||
pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"e":%q,"kty":"RSA","n":%q}`, pub.Thumbprint(), e, n))
|
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
|
pub = packPublicECJWK(k)
|
||||||
y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes())
|
|
||||||
thumbprintable := fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, k.Curve, x, y)
|
|
||||||
sha := sha256.Sum256([]byte(thumbprintable))
|
|
||||||
pub.thumbprint = thumbstr(base64.RawURLEncoding.EncodeToString(sha[:]))
|
|
||||||
pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"crv":%q,"kty":"EC","x":%q,"y":%q}`, pub.Thumbprint(), k.Curve, x, y))
|
|
||||||
case *dsa.PublicKey:
|
case *dsa.PublicKey:
|
||||||
panic(EInvalidPublicKey)
|
panic(EInvalidPublicKey)
|
||||||
default:
|
default:
|
||||||
|
@ -84,12 +81,25 @@ func FromPublic(key crypto.PublicKey) (pub PublicJWK) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var EInvalidPrivateKey = errors.New("PrivateKey must be of type rsa.PrivateKey or ecdsa.PrivateKey")
|
func packPublicECJWK(k *ecdsa.PublicKey) (pub PublicJWK) {
|
||||||
var EInvalidPublicKey = errors.New("PublicKey must be of type rsa.PublicKey or ecdsa.PublicKey")
|
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
|
||||||
var EParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK")
|
y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes())
|
||||||
var EParseJWK = errors.New("JWK is missing required base64-encoded JSON fields")
|
thumbprintable := fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, k.Curve, x, y)
|
||||||
var EInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'")
|
sha := sha256.Sum256([]byte(thumbprintable))
|
||||||
var EInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'")
|
pub.thumbprint = thumbstr(base64.RawURLEncoding.EncodeToString(sha[:]))
|
||||||
|
pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"crv":%q,"kty":"EC","x":%q,"y":%q}`, pub.Thumbprint(), k.Curve, x, y))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func packPublicRSAJWK(k *rsa.PublicKey) (pub PublicJWK) {
|
||||||
|
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(k.E)).Bytes())
|
||||||
|
n := base64.RawURLEncoding.EncodeToString(k.N.Bytes())
|
||||||
|
thumbprintable := fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n)
|
||||||
|
sha := sha256.Sum256([]byte(thumbprintable))
|
||||||
|
pub.thumbprint = thumbstr(base64.RawURLEncoding.EncodeToString(sha[:]))
|
||||||
|
pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"e":%q,"kty":"RSA","n":%q}`, pub.Thumbprint(), e, n))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ParsePrivateKey(block []byte) (key PrivateKey, err error) {
|
func ParsePrivateKey(block []byte) (key PrivateKey, err error) {
|
||||||
var pemblock *pem.Block
|
var pemblock *pem.Block
|
||||||
|
@ -148,6 +158,25 @@ func parsePrivateKey(der []byte) (key PrivateKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseJWKPublicKey(b []byte) (key crypto.PublicKey, err error) {
|
||||||
|
var m map[string]string
|
||||||
|
err = json.Unmarshal(b, &m)
|
||||||
|
if nil != err {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m["kty"] {
|
||||||
|
case "RSA":
|
||||||
|
key, err = parsePublicRSAJWK(m)
|
||||||
|
case "EC":
|
||||||
|
key, err = parsePublicECJWK(m)
|
||||||
|
default:
|
||||||
|
err = EInvalidKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ParseJWKPrivateKey(b []byte) (key PrivateKey, err error) {
|
func ParseJWKPrivateKey(b []byte) (key PrivateKey, err error) {
|
||||||
var m map[string]string
|
var m map[string]string
|
||||||
err = json.Unmarshal(b, &m)
|
err = json.Unmarshal(b, &m)
|
||||||
|
@ -167,21 +196,32 @@ func ParseJWKPrivateKey(b []byte) (key PrivateKey, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePrivateRSAJWK(m map[string]string) (key *rsa.PrivateKey, err error) {
|
func parsePublicRSAJWK(m map[string]string) (pub *rsa.PublicKey, err error) {
|
||||||
n, _ := base64.RawURLEncoding.DecodeString(m["n"])
|
n, _ := base64.RawURLEncoding.DecodeString(m["n"])
|
||||||
e, _ := base64.RawURLEncoding.DecodeString(m["e"])
|
e, _ := base64.RawURLEncoding.DecodeString(m["e"])
|
||||||
if 0 == len(n) || 0 == len(e) {
|
if 0 == len(n) || 0 == len(e) {
|
||||||
return nil, EParseJWK
|
err = EParseJWK
|
||||||
|
return
|
||||||
}
|
}
|
||||||
ni := &big.Int{}
|
ni := &big.Int{}
|
||||||
ni.SetBytes(n)
|
ni.SetBytes(n)
|
||||||
ei := &big.Int{}
|
ei := &big.Int{}
|
||||||
ei.SetBytes(e)
|
ei.SetBytes(e)
|
||||||
|
|
||||||
pub := rsa.PublicKey{
|
pub = &rsa.PublicKey{
|
||||||
N: ni,
|
N: ni,
|
||||||
E: int(ei.Int64()),
|
E: int(ei.Int64()),
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePrivateRSAJWK(m map[string]string) (key *rsa.PrivateKey, err error) {
|
||||||
|
var pub *rsa.PublicKey
|
||||||
|
|
||||||
|
pub, err = parsePublicRSAJWK(m)
|
||||||
|
if nil != err {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
|
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
|
||||||
p, _ := base64.RawURLEncoding.DecodeString(m["p"])
|
p, _ := base64.RawURLEncoding.DecodeString(m["p"])
|
||||||
|
@ -207,7 +247,7 @@ func parsePrivateRSAJWK(m map[string]string) (key *rsa.PrivateKey, err error) {
|
||||||
qinvi.SetBytes(qinv)
|
qinvi.SetBytes(qinv)
|
||||||
|
|
||||||
key = &rsa.PrivateKey{
|
key = &rsa.PrivateKey{
|
||||||
PublicKey: pub,
|
PublicKey: *pub,
|
||||||
D: di,
|
D: di,
|
||||||
Primes: []*big.Int{pi, qi},
|
Primes: []*big.Int{pi, qi},
|
||||||
Precomputed: rsa.PrecomputedValues{
|
Precomputed: rsa.PrecomputedValues{
|
||||||
|
@ -220,7 +260,7 @@ func parsePrivateRSAJWK(m map[string]string) (key *rsa.PrivateKey, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePrivateECJWK(m map[string]string) (key *ecdsa.PrivateKey, err error) {
|
func parsePublicECJWK(m map[string]string) (pub *ecdsa.PublicKey, err error) {
|
||||||
x, _ := base64.RawURLEncoding.DecodeString(m["n"])
|
x, _ := base64.RawURLEncoding.DecodeString(m["n"])
|
||||||
y, _ := base64.RawURLEncoding.DecodeString(m["e"])
|
y, _ := base64.RawURLEncoding.DecodeString(m["e"])
|
||||||
if 0 == len(x) || 0 == len(y) || 0 == len(m["crv"]) {
|
if 0 == len(x) || 0 == len(y) || 0 == len(m["crv"]) {
|
||||||
|
@ -246,12 +286,23 @@ func parsePrivateECJWK(m map[string]string) (key *ecdsa.PrivateKey, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pub := ecdsa.PublicKey{
|
pub = &ecdsa.PublicKey{
|
||||||
Curve: crv,
|
Curve: crv,
|
||||||
X: xi,
|
X: xi,
|
||||||
Y: yi,
|
Y: yi,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePrivateECJWK(m map[string]string) (key *ecdsa.PrivateKey, err error) {
|
||||||
|
var pub *ecdsa.PublicKey
|
||||||
|
|
||||||
|
pub, err = parsePublicECJWK(m)
|
||||||
|
if nil != err {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
|
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
|
||||||
if 0 == len(d) {
|
if 0 == len(d) {
|
||||||
return nil, EParseJWK
|
return nil, EParseJWK
|
||||||
|
@ -260,7 +311,7 @@ func parsePrivateECJWK(m map[string]string) (key *ecdsa.PrivateKey, err error) {
|
||||||
di.SetBytes(d)
|
di.SetBytes(d)
|
||||||
|
|
||||||
key = &ecdsa.PrivateKey{
|
key = &ecdsa.PrivateKey{
|
||||||
PublicKey: pub,
|
PublicKey: *pub,
|
||||||
D: di,
|
D: di,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue