1
0
mirror of https://github.com/ssh-vault/ssh-vault.git synced 2025-04-19 07:42:18 +03:00
ssh-vault/view.go
Modestas Vainius 4a7c1d3e74
Support encrypted openssh private keys (#50)
Currently ssh-vault does not appear to support encrypted openssh private
keys, i.e. those which type is "OPENSSH PRIVATE KEY". While trying to
view a file encrypted against such a key, user gets the following error:

    could not parse private key: ssh: this private key is passphrase protected

This commit fixes the problem by trying to decrypt the key without
password first and then handling missing password error appropriately.
2021-07-11 23:08:01 +02:00

116 lines
2.7 KiB
Go

package sshvault
import (
"bufio"
"bytes"
"crypto/rsa"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/ssh-vault/crypto/aead"
"github.com/ssh-vault/crypto/oaep"
"golang.org/x/crypto/ssh"
)
// View decrypts data and print it to stdout
func (v *vault) View() ([]byte, error) {
var (
header []string
rawPayload bytes.Buffer
scanner *bufio.Scanner
)
// check if there is someting to read on STDIN
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
scanner = bufio.NewScanner(os.Stdin)
} else {
file, err := os.Open(v.vault)
if err != nil {
return nil, fmt.Errorf("missing vault name, use (\"%s -h\") for help", os.Args[0])
}
defer file.Close()
scanner = bufio.NewScanner(file)
}
scanner.Split(bufio.ScanLines)
l := 1
for scanner.Scan() {
line := scanner.Text()
if l == 1 {
header = strings.Split(line, ";")
} else {
rawPayload.WriteString(line)
}
l++
}
// ssh-vault;AES256;fingerprint
if len(header) != 3 {
return nil, fmt.Errorf("bad ssh-vault signature, verify the input")
}
// password, body
payload := strings.Split(rawPayload.String(), ";")
if len(payload) != 2 {
return nil, fmt.Errorf("bad ssh-vault payload, verify the input")
}
// use private key only
if strings.HasSuffix(v.key, ".pub") {
v.key = strings.Trim(v.key, ".pub")
}
keyFile, err := ioutil.ReadFile(v.key)
if err != nil {
return nil, fmt.Errorf("Error reading private key: %s", err)
}
block, _ := pem.Decode(keyFile)
if block == nil || !strings.HasSuffix(block.Type, "PRIVATE KEY") {
return nil, fmt.Errorf("No valid PEM (private key) data found")
}
var privateKey interface{}
privateKey, err = ssh.ParseRawPrivateKey(keyFile)
if err, ok := err.(*ssh.PassphraseMissingError); ok {
keyPassword, err := v.GetPassword()
if err != nil {
return nil, fmt.Errorf("unable to get private key password, Decryption failed")
}
privateKey, err = ssh.ParseRawPrivateKeyWithPassphrase(keyFile, keyPassword)
if err != nil {
return nil, fmt.Errorf("could not parse private key: %v", err)
}
} else if err != nil {
return nil, fmt.Errorf("could not parse private key: %v", err)
}
ciphertext, err := base64.StdEncoding.DecodeString(payload[0])
if err != nil {
return nil, err
}
v.Password, err = oaep.Decrypt(privateKey.(*rsa.PrivateKey), ciphertext, []byte(""))
if err != nil {
return nil, fmt.Errorf("Decryption failed, use private key with fingerprint: %s", header[2])
}
ciphertext, err = base64.StdEncoding.DecodeString(payload[1])
if err != nil {
return nil, err
}
// decrypt ciphertext using fingerprint as additionalData
data, err := aead.Decrypt(v.Password, ciphertext, []byte(header[2]))
if err != nil {
return nil, err
}
return data, nil
}