/
keyStore.go
276 lines (249 loc) · 8.35 KB
/
keyStore.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package utility
import (
"bufio"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"time"
"unsafe"
keystore "github.com/pavel-v-chernykh/keystore-go/v4"
)
const (
defaultHostname = "default"
)
// GenerateKeyStore generates the keyStore and saves it to disk. It requires
// the keypass, keystore file, hostname for the cert and the certificate and
// key file locations in PEM format
// GenerateKeyStore generates the keyStore and saves it to disk.
func GenerateKeyStore(keyStoreFile *string, hostname *string,
pemCertFile *string, pemKeyFile *string, keyStorePass *string) error {
// If parsing the following parameters off the command line
// Usage: ./sillyProxy -keypass arg -keystore arg1 -hostname arg3 -pemCert arg4
// -pemKey arg5 KeyStore
//check if flags are provided or not
if *keyStoreFile == "" {
return fmt.Errorf("keyStore not provided. Please use -keyStore flag")
}
if *pemCertFile == "" {
return fmt.Errorf("pemCert flag not set. Please use -pemCert to set it")
}
if *pemKeyFile == "" {
return fmt.Errorf("pemkey flag not set. Please use -pemkey to set it")
}
if *keyStorePass == "" {
fmt.Printf("keyPass is not set. Proceed with blank password?[y/anyotherkey]: ")
reader := bufio.NewReader(os.Stdin)
choice, _ := reader.ReadString('\n')
if choice[0] != 89 && choice[0] != 121 {
return fmt.Errorf("Sure! Aborting key entry")
}
fmt.Printf("Proceeding with blank password..")
}
if *hostname == "" {
fmt.Printf("hostname not provided. Proceed with %s?[y/anyotherkey]: ", "default")
reader := bufio.NewReader(os.Stdin)
choice, _ := reader.ReadString('\n')
if choice[0] != 89 && choice[0] != 121 {
fmt.Printf("Aborting key entry. Try with hostname flag")
return fmt.Errorf("key entry aborted")
}
*hostname = defaultHostname
}
keyStorePassBytes := []byte(*keyStorePass)
// zeroing out the password and its bytes
zeroString(keyStorePass)
defer zeroBytes(keyStorePassBytes)
//var keyStore keystore.Keystore
//keyStore = make(keystore.KeyStore)
keyStore := keystore.New(keystore.WithCaseExactAliases())
//load the pem files in a *tls.Certificate Type
cert, pemLoadError := tls.LoadX509KeyPair(*pemCertFile, *pemKeyFile)
if pemLoadError != nil {
return fmt.Errorf("Pem files loading failed with the error:%v", pemLoadError)
}
//now parse the returned certificate to figure out the kind of
// keyentry and certificate
x509Cert, parseError := x509.ParseCertificate(cert.Certificate[0])
if parseError != nil {
return fmt.Errorf(parseError.Error())
}
//build the appropriate alias for the certificate entry
var alias string
var certType string
switch x509Cert.PublicKey.(type) {
case *rsa.PublicKey:
alias = *hostname + ":RSA"
certType = "RSA"
case *ecdsa.PublicKey:
alias = *hostname + ":ECDSA"
certType = "ECDSA"
default:
log.Fatal(errors.New("unsupported public key algorithm"))
}
// check if the keystore already exists at the location
if fileExists(*keyStoreFile) {
//there is a bug with the below code. It will throw an error if the file
// exists but is empty.
loadKSErr := loadKeyStore(*keyStoreFile, keyStorePassBytes, &keyStore)
if loadKSErr != nil {
log.Fatal(errors.New("keyStore file loading failed with the error: " +
fmt.Sprintf("%v", loadKSErr)))
}
//checking if the alias already exists. If yes, prompt the user if she wishes
// to overwrite it
if aliasExists(&keyStore, alias) {
//get a confirmation from the user that she wishes to
// overwrite the alias
reader := bufio.NewReader(os.Stdin)
fmt.Printf("A key-pair cert already exists under the name %s. Do "+
"you wish to overwrite? [y/anyotherkey]?", alias)
choice, _ := reader.ReadString('\n')
if choice[0] != 89 && choice[0] != 121 {
//fmt.Printf("Aborting key entry. Have a good day!\n")
return fmt.Errorf("aborting key entry, have a good day")
}
}
if !(*hostname == defaultHostname) &&
!aliasExists(&keyStore, "default:RSA") &&
!aliasExists(&keyStore, "default:ECDSA") {
// Throw a warning to the user that the keystore does not yet have a "default"
// alias cert and whether she would like to import the current cert as "default"
reader := bufio.NewReader(os.Stdin)
fmt.Printf("A \"default\" cert alias does not exist in the keystore. Do " +
"you wish to import this cert with the \"default\" alias? [y/anyotherkey]?")
choice, _ := reader.ReadString('\n')
if choice[0] == 89 || choice[0] == 121 {
fmt.Printf("Great, importing this cert as %#v", "default:"+certType)
alias = "default:" + certType
} else {
fmt.Printf("Sure, Continuing with %#v as the alias.\n"+
"But do remember that sillyProxy wont run without atleast"+
"one cert type with default alias", alias)
}
}
} else if !(*hostname == defaultHostname) {
//We know that the keystore does not exist yet. But the alias provided
// is not "default". Warn the user that a default cert is absolutely
//necessary for SillyProxy to fire up.
reader := bufio.NewReader(os.Stdin)
fmt.Printf("A \"default\" cert alias is necessary for SillyProxy to fire. " +
"Wish to import this cert with \"default\" alias instead? [y/anyotherkey]?")
choice, _ := reader.ReadString('\n')
if choice[0] == 89 || choice[0] == 121 {
fmt.Printf("Great, importing this cert as %#v", "default:"+certType)
alias = "default:" + certType
} else {
fmt.Printf("Sure, Continuing with %#v as the alias.\n"+
"But do remember that sillyProxy won't run unless "+
"one cert type with default alias is in the keystore", alias)
}
}
//populate the keystore
populatekeyStoreErr := populateKeyStore(&keyStore, alias,
*pemKeyFile, &cert, keyStorePassBytes)
if populatekeyStoreErr != nil {
return fmt.Errorf("keyStore population failed with the error:" +
fmt.Sprintf("%v", populatekeyStoreErr))
}
//clearout the keystore and the cert files after usage
defer clearOut(&keyStore)
defer clearOut(&cert)
//write the keystore to file
keyStoreWriteErr := writeKeystore(&keyStore, *keyStoreFile, keyStorePassBytes)
if keyStoreWriteErr != nil {
return fmt.Errorf("KeyStore writing failed with error: " +
fmt.Sprintf("%v", keyStoreWriteErr))
}
return nil
}
func zeroBytes(s []byte) {
for i := 0; i < len(s); i++ {
s[i] = 0
}
}
func clearOut(v interface{}) {
p := reflect.ValueOf(v).Elem()
p.Set(reflect.Zero(p.Type()))
}
func zeroString(s *string) {
len := len(*s)
sHeader := (*reflect.StringHeader)(unsafe.Pointer(s))
var a []byte
for i := 0; i < len; i++ {
a = append(a, 48)
}
dupe := string(a)
dupeHeader := (*reflect.StringHeader)(unsafe.Pointer(&dupe))
sHeader.Data = dupeHeader.Data
}
func loadKeyStore(fileLocation string, password []byte,
keyStore *keystore.KeyStore) error {
//first check for the file's existence.
f, err := os.Open(fileLocation)
defer f.Close()
if err != nil {
err = errors.New("loadKeyStore failed with error: " +
fmt.Sprintf("%v", err))
return err
}
err = keyStore.Load(f, password)
if err != nil {
err = errors.New("loadKeyStore failed with error: " +
fmt.Sprintf("%v", err))
return err
}
return nil
}
func fileExists(fileLocation string) bool {
if _, err := os.Stat(fileLocation); os.IsNotExist(err) {
return false
}
return true
}
func aliasExists(keyStore *keystore.KeyStore, alias string) bool {
if exists := keyStore.IsPrivateKeyEntry(alias); exists {
return true
}
return false
}
func populateKeyStore(keyStore *keystore.KeyStore, alias string,
pemKeyFile string, cert *tls.Certificate, password []byte) error {
certChain := make([]keystore.Certificate, len(cert.Certificate),
len(cert.Certificate))
for i := 0; i < len(cert.Certificate); i++ {
certChain[i].Content = cert.Certificate[i]
certChain[i].Type = fmt.Sprintf("%dth Certificate in %s", i, alias)
}
keyPEMBlock, keyReadError := ioutil.ReadFile(pemKeyFile)
if keyReadError != nil {
return keyReadError
}
privateKeyEntry := keystore.PrivateKeyEntry{
CreationTime: time.Now(),
PrivateKey: keyPEMBlock,
CertificateChain: certChain,
}
setPrivateKeyEntryErr := keyStore.SetPrivateKeyEntry(alias, privateKeyEntry,
password)
if setPrivateKeyEntryErr != nil {
return setPrivateKeyEntryErr
}
return nil
}
func writeKeystore(keyStore *keystore.KeyStore, fileLocation string,
password []byte) error {
o, err := os.Create(fileLocation)
if err != nil {
return err
}
defer o.Close()
err = keyStore.Store(o, password)
return err
}