...

Source file src/gopkg.in/square/go-jose.v2/jwk-keygen/main.go

Documentation: gopkg.in/square/go-jose.v2/jwk-keygen

     1  /*-
     2   * Copyright 2017 Square Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package main
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/elliptic"
    23  	"crypto/rand"
    24  	"crypto/rsa"
    25  	"encoding/base64"
    26  	"errors"
    27  	"fmt"
    28  	"io"
    29  	"os"
    30  
    31  	"golang.org/x/crypto/ed25519"
    32  
    33  	"gopkg.in/alecthomas/kingpin.v2"
    34  	"gopkg.in/square/go-jose.v2"
    35  )
    36  
    37  var (
    38  	app = kingpin.New("jwk-keygen", "A command-line utility to generate public/pirvate keypairs in JWK format.")
    39  
    40  	use = app.Flag("use", "Desired key use").Required().Enum("enc", "sig")
    41  	alg = app.Flag("alg", "Generate key to be used for ALG").Required().Enum(
    42  		// `sig`
    43  		string(jose.ES256), string(jose.ES384), string(jose.ES512), string(jose.EdDSA),
    44  		string(jose.RS256), string(jose.RS384), string(jose.RS512), string(jose.PS256), string(jose.PS384), string(jose.PS512),
    45  		// `enc`
    46  		string(jose.RSA1_5), string(jose.RSA_OAEP), string(jose.RSA_OAEP_256),
    47  		string(jose.ECDH_ES), string(jose.ECDH_ES_A128KW), string(jose.ECDH_ES_A192KW), string(jose.ECDH_ES_A256KW),
    48  	)
    49  	bits    = app.Flag("bits", "Key size in bits").Int()
    50  	kid     = app.Flag("kid", "Key ID").String()
    51  	kidRand = app.Flag("kid-rand", "Generate random Key ID").Bool()
    52  )
    53  
    54  // KeygenSig generates keypair for corresponding SignatureAlgorithm.
    55  func KeygenSig(alg jose.SignatureAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) {
    56  	switch alg {
    57  	case jose.ES256, jose.ES384, jose.ES512, jose.EdDSA:
    58  		keylen := map[jose.SignatureAlgorithm]int{
    59  			jose.ES256: 256,
    60  			jose.ES384: 384,
    61  			jose.ES512: 521, // sic!
    62  			jose.EdDSA: 256,
    63  		}
    64  		if bits != 0 && bits != keylen[alg] {
    65  			return nil, nil, errors.New("this `alg` does not support arbitrary key length")
    66  		}
    67  	case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
    68  		if bits == 0 {
    69  			bits = 2048
    70  		}
    71  		if bits < 2048 {
    72  			return nil, nil, errors.New("too short key for RSA `alg`, 2048+ is required")
    73  		}
    74  	}
    75  	switch alg {
    76  	case jose.ES256:
    77  		// The cryptographic operations are implemented using constant-time algorithms.
    78  		key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    79  		if err != nil {
    80  			return nil, nil, err
    81  		}
    82  		return key.Public(), key, err
    83  	case jose.ES384:
    84  		// NB: The cryptographic operations do not use constant-time algorithms.
    85  		key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
    86  		if err != nil {
    87  			return nil, nil, err
    88  		}
    89  		return key.Public(), key, err
    90  	case jose.ES512:
    91  		// NB: The cryptographic operations do not use constant-time algorithms.
    92  		key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
    93  		if err != nil {
    94  			return nil, nil, err
    95  		}
    96  		return key.Public(), key, err
    97  	case jose.EdDSA:
    98  		pub, key, err := ed25519.GenerateKey(rand.Reader)
    99  		return pub, key, err
   100  	case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
   101  		key, err := rsa.GenerateKey(rand.Reader, bits)
   102  		if err != nil {
   103  			return nil, nil, err
   104  		}
   105  		return key.Public(), key, err
   106  	default:
   107  		return nil, nil, errors.New("unknown `alg` for `use` = `sig`")
   108  	}
   109  }
   110  
   111  // KeygenEnc generates keypair for corresponding KeyAlgorithm.
   112  func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) {
   113  	switch alg {
   114  	case jose.RSA1_5, jose.RSA_OAEP, jose.RSA_OAEP_256:
   115  		if bits == 0 {
   116  			bits = 2048
   117  		}
   118  		if bits < 2048 {
   119  			return nil, nil, errors.New("too short key for RSA `alg`, 2048+ is required")
   120  		}
   121  		key, err := rsa.GenerateKey(rand.Reader, bits)
   122  		if err != nil {
   123  			return nil, nil, err
   124  		}
   125  		return key.Public(), key, err
   126  	case jose.ECDH_ES, jose.ECDH_ES_A128KW, jose.ECDH_ES_A192KW, jose.ECDH_ES_A256KW:
   127  		var crv elliptic.Curve
   128  		switch bits {
   129  		case 0, 256:
   130  			crv = elliptic.P256()
   131  		case 384:
   132  			crv = elliptic.P384()
   133  		case 521:
   134  			crv = elliptic.P521()
   135  		default:
   136  			return nil, nil, errors.New("unknown elliptic curve bit length, use one of 256, 384, 521")
   137  		}
   138  		key, err := ecdsa.GenerateKey(crv, rand.Reader)
   139  		if err != nil {
   140  			return nil, nil, err
   141  		}
   142  		return key.Public(), key, err
   143  	default:
   144  		return nil, nil, errors.New("unknown `alg` for `use` = `enc`")
   145  	}
   146  }
   147  
   148  func main() {
   149  	app.Version("v2")
   150  	kingpin.MustParse(app.Parse(os.Args[1:]))
   151  
   152  	var privKey crypto.PrivateKey
   153  	var pubKey crypto.PublicKey
   154  	var err error
   155  	switch *use {
   156  	case "sig":
   157  		pubKey, privKey, err = KeygenSig(jose.SignatureAlgorithm(*alg), *bits)
   158  	case "enc":
   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")
   165  	}
   166  	app.FatalIfError(err, "unable to generate key")
   167  
   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.
   185  	pub := jose.JSONWebKey{Key: pubKey, KeyID: *kid, Algorithm: *alg, Use: *use}
   186  
   187  	if priv.IsPublic() || !pub.IsPublic() || !priv.Valid() || !pub.Valid() {
   188  		app.Fatalf("invalid keys were generated")
   189  	}
   190  
   191  	privJS, err := priv.MarshalJSON()
   192  	app.FatalIfError(err, "can't Marshal private key to JSON")
   193  	pubJS, err := pub.MarshalJSON()
   194  	app.FatalIfError(err, "can't Marshal public key to JSON")
   195  
   196  	if *kid == "" {
   197  		fmt.Printf("==> jwk_%s.pub <==\n", *alg)
   198  		fmt.Println(string(pubJS))
   199  		fmt.Printf("==> jwk_%s <==\n", *alg)
   200  		fmt.Println(string(privJS))
   201  	} else {
   202  		fname := fmt.Sprintf("jwk_%s_%s_%s", *use, *alg, *kid)
   203  		err = writeNewFile(fname+".pub", pubJS, 0444)
   204  		app.FatalIfError(err, "can't write public key to file %s.pub", fname)
   205  		fmt.Printf("Written public key to %s.pub\n", fname)
   206  		err = writeNewFile(fname, privJS, 0400)
   207  		app.FatalIfError(err, "cant' write private key to file %s", fname)
   208  		fmt.Printf("Written private key to %s\n", fname)
   209  	}
   210  }
   211  
   212  // writeNewFile is shameless copy-paste from ioutil.WriteFile with a bit
   213  // different flags for OpenFile.
   214  func writeNewFile(filename string, data []byte, perm os.FileMode) error {
   215  	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
   216  	if err != nil {
   217  		return err
   218  	}
   219  	n, err := f.Write(data)
   220  	if err == nil && n < len(data) {
   221  		err = io.ErrShortWrite
   222  	}
   223  	if err1 := f.Close(); err == nil {
   224  		err = err1
   225  	}
   226  	return err
   227  }
   228  

View as plain text