pgp2ssh/main.go

117 lines
2.8 KiB
Go
Raw Normal View History

2024-03-20 20:34:29 +01:00
package main
import (
"encoding/pem"
2024-03-20 20:34:29 +01:00
"fmt"
"log"
"os"
"syscall"
2024-03-20 20:34:29 +01:00
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
2024-03-20 20:34:29 +01:00
"github.com/ProtonMail/go-crypto/openpgp/packet"
"crypto/ed25519"
"errors"
2024-03-26 22:25:47 +01:00
"golang.org/x/crypto/ssh"
"golang.org/x/term"
2024-03-25 17:22:47 +01:00
"reflect"
2024-03-20 20:34:29 +01:00
)
func readEntity(keypath string) (*openpgp.Entity, error) {
f, err := os.Open(keypath)
if err != nil {
log.Println("Error opening file")
return nil, err
}
defer f.Close()
block, err := armor.Decode(f)
if err != nil {
log.Println("decoding")
return nil, err
}
return openpgp.ReadEntity(packet.NewReader(block.Body))
}
var (
UnsupportedKeyType = errors.New("only ed25519 keys are supported")
)
func main() {
var keyfile string
log.Println("Enter path to private PGP key (default: ./priv.asc):")
_, err := fmt.Scanf("%s", &keyfile)
if err != nil && err.Error() == "unexpected newline" {
2024-03-27 19:29:36 +01:00
keyfile = "./priv.asc"
} else if err != nil {
log.Fatal(err)
2024-03-20 20:34:29 +01:00
}
e, err := readEntity(keyfile)
2024-03-20 20:34:29 +01:00
if err != nil {
log.Fatal(err)
2024-03-20 20:34:29 +01:00
}
log.Println("Keys:")
log.Println("[0]", e.PrimaryKey.KeyIdString()+" (primary)")
for i := 0; i < len(e.Subkeys); i++ {
log.Println(fmt.Sprintf("[%d]", i+1), e.Subkeys[i].PublicKey.KeyIdString()+" (subkey)")
}
2024-03-25 17:22:47 +01:00
log.Println("Choose key by index (default: 0):")
var keyIndex int
_, err = fmt.Scanf("%d", &keyIndex)
if err != nil && err.Error() == "unexpected newline" {
keyIndex = 0
} else if err != nil {
2024-03-20 20:34:29 +01:00
log.Fatal(err)
}
var targetKey *packet.PrivateKey
if keyIndex == 0 {
log.Println(fmt.Sprintf("Continuing with key [%d]", keyIndex), e.PrimaryKey.KeyIdString())
targetKey = e.PrivateKey
} else if keyIndex > 0 {
var subkey = e.Subkeys[keyIndex-1]
log.Println(fmt.Sprintf("Continuing with key [%d]", keyIndex), subkey.PublicKey.KeyIdString())
targetKey = subkey.PrivateKey
} else {
log.Fatal("Invalid key index")
}
if targetKey.Encrypted {
log.Println("Please enter passphrase to decrypt PGP key:")
bytePassphrase, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
log.Fatal(err)
}
targetKey.Decrypt(bytePassphrase)
}
log.Println("private key type:", reflect.TypeOf(targetKey.PrivateKey))
castkey, ok := targetKey.PrivateKey.(*eddsa.PrivateKey)
if !ok {
log.Fatal("failed to cast")
}
log.Println("public key type:", reflect.TypeOf(castkey.PublicKey))
2024-03-26 22:25:47 +01:00
var pubkey ed25519.PublicKey = castkey.PublicKey.X
sshPub, err := ssh.NewPublicKey(pubkey)
2024-03-25 17:22:47 +01:00
if err != nil {
log.Fatal(err)
}
log.Println("public SSH key:\n" + string(ssh.MarshalAuthorizedKey(sshPub)))
2024-03-20 20:34:29 +01:00
var privkey = ed25519.NewKeyFromSeed(castkey.D)
2024-03-27 16:34:28 +01:00
privPem, err := ssh.MarshalPrivateKey(&privkey, "")
2024-03-20 20:34:29 +01:00
if err != nil {
log.Fatal(err)
}
privateKeyPem := pem.EncodeToMemory(privPem)
log.Println("Private SSH key:\n" + string(privateKeyPem))
2024-03-20 20:34:29 +01:00
}