-
Notifications
You must be signed in to change notification settings - Fork 96
/
Copy pathkey.go
131 lines (103 loc) · 2.56 KB
/
key.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package tunnel
import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"github.com/awnumar/memguard"
"golang.org/x/crypto/ssh"
)
// PemKeyParser translates pem keys to a signature signer.
type PemKeyParser interface {
// Parse returns a key signer to create signatures that verify against a
// public key.
Parse() (*ssh.Signer, error)
}
// PemKey holds data related to PEM keys
type PemKey struct {
// Data holds the data for a PEM private key
Data []byte
// passphrase used to parse a PEM encoded private key
passphrase *memguard.LockedBuffer
}
func NewPemKey(keyPath, passphrase string) (*PemKey, error) {
data, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, err
}
k := &PemKey{Data: data}
if passphrase != "" {
k.updatePassphrase([]byte(passphrase))
}
return k, nil
}
// IsEncrypted inspects the key data block to tell if it is whether encrypted
// or not.
func (k PemKey) IsEncrypted() (bool, error) {
p, err := decodePemKey(k.Data)
if err != nil {
return false, err
}
return x509.IsEncryptedPEMBlock(p), nil //nolint
}
// Parse translates a pem key to a signer to create signatures that verify
// against a public key.
func (k *PemKey) Parse() (ssh.Signer, error) {
var signer ssh.Signer
enc, err := k.IsEncrypted()
if err != nil {
return nil, err
}
if enc {
if k.passphrase == nil {
return nil, fmt.Errorf("can't read protected ssh key because no passphrase was provided")
}
signer, err = ssh.ParsePrivateKeyWithPassphrase(k.Data, k.passphrase.Bytes())
if err != nil {
return nil, err
}
} else {
signer, err = ssh.ParsePrivateKey(k.Data)
if err != nil {
return nil, err
}
}
return signer, nil
}
// HandlePassphrase securely records a passphrase given by a callback to the
// memory.
func (k *PemKey) HandlePassphrase(handler func() ([]byte, error)) error {
enc, err := k.IsEncrypted()
if err != nil {
return fmt.Errorf("error while reading ssh key: %v", err)
}
if !enc {
return nil
}
pp, err := handler()
if err != nil {
return fmt.Errorf("error while reading password: %v", err)
}
k.updatePassphrase(pp)
return nil
}
func (k *PemKey) updatePassphrase(pp []byte) {
if k.passphrase != nil {
k.passphrase.Destroy()
}
if len(pp) < 1 {
k.passphrase = nil
return
}
k.passphrase = memguard.NewBufferFromBytes(pp)
}
func decodePemKey(data []byte) (*pem.Block, error) {
p, r := pem.Decode(data)
if p == nil && len(r) > 0 {
return nil, fmt.Errorf("error while parsing key: no PEM data found")
}
if len(r) != 0 {
return nil, fmt.Errorf("extra data in encoded key")
}
return p, nil
}