From d4085cc29b9742a2e75c895d6551579eac4cea48 Mon Sep 17 00:00:00 2001
From: David Cowden <dcow@smallstep.com>
Date: Thu, 27 Aug 2020 21:50:10 -0700
Subject: [PATCH] keys: add method to load cert and encrypted key

Consumers can now have the convenience of loading an openssh style key
and cert pair when the key is encrypted with a passphrase.
---
 keys.go | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/keys.go b/keys.go
index 7353555..d483e90 100644
--- a/keys.go
+++ b/keys.go
@@ -19,6 +19,15 @@ func LoadCertFromKeyFileOpenSSH(keypath string) (ssh.Signer, error) {
 	return LoadCertFromFiles(keypath, certpath)
 }
 
+// LoadCertFromKeyFileEncOpenSSH returns an ssh.Signer from the encrypted key
+// stored at the given filesystem path with a public key that is the ssh
+// certificate loaded from the file "<path>-cert.pub". This is how ssh-add looks
+// for certs when adding keys to ssh-agent.
+func LoadCertFromKeyFileEncOpenSSH(keypath string, pass []byte) (ssh.Signer, error) {
+	certpath := keypath + "-cert.pub"
+	return LoadCertFromFilesEnc(keypath, certpath, pass)
+}
+
 // LoadCertFromFiles returns an ssh.Signer with private key loaded from the
 // unecrypted path keypath and a public cert component loaded from certpath.
 func LoadCertFromFiles(keypath, certpath string) (ssh.Signer, error) {
@@ -44,6 +53,32 @@ func LoadCertFromFiles(keypath, certpath string) (ssh.Signer, error) {
 	return signer, nil
 }
 
+// LoadCertFromFilesEnc returns an ssh.Signer with private key loaded from the
+// ecrypted key at path keypath and a public cert component loaded from certpath.
+func LoadCertFromFilesEnc(keypath, certpath string, pass []byte) (ssh.Signer, error) {
+	// Read host key from a file, parse using x/crypto/ssh.
+	kb, err := ioutil.ReadFile(keypath)
+	if err != nil {
+		return nil, err
+	}
+	key, err := ssh.ParsePrivateKeyWithPassphrase(kb, pass)
+	if err != nil {
+		return nil, err
+	}
+	cb, err := ioutil.ReadFile(certpath)
+	if err != nil {
+		return nil, err
+	}
+	pub, _, _, _, err := ssh.ParseAuthorizedKey(cb)
+	if err != nil {
+		return nil, err
+	}
+	cert := pub.(*ssh.Certificate)
+	signer, err := ssh.NewCertSigner(cert, key)
+	return signer, nil
+}
+
+
 // LoadKeyFromFile returns an ssh.Signer from the unencrypted key stored
 // at the given filesystem path.
 func LoadKeyFromFile(path string) (ssh.Signer, error) {
-- 
GitLab