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 }