-
Notifications
You must be signed in to change notification settings - Fork 0
/
key_private.go
119 lines (98 loc) · 3.54 KB
/
key_private.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
package sshx
import (
"crypto"
"crypto/dsa" //nolint:staticcheck // to handle all keys that are already handled by ssh package we have to handle deprecated dsa keys
"errors"
"fmt"
"os"
"golang.org/x/crypto/ssh"
)
// PrivateKey defines common methods for all ssh private keys.
type PrivateKey interface {
Signer() ssh.Signer
PublicKey() PublicKey
Raw() crypto.PrivateKey
Equal(PrivateKey) error
}
// WrapPrivateKey wraps the provided crypto.PrivateKey.
func WrapPrivateKey(cryptoPrivKey crypto.PrivateKey) (PrivateKey, error) {
var pubKey PublicKey
{ // handle keys not implementing Public() any.
var cryptoPubKey crypto.PublicKey
if cryptoPrivKeyWithPublicKeyGetter, ok := cryptoPrivKey.(interface{ Public() crypto.PublicKey }); ok {
cryptoPubKey = cryptoPrivKeyWithPublicKeyGetter.Public()
} else {
switch typ := cryptoPrivKey.(type) {
case *dsa.PrivateKey:
cryptoPubKey = &typ.PublicKey
default:
// we don't know how to get the public key, either ssh.NewPublicKey know, or it won't work
cryptoPubKey = cryptoPrivKey
}
}
sshPubKey, err := ssh.NewPublicKey(cryptoPubKey)
if err != nil {
return nil, fmt.Errorf("unable to parse ssh public key: %v", err)
}
pubKey = WrapSSHPublicKey(sshPubKey)
}
signer, err := ssh.NewSignerFromKey(cryptoPrivKey)
if err != nil {
return nil, fmt.Errorf("unable to create signer from key %v", err)
}
return &privateKey{
privateKey: cryptoPrivKey,
publicKey: pubKey,
signer: signer,
}, nil
}
// NewPrivateKeyFromPEMBytes parses an SSH private key from PEM bytes.
func NewPrivateKeyFromPEMBytes(raw []byte, passphraseGetter func() ([]byte, error)) (PrivateKey, error) {
switch privKey, err := ssh.ParseRawPrivateKey(raw); {
case err == nil:
return WrapPrivateKey(privKey)
default:
if passphraseError := new(ssh.PassphraseMissingError); !(errors.As(err, &passphraseError) && passphraseGetter != nil) {
return nil, fmt.Errorf("unable to parse private key: %w", err)
}
}
passphrase, err := passphraseGetter()
if err != nil {
return nil, fmt.Errorf("unable to get passphrase: %w", err)
}
privKey, err := ssh.ParseRawPrivateKeyWithPassphrase(raw, passphrase)
if err != nil {
return nil, fmt.Errorf("unable to parse private key with passphrase: %v", err)
}
return WrapPrivateKey(privKey)
}
// NewPrivateKeyFromPEMFile parses an SSH private key from a pem file.
func NewPrivateKeyFromPEMFile(filePath string, passphraseGetter func() ([]byte, error)) (PrivateKey, error) {
rawFileContent, err := os.ReadFile(filePath) //nolint:gosec // G304 is a choice here
if err != nil {
return nil, fmt.Errorf("unable to read %q file: %w", filePath, err)
}
return NewPrivateKeyFromPEMBytes(rawFileContent, passphraseGetter)
}
type privateKey struct {
privateKey crypto.PrivateKey
publicKey PublicKey
signer ssh.Signer
}
func (key privateKey) Signer() ssh.Signer { return key.signer }
func (key privateKey) Raw() crypto.PrivateKey { return key.privateKey }
func (key privateKey) PublicKey() PublicKey { return key.publicKey }
func (key privateKey) Equal(comparedKey PrivateKey) error {
compareKeyCryptoPrivKey := comparedKey.Raw()
cryptoPrivKey := key.Raw()
cryptoPrivKeyWithEqual, ok := cryptoPrivKey.(interface {
Equal(crypto.PrivateKey) bool
})
if !ok {
return fmt.Errorf("crypto private key %T does not implement Equal method", cryptoPrivKey)
}
if !cryptoPrivKeyWithEqual.Equal(compareKeyCryptoPrivKey) {
return fmt.Errorf("crypto private key %T is not equal to provided crypto private key %T", cryptoPrivKeyWithEqual, compareKeyCryptoPrivKey)
}
return nil
}