fix public gpg key import

This commit is contained in:
Jörg Thalheim 2020-07-13 09:12:47 +01:00
parent 23ffb7df4e
commit 6286c5cc75
No known key found for this signature in database
GPG key ID: 003F2096411B5F92
5 changed files with 38 additions and 64 deletions

View file

@ -33,7 +33,10 @@ conversion tool to convert an existing ssh key (we only support RSA keys right n
``` ```
$ nix-shell -p ssh-to-pgp $ nix-shell -p ssh-to-pgp
$ ssh-to-pgp -privkey $HOME/.ssh/id_rsa | gpg --import --quiet $ ssh-to-pgp -private-key -i $HOME/.ssh/id_rsa | gpg --import --quiet
2504791468b153b8a3963cc97ba53d1919c5dfd4
# This exports the public key
$ ssh-to-pgp -i $HOME/.ssh/id_rsa -o $USER.asc
2504791468b153b8a3963cc97ba53d1919c5dfd4 2504791468b153b8a3963cc97ba53d1919c5dfd4
``` ```
@ -48,7 +51,7 @@ then your ssh key is encrypted with your password and you need to create a encry
``` ```
$ cp $HOME/.ssh/id_rsa /tmp/id_rsa $ cp $HOME/.ssh/id_rsa /tmp/id_rsa
$ ssh-keygen -p -N "" -f /tmp/id_rsa $ ssh-keygen -p -N "" -f /tmp/id_rsa
$ ssh-to-pgp -privkey /tmp/id_rsa | gpg --import --quiet $ ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --quiet
``` ```
The hex string printed here is your GPG fingerprint that can be exported to `SOPS_PGP_FP`. The hex string printed here is your GPG fingerprint that can be exported to `SOPS_PGP_FP`.
@ -71,23 +74,20 @@ uid [ unknown] root <root@localhost>
The fingerprint here is `9F89 C5F6 9A10 281A 8350 14B0 9C3D C61F 7520 87EF`, you The fingerprint here is `9F89 C5F6 9A10 281A 8350 14B0 9C3D C61F 7520 87EF`, you
need to remove the space in-between manually. need to remove the space in-between manually.
### 3. Get a GPG key for your machine ### 3. Get a PGP Public key for your machine
The easiest way to add new hosts is using ssh host keys (requires openssh to be enabled). The easiest way to add new hosts is using ssh host keys (requires openssh to be enabled).
Since sops does not natively supports ssh keys yet, nix-sops supports a conversion tool Since sops does not natively supports ssh keys yet, nix-sops supports a conversion tool
to store them as gpg keys. to store them as gpg keys.
``` ```
$ nix-shell -p ssh-to-gpg $ nix-shell -p ssh-to-pgp
# One can use ssh-keyscan over the network $ ssh root@server01 "cat /etc/ssh/ssh_host_rsa_key" | ssh-to-pgp -o server01.asc
$ ssh-keyscan -t rsa server01 | ssh-to-pgp -pubkey - > server01.asc # or with sudo
# server01:22 SSH-2.0-OpenSSH_8.2 $ ssh youruser@server01 "sudo cat /etc/ssh/ssh_host_rsa_key" | ssh-to-pgp -o server01.asc
0fd60c8c3b664aceb1796ce02b318df330331003
# via ssh command:
$ ssh server01 "cat /etc/ssh/ssh_host_rsa_key.pub" | ssh-to-gpg -pubkey - > hosts/server01.asc
0fd60c8c3b664aceb1796ce02b318df330331003 0fd60c8c3b664aceb1796ce02b318df330331003
# Or just read them locally (or in a ssh session) # Or just read them locally (or in a ssh session)
$ ssh-to-pgp -pubkey /etc/ssh/ssh_host_rsa_key.pub > server01.asc $ ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o server01.asc
0fd60c8c3b664aceb1796ce02b318df330331003 0fd60c8c3b664aceb1796ce02b318df330331003
``` ```

View file

@ -14,28 +14,19 @@ import (
) )
type options struct { type options struct {
publicKey, privateKey, format, out string format, out, in string
privateKey bool
} }
func parseFlags(args []string) options { func parseFlags(args []string) options {
var opts options var opts options
f := flag.NewFlagSet(args[0], flag.ExitOnError) f := flag.NewFlagSet(args[0], flag.ExitOnError)
f.StringVar(&opts.publicKey, "pubkey", "", "Path to public key. Reads from standard input if equal to '-'") f.BoolVar(&opts.privateKey, "private-key", false, "Export private key instead of public key")
f.StringVar(&opts.privateKey, "privkey", "", "Path to private key. Reads from standard input if equal to '-'")
f.StringVar(&opts.format, "format", "armor", "GPG format encoding (binary|armor)") f.StringVar(&opts.format, "format", "armor", "GPG format encoding (binary|armor)")
f.StringVar(&opts.in, "i", "-", "Input path. Reads by default from standard output")
f.StringVar(&opts.out, "o", "-", "Output path. Prints by default to standard output") f.StringVar(&opts.out, "o", "-", "Output path. Prints by default to standard output")
f.Parse(args[1:]) f.Parse(args[1:])
if opts.publicKey != "" && opts.privateKey != "" {
fmt.Fprintln(os.Stderr, "-pubkey and -privkey are mutual exclusive")
os.Exit(1)
}
if opts.publicKey == "" && opts.privateKey == "" {
fmt.Fprintln(os.Stderr, "Either -pubkey and -privkey must be specified")
os.Exit(1)
}
return opts return opts
} }
@ -43,19 +34,15 @@ func convertKeys(args []string) error {
opts := parseFlags(args) opts := parseFlags(args)
var err error var err error
var sshKey []byte var sshKey []byte
keyPath := opts.privateKey if opts.in == "-" {
if opts.publicKey != "" {
keyPath = opts.publicKey
}
if keyPath == "-" {
sshKey, _ = ioutil.ReadAll(os.Stdin) sshKey, _ = ioutil.ReadAll(os.Stdin)
if err != nil { if err != nil {
return fmt.Errorf("error reading stdin: %s", err) return fmt.Errorf("error reading stdin: %s", err)
} }
} else { } else {
sshKey, err = ioutil.ReadFile(keyPath) sshKey, err = ioutil.ReadFile(opts.in)
if err != nil { if err != nil {
return fmt.Errorf("error reading %s: %s", opts.privateKey, err) return fmt.Errorf("error reading %s: %s", opts.in, err)
} }
} }
@ -69,9 +56,9 @@ func convertKeys(args []string) error {
} }
if opts.format == "armor" { if opts.format == "armor" {
keyType := openpgp.PrivateKeyType keyType := openpgp.PublicKeyType
if opts.publicKey != "" { if opts.privateKey {
keyType = openpgp.PublicKeyType keyType = openpgp.PrivateKeyType
} }
writer, err = armor.Encode(writer, keyType, make(map[string]string)) writer, err = armor.Encode(writer, keyType, make(map[string]string))
if err != nil { if err != nil {
@ -79,28 +66,21 @@ func convertKeys(args []string) error {
} }
} }
var fingerprint [20]byte gpgKey, err := sshkeys.SSHPrivateKeyToPGP(sshKey)
if err != nil {
return err
}
if opts.publicKey != "" { if opts.privateKey {
gpgKey, err := sshkeys.SSHPublicKeyToPGP(sshKey)
if err != nil {
return err
}
err = gpgKey.Serialize(writer)
fingerprint = gpgKey.Fingerprint
} else {
gpgKey, err := sshkeys.SSHPrivateKeyToPGP(sshKey)
if err != nil {
return err
}
err = gpgKey.SerializePrivate(writer, nil) err = gpgKey.SerializePrivate(writer, nil)
fingerprint = gpgKey.PrimaryKey.Fingerprint } else {
err = gpgKey.Serialize(writer)
} }
if err == nil { if err == nil {
if opts.format == "armor" { if opts.format == "armor" {
writer.Close() writer.Close()
} }
fmt.Fprintf(os.Stderr, "%s\n", hex.EncodeToString(fingerprint[:])) fmt.Fprintf(os.Stderr, "%s\n", hex.EncodeToString(gpgKey.PrimaryKey.Fingerprint[:]))
} }
return err return err
} }

