From 884013090b1b56b207f644393865c6057c9999ca Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Mon, 18 Apr 2016 22:11:39 +0100 Subject: Initial commit --- client/keys.go | 64 ++++++++++++++++++++++++++++++ client/main.go | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 client/keys.go create mode 100644 client/main.go (limited to 'client') diff --git a/client/keys.go b/client/keys.go new file mode 100644 index 0000000..4acfbb9 --- /dev/null +++ b/client/keys.go @@ -0,0 +1,64 @@ +package main + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "fmt" + + "golang.org/x/crypto/ssh" +) + +const ( + rsaKey = "rsa" + ecdsaKey = "ecdsa" +) + +type key interface{} + +func generateRSAKey(bits int) (*rsa.PrivateKey, ssh.PublicKey, error) { + k, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return nil, nil, err + } + pub, err := ssh.NewPublicKey(&k.PublicKey) + if err != nil { + return nil, nil, err + } + return k, pub, nil +} + +func generateECDSAKey(bits int) (*ecdsa.PrivateKey, ssh.PublicKey, error) { + var curve elliptic.Curve + switch bits { + case 256: + curve = elliptic.P256() + case 384: + curve = elliptic.P384() + case 521: + curve = elliptic.P521() + default: + return nil, nil, fmt.Errorf("Unsupported key size. Valid sizes are '256', '384', '521'") + } + k, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, nil, err + } + pub, err := ssh.NewPublicKey(&k.PublicKey) + if err != nil { + return nil, nil, err + } + return k, pub, nil +} + +func generateKey(keytype string, bits int) (key, ssh.PublicKey, error) { + switch keytype { + case rsaKey: + return generateRSAKey(bits) + case ecdsaKey: + return generateECDSAKey(bits) + default: + return nil, nil, fmt.Errorf("Unsupported key type %s. Valid choices are [%s, %s]", keytype, rsaKey, ecdsaKey) + } +} diff --git a/client/main.go b/client/main.go new file mode 100644 index 0000000..10a3646 --- /dev/null +++ b/client/main.go @@ -0,0 +1,121 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "os" + "time" + + "github.com/nsheridan/cashier/lib" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" +) + +var ( + url = flag.String("url", "http://localhost:10000/sign", "Signing URL") + keybits = flag.Int("bits", 4096, "Key size") + validity = flag.Duration("validity", time.Hour*24, "Key validity") + keytype = flag.String("key_type", "rsa", "Type of private key to generate - rsa or ecdsa") +) + +func installCert(a agent.Agent, cert *ssh.Certificate, key key) error { + pubcert := agent.AddedKey{ + PrivateKey: key, + Certificate: cert, + Comment: cert.KeyId, + } + if err := a.Add(pubcert); err != nil { + return fmt.Errorf("error importing certificate: %s", err) + } + return nil +} + +func send(s []byte, token string) (*lib.SignResponse, error) { + req, err := http.NewRequest("POST", *url, bytes.NewReader(s)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Add("Accept", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + client := &http.Client{} + resp, err := client.Do(req) + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Bad response from server: %s", resp.Status) + } + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + c := &lib.SignResponse{} + if err := json.Unmarshal(body, c); err != nil { + return nil, err + } + return c, nil +} + +func sign(pub ssh.PublicKey, token string) (*ssh.Certificate, error) { + marshaled := ssh.MarshalAuthorizedKey(pub) + marshaled = marshaled[:len(marshaled)-1] + s, err := json.Marshal(&lib.SignRequest{ + Key: string(marshaled), + ValidUntil: time.Now().Add(*validity), + }) + if err != nil { + return nil, err + } + resp, err := send(s, token) + if err != nil { + return nil, err + } + if resp.Status != "ok" { + return nil, fmt.Errorf("error: %s", resp.Response) + } + k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(resp.Response)) + if err != nil { + return nil, err + } + cert, ok := k.(*ssh.Certificate) + if !ok { + return nil, fmt.Errorf("did not receive a certificate from server") + } + return cert, nil +} + +func main() { + flag.Parse() + + priv, pub, err := generateKey(*keytype, *keybits) + if err != nil { + log.Fatalln("Error generating key pair: ", err) + } + + fmt.Print("Enter token: ") + var token string + fmt.Scanln(&token) + + cert, err := sign(pub, token) + if err != nil { + log.Fatalln(err) + } + sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) + if err != nil { + log.Fatalln("Error connecting to agent: %s", err) + } + defer sock.Close() + a := agent.NewClient(sock) + if err := installCert(a, cert, priv); err != nil { + log.Fatalln(err) + } + fmt.Println("Certificate added.") +} -- cgit v1.2.3