mirror of
https://github.com/ssh-vault/ssh-vault.git
synced 2025-04-19 07:42:18 +03:00
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.
116 lines
2.7 KiB
Go
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
|
|
}
|