View file

@ -28,13 +28,12 @@ func TestCli(t *testing.T) {
defer os.RemoveAll(tempdir) defer os.RemoveAll(tempdir)
out := path.Join(tempdir, "out") out := path.Join(tempdir, "out")
pubKey := path.Join(assets, "id_rsa.pub")
privKey := path.Join(assets, "id_rsa") privKey := path.Join(assets, "id_rsa")
cmds := [][]string{ cmds := [][]string{
{"ssh-to-pgp", "-pubkey", pubKey, "-o", out}, {"ssh-to-pgp", "-i", privKey, "-o", out},
{"ssh-to-pgp", "-format=armor", "-pubkey", pubKey, "-o", out}, {"ssh-to-pgp", "-format=binary", "-i", privKey, "-o", out},
{"ssh-to-pgp", "-privkey", privKey, "-o", out}, {"ssh-to-pgp", "-private-key", "-i", privKey, "-o", out},
{"ssh-to-pgp", "-format=armor", "-privkey", privKey, "-o", out}, {"ssh-to-pgp", "-format=binary", "-private-key", "-i", privKey, "-o", out},
} }
for _, cmd := range cmds { for _, cmd := range cmds {
err = convertKeys(cmd) err = convertKeys(cmd)

View file

@ -1 +0,0 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDSeS6SEputIOi2mQhLeIEJnMAink+KcUv38HnaLak3nnLmUsYJnXYB5KZGxaxVtIjr59J8TndmwniZ+wc0rql6Dkif9CsTXgAjxrPiknZNQ7JQbgWUr0pk4jx/K3zLD6i/XAS8QWySNJmY5aJWySbF/K687kUMJ5ql0BX4Tt0RiWL4pIwzZZlLzH4rRySy4z1kbiuOZf8htVRtlGoDGqGViJRpuybSKrmXbevRI7aWjiml2BVTMktPekAPx+MA3t/8EM/uJxtWp7g3BsneHQdKIjR0WEKAITTmuDLEEtIXXEUbgBW0WjbD62nRft/A6/iyWykPJmkLA4WnSLS03caeUxCKoEthZ1xfBPCRNw7xbysQF8CHJz8cAMjZGgBGlOin8EKDhmlma6FZ94cAB5Tr4G3R0h4ky77bPk2/6vvZtyU/AFnDP2HfGaRCDNF+Q7+fR9YmKwcW/vCa2ItIEXgMmBjS+yl0p+4fVaY6Q7bCTbrd6znb6gTGo7nD9Kj/CGU= joerg@turingmachine

View file

@ -33,14 +33,6 @@ func parsePublicKey(publicKey []byte) (*rsa.PublicKey, error) {
return rsaKey, nil return rsaKey, nil
} }
func SSHPublicKeyToPGP(sshPublicKey []byte) (*packet.PublicKey, error) {
rsaKey, err := parsePublicKey(sshPublicKey)
if err != nil {
return nil, err
}
return packet.NewRSAPublicKey(time.Unix(0, 0), rsaKey), nil
}
func parsePrivateKey(sshPrivateKey []byte) (*rsa.PrivateKey, error) { func parsePrivateKey(sshPrivateKey []byte) (*rsa.PrivateKey, error) {
privateKey, err := ssh.ParseRawPrivateKey(sshPrivateKey) privateKey, err := ssh.ParseRawPrivateKey(sshPrivateKey)
if err != nil { if err != nil {
@ -70,7 +62,7 @@ func SSHPrivateKeyToPGP(sshPrivateKey []byte) (*openpgp.Entity, error) {
PrivateKey: packet.NewRSAPrivateKey(timeNull, key), PrivateKey: packet.NewRSAPrivateKey(timeNull, key),
Identities: make(map[string]*openpgp.Identity), Identities: make(map[string]*openpgp.Identity),
} }
uid := packet.NewUserId("root", "", "root@localhost") uid := packet.NewUserId("root", "Imported from SSH", "root@localhost")
isPrimaryID := true isPrimaryID := true
gpgKey.Identities[uid.Id] = &openpgp.Identity{ gpgKey.Identities[uid.Id] = &openpgp.Identity{
Name: uid.Id, Name: uid.Id,
@ -89,6 +81,10 @@ func SSHPrivateKeyToPGP(sshPrivateKey []byte) (*openpgp.Entity, error) {
IssuerKeyId: &gpgKey.PrimaryKey.KeyId, IssuerKeyId: &gpgKey.PrimaryKey.KeyId,
}, },
} }
err = gpgKey.Identities[uid.Id].SelfSignature.SignUserId(uid.Id, gpgKey.PrimaryKey, gpgKey.PrivateKey, nil)
if err != nil {
return nil, err
}
return gpgKey, nil return gpgKey, nil
} }