256 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
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
 | 
						|
}
 |