/*- * Copyright 2014 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 ( "fmt" "io/ioutil" "os" "gopkg.in/alecthomas/kingpin.v2" "gopkg.in/square/go-jose.v1" ) var ( app = kingpin.New("jose-util", "A command-line utility for dealing with JOSE objects.") keyFile = app.Flag("key", "Path to key file (PEM or DER-encoded)").ExistingFile() inFile = app.Flag("in", "Path to input file (stdin if missing)").ExistingFile() outFile = app.Flag("out", "Path to output file (stdout if missing)").ExistingFile() encryptCommand = app.Command("encrypt", "Encrypt a plaintext, output ciphertext.") algFlag = encryptCommand.Flag("alg", "Key management algorithm (e.g. RSA-OAEP)").Required().String() encFlag = encryptCommand.Flag("enc", "Content encryption algorithm (e.g. A128GCM)").Required().String() decryptCommand = app.Command("decrypt", "Decrypt a ciphertext, output plaintext.") signCommand = app.Command("sign", "Sign a payload, output signed message.") sigAlgFlag = signCommand.Flag("alg", "Key management algorithm (e.g. RSA-OAEP)").Required().String() verifyCommand = app.Command("verify", "Verify a signed message, output payload.") expandCommand = app.Command("expand", "Expand JOSE object to full serialization format.") formatFlag = expandCommand.Flag("format", "Type of message to expand (JWS or JWE, defaults to JWE)").String() full = app.Flag("full", "Use full serialization format (instead of compact)").Bool() ) func main() { app.Version("v1") command := kingpin.MustParse(app.Parse(os.Args[1:])) var keyBytes []byte var err error if command != "expand" { keyBytes, err = ioutil.ReadFile(*keyFile) exitOnError(err, "unable to read key file") } switch command { case "encrypt": pub, err := jose.LoadPublicKey(keyBytes) exitOnError(err, "unable to read public key") alg := jose.KeyAlgorithm(*algFlag) enc := jose.ContentEncryption(*encFlag) crypter, err := jose.NewEncrypter(alg, enc, pub) exitOnError(err, "unable to instantiate encrypter") obj, err := crypter.Encrypt(readInput(*inFile)) exitOnError(err, "unable to encrypt") var msg string if *full { msg = obj.FullSerialize() } else { msg, err = obj.CompactSerialize() exitOnError(err, "unable to serialize message") } writeOutput(*outFile, []byte(msg)) case "decrypt": priv, err := jose.LoadPrivateKey(keyBytes) exitOnError(err, "unable to read private key") obj, err := jose.ParseEncrypted(string(readInput(*inFile))) exitOnError(err, "unable to parse message") plaintext, err := obj.Decrypt(priv) exitOnError(err, "unable to decrypt message") writeOutput(*outFile, plaintext) case "sign": signingKey, err := jose.LoadPrivateKey(keyBytes) exitOnError(err, "unable to read private key") alg := jose.SignatureAlgorithm(*sigAlgFlag) signer, err := jose.NewSigner(alg, signingKey) exitOnError(err, "unable to make signer") obj, err := signer.Sign(readInput(*inFile)) exitOnError(err, "unable to sign") var msg string if *full { msg = obj.FullSerialize() } else { msg, err = obj.CompactSerialize() exitOnError(err, "unable to serialize message") } writeOutput(*outFile, []byte(msg)) case "verify": verificationKey, err := jose.LoadPublicKey(keyBytes) exitOnError(err, "unable to read private key") obj, err := jose.ParseSigned(string(readInput(*inFile))) exitOnError(err, "unable to parse message") plaintext, err := obj.Verify(verificationKey) exitOnError(err, "invalid signature") writeOutput(*outFile, plaintext) case "expand": input := string(readInput(*inFile)) var serialized string var err error switch *formatFlag { case "", "JWE": var jwe *jose.JsonWebEncryption jwe, err = jose.ParseEncrypted(input) if err == nil { serialized = jwe.FullSerialize() } case "JWS": var jws *jose.JsonWebSignature jws, err = jose.ParseSigned(input) if err == nil { serialized = jws.FullSerialize() } } exitOnError(err, "unable to expand message") writeOutput(*outFile, []byte(serialized)) writeOutput(*outFile, []byte("\n")) } } // Exit and print error message if we encountered a problem func exitOnError(err error, msg string) { if err != nil { fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err) os.Exit(1) } } // Read input from file or stdin func readInput(path string) []byte { var bytes []byte var err error if path != "" { bytes, err = ioutil.ReadFile(path) } else { bytes, err = ioutil.ReadAll(os.Stdin) } exitOnError(err, "unable to read input") return bytes } // Write output to file or stdin func writeOutput(path string, data []byte) { var err error if path != "" { err = ioutil.WriteFile(path, data, 0644) } else { _, err = os.Stdout.Write(data) } exitOnError(err, "unable to write output") }