-
Notifications
You must be signed in to change notification settings - Fork 280
/
jose.go
142 lines (121 loc) · 3.57 KB
/
jose.go
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
133
134
135
136
137
138
139
140
141
142
package cryptutil
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"github.com/go-jose/go-jose/v3"
"github.com/hashicorp/go-multierror"
)
// PrivateJWKFromBytes returns a jose JSON Web _Private_ Key from bytes.
func PrivateJWKFromBytes(data []byte) (*jose.JSONWebKey, error) {
jwks, err := loadKeys(data, loadPrivateKey)
if err != nil {
return nil, err
} else if len(jwks) == 0 {
return nil, fmt.Errorf("invalid pem data")
}
return jwks[0], nil
}
// PrivateJWKsFromBytes returns jose JSON Web _Private_ Keys from bytes.
func PrivateJWKsFromBytes(data []byte) ([]*jose.JSONWebKey, error) {
return loadKeys(data, loadPrivateKey)
}
// PublicJWKFromBytes returns a jose JSON Web _Public_ Key from bytes.
func PublicJWKFromBytes(data []byte) (*jose.JSONWebKey, error) {
jwks, err := loadKeys(data, loadPublicKey)
if err != nil {
return nil, err
} else if len(jwks) == 0 {
return nil, fmt.Errorf("invalid pem data")
}
return jwks[0], nil
}
// PublicJWKsFromBytes returns jose JSON Web _Public_ Keys from bytes.
func PublicJWKsFromBytes(data []byte) ([]*jose.JSONWebKey, error) {
return loadKeys(data, loadPublicKey)
}
func loadKeys(data []byte, unmarshal func([]byte) (any, error)) ([]*jose.JSONWebKey, error) {
var jwks []*jose.JSONWebKey
for {
var block *pem.Block
block, data = pem.Decode(data)
if block == nil {
break
}
key, err := unmarshal(block.Bytes)
if err != nil {
return nil, fmt.Errorf("unmarshal key: %w", err)
}
alg, err := SignatureAlgorithmForKey(key)
if err != nil {
return nil, err
}
jwk := &jose.JSONWebKey{Key: key, Use: "sig", Algorithm: string(alg)}
thumbprint, err := jwk.Thumbprint(crypto.SHA256)
if err != nil {
return nil, fmt.Errorf("computing thumbprint: %w", err)
}
jwk.KeyID = hex.EncodeToString(thumbprint)
jwks = append(jwks, jwk)
}
return jwks, nil
}
func loadPrivateKey(b []byte) (any, error) {
var wrappedErr error
var err error
var key any
if key, err = x509.ParseECPrivateKey(b); err == nil {
return key, nil
}
wrappedErr = multierror.Append(wrappedErr, err)
if key, err = x509.ParsePKCS1PrivateKey(b); err == nil {
return key, nil
}
wrappedErr = multierror.Append(wrappedErr, err)
if key, err = x509.ParsePKCS8PrivateKey(b); err == nil {
return key, nil
}
wrappedErr = multierror.Append(wrappedErr, err)
return nil, fmt.Errorf("couldn't load private key: %w", wrappedErr)
}
// https://github.com/square/go-jose/tree/v2.5.1#supported-key-types
func loadPublicKey(b []byte) (any, error) {
var wrappedErr error
var err error
var key any
if key, err = loadPrivateKey(b); err == nil {
switch k := key.(type) {
case *rsa.PrivateKey:
return k.Public(), nil
case *ecdsa.PrivateKey:
return k.Public(), nil
default:
return nil, fmt.Errorf("private key is unsupported type")
}
}
wrappedErr = multierror.Append(wrappedErr, err)
if key, err = x509.ParsePKIXPublicKey(b); err == nil {
return key, nil
}
wrappedErr = multierror.Append(wrappedErr, err)
if key, err = x509.ParseCertificate(b); err == nil {
return key, nil
}
wrappedErr = multierror.Append(wrappedErr, err)
return nil, fmt.Errorf("couldn't load public key: %w", wrappedErr)
}
// SignatureAlgorithmForKey returns the signature algorithm for the given key.
func SignatureAlgorithmForKey(key any) (jose.SignatureAlgorithm, error) {
switch key.(type) {
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
return jose.ES256, nil
case *rsa.PrivateKey, *rsa.PublicKey:
return jose.RS256, nil
default:
return "", fmt.Errorf("crypto: unsupported key type for signing: %T", key)
}
}