blob: c526de5455bcf040d4f3ebddb40318240147f854 [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001package libtrust
2
3import (
4 "encoding/json"
5 "encoding/pem"
6 "errors"
7 "fmt"
8 "io/ioutil"
9 "os"
10 "strings"
11)
12
13var (
14 // ErrKeyFileDoesNotExist indicates that the private key file does not exist.
15 ErrKeyFileDoesNotExist = errors.New("key file does not exist")
16)
17
18func readKeyFileBytes(filename string) ([]byte, error) {
19 data, err := ioutil.ReadFile(filename)
20 if err != nil {
21 if os.IsNotExist(err) {
22 err = ErrKeyFileDoesNotExist
23 } else {
24 err = fmt.Errorf("unable to read key file %s: %s", filename, err)
25 }
26
27 return nil, err
28 }
29
30 return data, nil
31}
32
33/*
34 Loading and Saving of Public and Private Keys in either PEM or JWK format.
35*/
36
37// LoadKeyFile opens the given filename and attempts to read a Private Key
38// encoded in either PEM or JWK format (if .json or .jwk file extension).
39func LoadKeyFile(filename string) (PrivateKey, error) {
40 contents, err := readKeyFileBytes(filename)
41 if err != nil {
42 return nil, err
43 }
44
45 var key PrivateKey
46
47 if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
48 key, err = UnmarshalPrivateKeyJWK(contents)
49 if err != nil {
50 return nil, fmt.Errorf("unable to decode private key JWK: %s", err)
51 }
52 } else {
53 key, err = UnmarshalPrivateKeyPEM(contents)
54 if err != nil {
55 return nil, fmt.Errorf("unable to decode private key PEM: %s", err)
56 }
57 }
58
59 return key, nil
60}
61
62// LoadPublicKeyFile opens the given filename and attempts to read a Public Key
63// encoded in either PEM or JWK format (if .json or .jwk file extension).
64func LoadPublicKeyFile(filename string) (PublicKey, error) {
65 contents, err := readKeyFileBytes(filename)
66 if err != nil {
67 return nil, err
68 }
69
70 var key PublicKey
71
72 if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
73 key, err = UnmarshalPublicKeyJWK(contents)
74 if err != nil {
75 return nil, fmt.Errorf("unable to decode public key JWK: %s", err)
76 }
77 } else {
78 key, err = UnmarshalPublicKeyPEM(contents)
79 if err != nil {
80 return nil, fmt.Errorf("unable to decode public key PEM: %s", err)
81 }
82 }
83
84 return key, nil
85}
86
87// SaveKey saves the given key to a file using the provided filename.
88// This process will overwrite any existing file at the provided location.
89func SaveKey(filename string, key PrivateKey) error {
90 var encodedKey []byte
91 var err error
92
93 if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
94 // Encode in JSON Web Key format.
95 encodedKey, err = json.MarshalIndent(key, "", " ")
96 if err != nil {
97 return fmt.Errorf("unable to encode private key JWK: %s", err)
98 }
99 } else {
100 // Encode in PEM format.
101 pemBlock, err := key.PEMBlock()
102 if err != nil {
103 return fmt.Errorf("unable to encode private key PEM: %s", err)
104 }
105 encodedKey = pem.EncodeToMemory(pemBlock)
106 }
107
108 err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0600))
109 if err != nil {
110 return fmt.Errorf("unable to write private key file %s: %s", filename, err)
111 }
112
113 return nil
114}
115
116// SavePublicKey saves the given public key to the file.
117func SavePublicKey(filename string, key PublicKey) error {
118 var encodedKey []byte
119 var err error
120
121 if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
122 // Encode in JSON Web Key format.
123 encodedKey, err = json.MarshalIndent(key, "", " ")
124 if err != nil {
125 return fmt.Errorf("unable to encode public key JWK: %s", err)
126 }
127 } else {
128 // Encode in PEM format.
129 pemBlock, err := key.PEMBlock()
130 if err != nil {
131 return fmt.Errorf("unable to encode public key PEM: %s", err)
132 }
133 encodedKey = pem.EncodeToMemory(pemBlock)
134 }
135
136 err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0644))
137 if err != nil {
138 return fmt.Errorf("unable to write public key file %s: %s", filename, err)
139 }
140
141 return nil
142}
143
144// Public Key Set files
145
146type jwkSet struct {
147 Keys []json.RawMessage `json:"keys"`
148}
149
150// LoadKeySetFile loads a key set
151func LoadKeySetFile(filename string) ([]PublicKey, error) {
152 if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
153 return loadJSONKeySetFile(filename)
154 }
155
156 // Must be a PEM format file
157 return loadPEMKeySetFile(filename)
158}
159
160func loadJSONKeySetRaw(data []byte) ([]json.RawMessage, error) {
161 if len(data) == 0 {
162 // This is okay, just return an empty slice.
163 return []json.RawMessage{}, nil
164 }
165
166 keySet := jwkSet{}
167
168 err := json.Unmarshal(data, &keySet)
169 if err != nil {
170 return nil, fmt.Errorf("unable to decode JSON Web Key Set: %s", err)
171 }
172
173 return keySet.Keys, nil
174}
175
176func loadJSONKeySetFile(filename string) ([]PublicKey, error) {
177 contents, err := readKeyFileBytes(filename)
178 if err != nil && err != ErrKeyFileDoesNotExist {
179 return nil, err
180 }
181
182 return UnmarshalPublicKeyJWKSet(contents)
183}
184
185func loadPEMKeySetFile(filename string) ([]PublicKey, error) {
186 data, err := readKeyFileBytes(filename)
187 if err != nil && err != ErrKeyFileDoesNotExist {
188 return nil, err
189 }
190
191 return UnmarshalPublicKeyPEMBundle(data)
192}
193
194// AddKeySetFile adds a key to a key set
195func AddKeySetFile(filename string, key PublicKey) error {
196 if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
197 return addKeySetJSONFile(filename, key)
198 }
199
200 // Must be a PEM format file
201 return addKeySetPEMFile(filename, key)
202}
203
204func addKeySetJSONFile(filename string, key PublicKey) error {
205 encodedKey, err := json.Marshal(key)
206 if err != nil {
207 return fmt.Errorf("unable to encode trusted client key: %s", err)
208 }
209
210 contents, err := readKeyFileBytes(filename)
211 if err != nil && err != ErrKeyFileDoesNotExist {
212 return err
213 }
214
215 rawEntries, err := loadJSONKeySetRaw(contents)
216 if err != nil {
217 return err
218 }
219
220 rawEntries = append(rawEntries, json.RawMessage(encodedKey))
221 entriesWrapper := jwkSet{Keys: rawEntries}
222
223 encodedEntries, err := json.MarshalIndent(entriesWrapper, "", " ")
224 if err != nil {
225 return fmt.Errorf("unable to encode trusted client keys: %s", err)
226 }
227
228 err = ioutil.WriteFile(filename, encodedEntries, os.FileMode(0644))
229 if err != nil {
230 return fmt.Errorf("unable to write trusted client keys file %s: %s", filename, err)
231 }
232
233 return nil
234}
235
236func addKeySetPEMFile(filename string, key PublicKey) error {
237 // Encode to PEM, open file for appending, write PEM.
238 file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.FileMode(0644))
239 if err != nil {
240 return fmt.Errorf("unable to open trusted client keys file %s: %s", filename, err)
241 }
242 defer file.Close()
243
244 pemBlock, err := key.PEMBlock()
245 if err != nil {
246 return fmt.Errorf("unable to encoded trusted key: %s", err)
247 }
248
249 _, err = file.Write(pem.EncodeToMemory(pemBlock))
250 if err != nil {
251 return fmt.Errorf("unable to write trusted keys file: %s", err)
252 }
253
254 return nil
255}