-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import jwk-kengen from https://github.com/square/go-jose/tree/v2/jwk-…
- Loading branch information
Showing
3 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module github.com/openstandia/jwkgen | ||
|
||
go 1.12 | ||
|
||
require ( | ||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect | ||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect | ||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f | ||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 | ||
gopkg.in/square/go-jose.v2 v2.3.1 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= | ||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= | ||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= | ||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= | ||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | ||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= | ||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
/*- | ||
* Copyright 2017 Square Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"crypto" | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"encoding/base32" | ||
"errors" | ||
"fmt" | ||
"golang.org/x/crypto/ed25519" | ||
"io" | ||
"os" | ||
|
||
"gopkg.in/alecthomas/kingpin.v2" | ||
"gopkg.in/square/go-jose.v2" | ||
) | ||
|
||
var ( | ||
app = kingpin.New("jwk-keygen", "A command-line utility to generate public/pirvate keypairs in JWK format.") | ||
|
||
use = app.Flag("use", "Desrired key use").Required().Enum("enc", "sig") | ||
alg = app.Flag("alg", "Generate key to be used for ALG").Required().Enum( | ||
// `sig` | ||
string(jose.ES256), string(jose.ES384), string(jose.ES512), string(jose.EdDSA), | ||
string(jose.RS256), string(jose.RS384), string(jose.RS512), string(jose.PS256), string(jose.PS384), string(jose.PS512), | ||
// `enc` | ||
string(jose.RSA1_5), string(jose.RSA_OAEP), string(jose.RSA_OAEP_256), | ||
string(jose.ECDH_ES), string(jose.ECDH_ES_A128KW), string(jose.ECDH_ES_A192KW), string(jose.ECDH_ES_A256KW), | ||
) | ||
bits = app.Flag("bits", "Key size in bits").Int() | ||
kid = app.Flag("kid", "Key ID").String() | ||
kidRand = app.Flag("kid-rand", "Generate random Key ID").Bool() | ||
) | ||
|
||
// KeygenSig generates keypair for corresponding SignatureAlgorithm. | ||
func KeygenSig(alg jose.SignatureAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) { | ||
switch alg { | ||
case jose.ES256, jose.ES384, jose.ES512, jose.EdDSA: | ||
keylen := map[jose.SignatureAlgorithm]int{ | ||
jose.ES256: 256, | ||
jose.ES384: 384, | ||
jose.ES512: 521, // sic! | ||
jose.EdDSA: 256, | ||
} | ||
if bits != 0 && bits != keylen[alg] { | ||
return nil, nil, errors.New("this `alg` does not support arbitrary key length") | ||
} | ||
case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512: | ||
if bits == 0 { | ||
bits = 2048 | ||
} | ||
if bits < 2048 { | ||
return nil, nil, errors.New("too short key for RSA `alg`, 2048+ is required") | ||
} | ||
} | ||
switch alg { | ||
case jose.ES256: | ||
// The cryptographic operations are implemented using constant-time algorithms. | ||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
return key.Public(), key, err | ||
case jose.ES384: | ||
// NB: The cryptographic operations do not use constant-time algorithms. | ||
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) | ||
return key.Public(), key, err | ||
case jose.ES512: | ||
// NB: The cryptographic operations do not use constant-time algorithms. | ||
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) | ||
return key.Public(), key, err | ||
case jose.EdDSA: | ||
pub, key, err := ed25519.GenerateKey(rand.Reader) | ||
return pub, key, err | ||
case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512: | ||
key, err := rsa.GenerateKey(rand.Reader, bits) | ||
return key.Public(), key, err | ||
default: | ||
return nil, nil, errors.New("unknown `alg` for `use` = `sig`") | ||
} | ||
} | ||
|
||
// KeygenEnc generates keypair for corresponding KeyAlgorithm. | ||
func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) { | ||
switch alg { | ||
case jose.RSA1_5, jose.RSA_OAEP, jose.RSA_OAEP_256: | ||
if bits == 0 { | ||
bits = 2048 | ||
} | ||
if bits < 2048 { | ||
return nil, nil, errors.New("too short key for RSA `alg`, 2048+ is required") | ||
} | ||
key, err := rsa.GenerateKey(rand.Reader, bits) | ||
return key.Public(), key, err | ||
case jose.ECDH_ES, jose.ECDH_ES_A128KW, jose.ECDH_ES_A192KW, jose.ECDH_ES_A256KW: | ||
var crv elliptic.Curve | ||
switch bits { | ||
case 0, 256: | ||
crv = elliptic.P256() | ||
case 384: | ||
crv = elliptic.P384() | ||
case 521: | ||
crv = elliptic.P521() | ||
default: | ||
return nil, nil, errors.New("unknown elliptic curve bit length, use one of 256, 384, 521") | ||
} | ||
key, err := ecdsa.GenerateKey(crv, rand.Reader) | ||
return key.Public(), key, err | ||
default: | ||
return nil, nil, errors.New("unknown `alg` for `use` = `enc`") | ||
} | ||
} | ||
|
||
func main() { | ||
app.Version("v2") | ||
kingpin.MustParse(app.Parse(os.Args[1:])) | ||
|
||
if *kidRand { | ||
if *kid == "" { | ||
b := make([]byte, 5) | ||
_, err := rand.Read(b) | ||
app.FatalIfError(err, "can't Read() crypto/rand") | ||
*kid = base32.StdEncoding.EncodeToString(b) | ||
} else { | ||
app.FatalUsage("can't combine --kid and --kid-rand") | ||
} | ||
} | ||
|
||
var privKey crypto.PublicKey | ||
var pubKey crypto.PrivateKey | ||
var err error | ||
switch *use { | ||
case "sig": | ||
pubKey, privKey, err = KeygenSig(jose.SignatureAlgorithm(*alg), *bits) | ||
case "enc": | ||
pubKey, privKey, err = KeygenEnc(jose.KeyAlgorithm(*alg), *bits) | ||
} | ||
app.FatalIfError(err, "unable to generate key") | ||
|
||
priv := jose.JSONWebKey{Key: privKey, KeyID: *kid, Algorithm: *alg, Use: *use} | ||
pub := jose.JSONWebKey{Key: pubKey, KeyID: *kid, Algorithm: *alg, Use: *use} | ||
|
||
if priv.IsPublic() || !pub.IsPublic() || !priv.Valid() || !pub.Valid() { | ||
app.Fatalf("invalid keys were generated") | ||
} | ||
|
||
privJS, err := priv.MarshalJSON() | ||
app.FatalIfError(err, "can't Marshal private key to JSON") | ||
pubJS, err := pub.MarshalJSON() | ||
app.FatalIfError(err, "can't Marshal public key to JSON") | ||
|
||
if *kid == "" { | ||
fmt.Printf("==> jwk_%s.pub <==\n", *alg) | ||
fmt.Println(string(pubJS)) | ||
fmt.Printf("==> jwk_%s <==\n", *alg) | ||
fmt.Println(string(privJS)) | ||
} else { | ||
// JWK Thumbprint (RFC7638) is not used for key id because of | ||
// lack of canonical representation. | ||
fname := fmt.Sprintf("jwk_%s_%s_%s", *use, *alg, *kid) | ||
err = writeNewFile(fname+".pub", pubJS, 0444) | ||
app.FatalIfError(err, "can't write public key to file %s.pub", fname) | ||
fmt.Printf("Written public key to %s.pub\n", fname) | ||
err = writeNewFile(fname, privJS, 0400) | ||
app.FatalIfError(err, "cant' write private key to file %s", fname) | ||
fmt.Printf("Written private key to %s\n", fname) | ||
} | ||
} | ||
|
||
// writeNewFile is shameless copy-paste from ioutil.WriteFile with a bit | ||
// different flags for OpenFile. | ||
func writeNewFile(filename string, data []byte, perm os.FileMode) error { | ||
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) | ||
if err != nil { | ||
return err | ||
} | ||
n, err := f.Write(data) | ||
if err == nil && n < len(data) { | ||
err = io.ErrShortWrite | ||
} | ||
if err1 := f.Close(); err == nil { | ||
err = err1 | ||
} | ||
return err | ||
} |