123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- package libtrust
- import (
- "encoding/json"
- "encoding/pem"
- "errors"
- "fmt"
- "io/ioutil"
- "os"
- "strings"
- )
- var (
- // ErrKeyFileDoesNotExist indicates that the private key file does not exist.
- ErrKeyFileDoesNotExist = errors.New("key file does not exist")
- )
- func readKeyFileBytes(filename string) ([]byte, error) {
- data, err := ioutil.ReadFile(filename)
- if err != nil {
- if os.IsNotExist(err) {
- err = ErrKeyFileDoesNotExist
- } else {
- err = fmt.Errorf("unable to read key file %s: %s", filename, err)
- }
- return nil, err
- }
- return data, nil
- }
- /*
- Loading and Saving of Public and Private Keys in either PEM or JWK format.
- */
- // LoadKeyFile opens the given filename and attempts to read a Private Key
- // encoded in either PEM or JWK format (if .json or .jwk file extension).
- func LoadKeyFile(filename string) (PrivateKey, error) {
- contents, err := readKeyFileBytes(filename)
- if err != nil {
- return nil, err
- }
- var key PrivateKey
- if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
- key, err = UnmarshalPrivateKeyJWK(contents)
- if err != nil {
- return nil, fmt.Errorf("unable to decode private key JWK: %s", err)
- }
- } else {
- key, err = UnmarshalPrivateKeyPEM(contents)
- if err != nil {
- return nil, fmt.Errorf("unable to decode private key PEM: %s", err)
- }
- }
- return key, nil
- }
- // LoadPublicKeyFile opens the given filename and attempts to read a Public Key
- // encoded in either PEM or JWK format (if .json or .jwk file extension).
- func LoadPublicKeyFile(filename string) (PublicKey, error) {
- contents, err := readKeyFileBytes(filename)
- if err != nil {
- return nil, err
- }
- var key PublicKey
- if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
- key, err = UnmarshalPublicKeyJWK(contents)
- if err != nil {
- return nil, fmt.Errorf("unable to decode public key JWK: %s", err)
- }
- } else {
- key, err = UnmarshalPublicKeyPEM(contents)
- if err != nil {
- return nil, fmt.Errorf("unable to decode public key PEM: %s", err)
- }
- }
- return key, nil
- }
- // SaveKey saves the given key to a file using the provided filename.
- // This process will overwrite any existing file at the provided location.
- func SaveKey(filename string, key PrivateKey) error {
- var encodedKey []byte
- var err error
- if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
- // Encode in JSON Web Key format.
- encodedKey, err = json.MarshalIndent(key, "", " ")
- if err != nil {
- return fmt.Errorf("unable to encode private key JWK: %s", err)
- }
- } else {
- // Encode in PEM format.
- pemBlock, err := key.PEMBlock()
- if err != nil {
- return fmt.Errorf("unable to encode private key PEM: %s", err)
- }
- encodedKey = pem.EncodeToMemory(pemBlock)
- }
- err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0600))
- if err != nil {
- return fmt.Errorf("unable to write private key file %s: %s", filename, err)
- }
- return nil
- }
- // SavePublicKey saves the given public key to the file.
- func SavePublicKey(filename string, key PublicKey) error {
- var encodedKey []byte
- var err error
- if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
- // Encode in JSON Web Key format.
- encodedKey, err = json.MarshalIndent(key, "", " ")
- if err != nil {
- return fmt.Errorf("unable to encode public key JWK: %s", err)
- }
- } else {
- // Encode in PEM format.
- pemBlock, err := key.PEMBlock()
- if err != nil {
- return fmt.Errorf("unable to encode public key PEM: %s", err)
- }
- encodedKey = pem.EncodeToMemory(pemBlock)
- }
- err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0644))
- if err != nil {
- return fmt.Errorf("unable to write public key file %s: %s", filename, err)
- }
- return nil
- }
- // Public Key Set files
- type jwkSet struct {
- Keys []json.RawMessage `json:"keys"`
- }
- // LoadKeySetFile loads a key set
- func LoadKeySetFile(filename string) ([]PublicKey, error) {
- if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
- return loadJSONKeySetFile(filename)
- }
- // Must be a PEM format file
- return loadPEMKeySetFile(filename)
- }
- func loadJSONKeySetRaw(data []byte) ([]json.RawMessage, error) {
- if len(data) == 0 {
- // This is okay, just return an empty slice.
- return []json.RawMessage{}, nil
- }
- keySet := jwkSet{}
- err := json.Unmarshal(data, &keySet)
- if err != nil {
- return nil, fmt.Errorf("unable to decode JSON Web Key Set: %s", err)
- }
- return keySet.Keys, nil
- }
- func loadJSONKeySetFile(filename string) ([]PublicKey, error) {
- contents, err := readKeyFileBytes(filename)
- if err != nil && err != ErrKeyFileDoesNotExist {
- return nil, err
- }
- return UnmarshalPublicKeyJWKSet(contents)
- }
- func loadPEMKeySetFile(filename string) ([]PublicKey, error) {
- data, err := readKeyFileBytes(filename)
- if err != nil && err != ErrKeyFileDoesNotExist {
- return nil, err
- }
- return UnmarshalPublicKeyPEMBundle(data)
- }
- // AddKeySetFile adds a key to a key set
- func AddKeySetFile(filename string, key PublicKey) error {
- if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
- return addKeySetJSONFile(filename, key)
- }
- // Must be a PEM format file
- return addKeySetPEMFile(filename, key)
- }
- func addKeySetJSONFile(filename string, key PublicKey) error {
- encodedKey, err := json.Marshal(key)
- if err != nil {
- return fmt.Errorf("unable to encode trusted client key: %s", err)
- }
- contents, err := readKeyFileBytes(filename)
- if err != nil && err != ErrKeyFileDoesNotExist {
- return err
- }
- rawEntries, err := loadJSONKeySetRaw(contents)
- if err != nil {
- return err
- }
- rawEntries = append(rawEntries, json.RawMessage(encodedKey))
- entriesWrapper := jwkSet{Keys: rawEntries}
- encodedEntries, err := json.MarshalIndent(entriesWrapper, "", " ")
- if err != nil {
- return fmt.Errorf("unable to encode trusted client keys: %s", err)
- }
- err = ioutil.WriteFile(filename, encodedEntries, os.FileMode(0644))
- if err != nil {
- return fmt.Errorf("unable to write trusted client keys file %s: %s", filename, err)
- }
- return nil
- }
- func addKeySetPEMFile(filename string, key PublicKey) error {
- // Encode to PEM, open file for appending, write PEM.
- file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.FileMode(0644))
- if err != nil {
- return fmt.Errorf("unable to open trusted client keys file %s: %s", filename, err)
- }
- defer file.Close()
- pemBlock, err := key.PEMBlock()
- if err != nil {
- return fmt.Errorf("unable to encoded trusted key: %s", err)
- }
- _, err = file.Write(pem.EncodeToMemory(pemBlock))
- if err != nil {
- return fmt.Errorf("unable to write trusted keys file: %s", err)
- }
- return nil
- }
|