@@ -22,13 +22,14 @@ import (
22
22
"crypto/elliptic"
23
23
"crypto/rand"
24
24
"crypto/rsa"
25
- "encoding/base32 "
25
+ "encoding/base64 "
26
26
"errors"
27
27
"fmt"
28
- "golang.org/x/crypto/ed25519"
29
28
"io"
30
29
"os"
31
30
31
+ "golang.org/x/crypto/ed25519"
32
+
32
33
"gopkg.in/alecthomas/kingpin.v2"
33
34
"gopkg.in/square/go-jose.v2"
34
35
)
@@ -75,20 +76,32 @@ func KeygenSig(alg jose.SignatureAlgorithm, bits int) (crypto.PublicKey, crypto.
75
76
case jose .ES256 :
76
77
// The cryptographic operations are implemented using constant-time algorithms.
77
78
key , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
79
+ if err != nil {
80
+ return nil , nil , err
81
+ }
78
82
return key .Public (), key , err
79
83
case jose .ES384 :
80
84
// NB: The cryptographic operations do not use constant-time algorithms.
81
85
key , err := ecdsa .GenerateKey (elliptic .P384 (), rand .Reader )
86
+ if err != nil {
87
+ return nil , nil , err
88
+ }
82
89
return key .Public (), key , err
83
90
case jose .ES512 :
84
91
// NB: The cryptographic operations do not use constant-time algorithms.
85
92
key , err := ecdsa .GenerateKey (elliptic .P521 (), rand .Reader )
93
+ if err != nil {
94
+ return nil , nil , err
95
+ }
86
96
return key .Public (), key , err
87
97
case jose .EdDSA :
88
98
pub , key , err := ed25519 .GenerateKey (rand .Reader )
89
99
return pub , key , err
90
100
case jose .RS256 , jose .RS384 , jose .RS512 , jose .PS256 , jose .PS384 , jose .PS512 :
91
101
key , err := rsa .GenerateKey (rand .Reader , bits )
102
+ if err != nil {
103
+ return nil , nil , err
104
+ }
92
105
return key .Public (), key , err
93
106
default :
94
107
return nil , nil , errors .New ("unknown `alg` for `use` = `sig`" )
@@ -106,6 +119,9 @@ func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.Privat
106
119
return nil , nil , errors .New ("too short key for RSA `alg`, 2048+ is required" )
107
120
}
108
121
key , err := rsa .GenerateKey (rand .Reader , bits )
122
+ if err != nil {
123
+ return nil , nil , err
124
+ }
109
125
return key .Public (), key , err
110
126
case jose .ECDH_ES , jose .ECDH_ES_A128KW , jose .ECDH_ES_A192KW , jose .ECDH_ES_A256KW :
111
127
var crv elliptic.Curve
@@ -120,6 +136,9 @@ func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.Privat
120
136
return nil , nil , errors .New ("unknown elliptic curve bit length, use one of 256, 384, 521" )
121
137
}
122
138
key , err := ecdsa .GenerateKey (crv , rand .Reader )
139
+ if err != nil {
140
+ return nil , nil , err
141
+ }
123
142
return key .Public (), key , err
124
143
default :
125
144
return nil , nil , errors .New ("unknown `alg` for `use` = `enc`" )
@@ -130,29 +149,39 @@ func main() {
130
149
app .Version ("v2" )
131
150
kingpin .MustParse (app .Parse (os .Args [1 :]))
132
151
133
- if * kidRand {
134
- if * kid == "" {
135
- b := make ([]byte , 5 )
136
- _ , err := rand .Read (b )
137
- app .FatalIfError (err , "can't Read() crypto/rand" )
138
- * kid = base32 .StdEncoding .EncodeToString (b )
139
- } else {
140
- app .FatalUsage ("can't combine --kid and --kid-rand" )
141
- }
142
- }
143
-
144
- var privKey crypto.PublicKey
145
- var pubKey crypto.PrivateKey
152
+ var privKey crypto.PrivateKey
153
+ var pubKey crypto.PublicKey
146
154
var err error
147
155
switch * use {
148
156
case "sig" :
149
157
pubKey , privKey , err = KeygenSig (jose .SignatureAlgorithm (* alg ), * bits )
150
158
case "enc" :
151
159
pubKey , privKey , err = KeygenEnc (jose .KeyAlgorithm (* alg ), * bits )
160
+ default :
161
+ // According to RFC 7517 section-8.2. This is unlikely to change in the
162
+ // near future. If it were, new values could be found in the registry under
163
+ // "JSON Web Key Use": https://www.iana.org/assignments/jose/jose.xhtml
164
+ app .FatalIfError (errors .New ("invalid key use. Must be \" sig\" or \" enc\" " ), "unable to generate key" )
152
165
}
153
166
app .FatalIfError (err , "unable to generate key" )
154
167
155
168
priv := jose.JSONWebKey {Key : privKey , KeyID : * kid , Algorithm : * alg , Use : * use }
169
+
170
+ if * kidRand {
171
+ // Generate a canonical kid based on RFC 7638
172
+ if * kid == "" {
173
+ thumb , err := priv .Thumbprint (crypto .SHA256 )
174
+ app .FatalIfError (err , "unable to compute thumbprint" )
175
+ * kid = base64 .URLEncoding .EncodeToString (thumb )
176
+ priv .KeyID = * kid
177
+ } else {
178
+ app .FatalUsage ("can't combine --kid and --kid-rand" )
179
+ }
180
+ }
181
+
182
+ // I'm not sure why we couldn't use `pub := priv.Public()` here as the private
183
+ // key should contain the public key. In case for some reason it doesn't,
184
+ // this builds a public JWK from scratch.
156
185
pub := jose.JSONWebKey {Key : pubKey , KeyID : * kid , Algorithm : * alg , Use : * use }
157
186
158
187
if priv .IsPublic () || ! pub .IsPublic () || ! priv .Valid () || ! pub .Valid () {
@@ -170,8 +199,6 @@ func main() {
170
199
fmt .Printf ("==> jwk_%s <==\n " , * alg )
171
200
fmt .Println (string (privJS ))
172
201
} else {
173
- // JWK Thumbprint (RFC7638) is not used for key id because of
174
- // lack of canonical representation.
175
202
fname := fmt .Sprintf ("jwk_%s_%s_%s" , * use , * alg , * kid )
176
203
err = writeNewFile (fname + ".pub" , pubJS , 0444 )
177
204
app .FatalIfError (err , "can't write public key to file %s.pub" , fname )
0 commit comments