-
Notifications
You must be signed in to change notification settings - Fork 248
/
validate.go
206 lines (188 loc) 路 5.55 KB
/
validate.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package jose
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"fmt"
"io/ioutil"
"github.com/pkg/errors"
"github.com/smallstep/cli/crypto/keys"
"github.com/smallstep/cli/crypto/pemutil"
"golang.org/x/crypto/ssh"
)
// ValidateSSHPOP validates the given SSH certificate and key for use in an
// sshpop header.
func ValidateSSHPOP(certFile string, key interface{}) (string, error) {
if certFile == "" {
return "", errors.New("ssh certfile cannot be empty")
}
certBytes, err := ioutil.ReadFile(certFile)
if err != nil {
return "", errors.Wrapf(err, "error reading ssh certificate from %s", certFile)
}
sshpub, _, _, _, err := ssh.ParseAuthorizedKey(certBytes)
if err != nil {
return "", errors.Wrapf(err, "error parsing ssh public key from %s", certFile)
}
cert, ok := sshpub.(*ssh.Certificate)
if !ok {
return "", errors.New("error casting ssh public key to ssh certificate")
}
pubkey, err := keys.ExtractKey(cert)
if err != nil {
return "", errors.Wrap(err, "error extracting public key from ssh public key interface")
}
if err = keys.VerifyPair(pubkey, key); err != nil {
return "", errors.Wrap(err, "error verifying ssh key pair")
}
return base64.StdEncoding.EncodeToString(cert.Marshal()), nil
}
func validateX5(certFile string, key interface{}) ([]*x509.Certificate, error) {
if certFile == "" {
return nil, errors.New("certfile cannot be empty")
}
certs, err := pemutil.ReadCertificateBundle(certFile)
if err != nil {
return nil, errors.Wrap(err, "error reading certificate chain from file")
}
if err = keys.VerifyPair(certs[0].PublicKey, key); err != nil {
return nil, errors.Wrap(err, "error verifying certificate and key")
}
if certs[0].KeyUsage&x509.KeyUsageDigitalSignature == 0 {
return nil, errors.New("certificate/private-key pair used to sign " +
"token is not approved for digital signature")
}
return certs, nil
}
// ValidateX5C validates the given certificate chain and key for use as a token
// signer and x5t header.
func ValidateX5C(certFile string, key interface{}) ([]string, error) {
certs, err := validateX5(certFile, key)
if err != nil {
return nil, errors.Wrap(err, "ValidateX5C")
}
strs := make([]string, len(certs))
for i, cert := range certs {
strs[i] = base64.StdEncoding.EncodeToString(cert.Raw)
}
return strs, nil
}
// ValidateX5T validates the given certificate and key for use as a token signer
// and x5t header.
func ValidateX5T(certFile string, key interface{}) (string, error) {
certs, err := validateX5(certFile, key)
if err != nil {
return "", errors.Wrap(err, "ValidateX5T")
}
// x5t is the base64 URL encoded SHA1 thumbprint
// (see https://tools.ietf.org/html/rfc7515#section-4.1.7)
fingerprint := sha1.Sum(certs[0].Raw)
return base64.URLEncoding.EncodeToString(fingerprint[:]), nil
}
// ValidateJWK validates the given JWK.
func ValidateJWK(jwk *JSONWebKey) error {
switch jwk.Use {
case "sig":
return validateSigJWK(jwk)
case "enc":
return validateEncJWK(jwk)
default:
return validateGeneric(jwk)
}
}
// validateSigJWK validates the given JWK for signature operations.
func validateSigJWK(jwk *JSONWebKey) error {
if jwk.Algorithm == "" {
return errors.New("flag '--alg' is required with the given key")
}
errctx := "the given key"
switch k := jwk.Key.(type) {
case []byte:
switch jwk.Algorithm {
case HS256, HS384, HS512:
return nil
}
errctx = "kty 'oct'"
case *rsa.PrivateKey, *rsa.PublicKey:
switch jwk.Algorithm {
case RS256, RS384, RS512:
return nil
case PS256, PS384, PS512:
return nil
}
errctx = "kty 'RSA'"
case *ecdsa.PrivateKey:
curve := k.Params().Name
switch {
case jwk.Algorithm == ES256 && curve == P256:
return nil
case jwk.Algorithm == ES384 && curve == P384:
return nil
case jwk.Algorithm == ES512 && curve == P521:
return nil
}
errctx = fmt.Sprintf("kty 'EC' and crv '%s'", curve)
case *ecdsa.PublicKey:
curve := k.Params().Name
switch {
case jwk.Algorithm == ES256 && curve == P256:
return nil
case jwk.Algorithm == ES384 && curve == P384:
return nil
case jwk.Algorithm == ES512 && curve == P521:
return nil
}
errctx = fmt.Sprintf("kty 'EC' and crv '%s'", curve)
case ed25519.PrivateKey, ed25519.PublicKey:
if jwk.Algorithm == EdDSA {
return nil
}
errctx = "kty 'OKP' and crv 'Ed25519'"
}
return errors.Errorf("alg '%s' is not compatible with %s", jwk.Algorithm, errctx)
}
// validatesEncJWK validates the given JWK for encryption operations.
func validateEncJWK(jwk *JSONWebKey) error {
alg := KeyAlgorithm(jwk.Algorithm)
var kty string
switch jwk.Key.(type) {
case []byte:
switch alg {
case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW:
return nil
}
kty = "oct"
case *rsa.PrivateKey, *rsa.PublicKey:
switch alg {
case RSA1_5, RSA_OAEP, RSA_OAEP_256:
return nil
}
kty = "RSA"
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
switch alg {
case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
return nil
}
kty = "EC"
case ed25519.PrivateKey, ed25519.PublicKey:
return errors.New("key Ed25519 cannot be used for encryption")
}
return errors.Errorf("alg '%s' is not compatible with kty '%s'", jwk.Algorithm, kty)
}
// validateGeneric validates just the supported key types.
func validateGeneric(jwk *JSONWebKey) error {
switch jwk.Key.(type) {
case []byte:
return nil
case *rsa.PrivateKey, *rsa.PublicKey:
return nil
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
return nil
case ed25519.PrivateKey, ed25519.PublicKey:
return nil
}
return errors.Errorf("unsupported key type '%T'", jwk.Key)
}