A Go package for encrypting and decrypting JSON data with a generic key-value store.
- Generic Store[T]: Type-safe key-value store that works with any Go struct
- AES-256-GCM encryption: Industry-standard authenticated encryption
- Argon2 key derivation: Secure password-based key derivation
- Multiple key sources: Environment variables, files, or direct strings
- Simple API: Easy to use encryption/decryption functions
go get github.com/skabbio1976/jsoncryptpackage main
import (
"fmt"
"github.com/skabbio1976/jsoncrypt"
)
type Config struct {
APIKey string `json:"api_key"`
Endpoint string `json:"endpoint"`
EnableSSL bool `json:"enable_ssl"`
}
func main() {
// Create a new store
store := jsoncrypt.NewStore[Config]()
// Add items
store.Add("production", Config{
APIKey: "secret-key-123",
Endpoint: "https://api.example.com",
EnableSSL: true,
})
// Get items
if cfg, ok := store.Get("production"); ok {
fmt.Printf("Config: %+v\n", cfg)
}
// List all keys
names := store.ListNames()
fmt.Println("Keys:", names)
}// Save encrypted with password
password := "my-secure-password"
err := store.SaveEncryptedWithPassword("/tmp/config.enc", password)
if err != nil {
panic(err)
}
// Load encrypted store
loaded, err := jsoncrypt.LoadEncryptedStoreWithPassword[Config]("/tmp/config.enc", password)
if err != nil {
panic(err)
}For production use, it's better to use KeySource instead of hardcoded passwords:
// Generate a key once and store it securely
key, err := jsoncrypt.GenerateEncryptionKey()
if err != nil {
panic(err)
}
fmt.Println("Save this key securely:", key)
// Use environment variable
ks := jsoncrypt.KeySourceEnv("ENCRYPTION_KEY")
// Or use a key file
// ks := jsoncrypt.KeySourceFile("/secure/path/to/key")
// Or use direct key
// ks := jsoncrypt.KeySourceDirect(key)
// Save encrypted
err = store.SaveEncrypted("/var/data/config.enc", ks)
if err != nil {
panic(err)
}
// Load encrypted
loaded, err := jsoncrypt.LoadEncryptedStore[Config]("/var/data/config.enc", ks)
if err != nil {
panic(err)
}You can also encrypt individual strings:
// With password
encrypted, err := jsoncrypt.EncryptStringWithPassword("sensitive data", "password123")
if err != nil {
panic(err)
}
decrypted, err := jsoncrypt.DecryptStringWithPassword(encrypted, "password123")
if err != nil {
panic(err)
}
fmt.Println("Decrypted:", decrypted)
// With KeySource
ks := jsoncrypt.KeySourceEnv("ENCRYPTION_KEY")
encrypted, err = jsoncrypt.EncryptString("sensitive data", ks)
if err != nil {
panic(err)
}
decrypted, err = jsoncrypt.DecryptString(encrypted, ks)
if err != nil {
panic(err)
}Here's how to use jsoncrypt for storing vCenter credentials (the original use case):
package main
import (
"fmt"
"github.com/skabbio1976/jsoncrypt"
)
type Credential struct {
Server string `json:"server"`
Username string `json:"username"`
Password string `json:"password"`
Insecure bool `json:"insecure"`
}
func main() {
// Create credential store
store := jsoncrypt.NewStore[Credential]()
// Add credentials
store.Add("vcenter-prod", Credential{
Server: "vcenter.example.com",
Username: "admin@vsphere.local",
Password: "super-secret",
Insecure: false,
})
store.Add("vcenter-dev", Credential{
Server: "vcenter-dev.example.com",
Username: "admin@vsphere.local",
Password: "dev-password",
Insecure: true,
})
// Save encrypted with KeySource
ks := jsoncrypt.KeySourceEnv("VCENTER_ENCRYPTION_KEY")
err := store.SaveEncrypted("/etc/vcenter/credentials.enc", ks)
if err != nil {
panic(err)
}
// Later, load credentials
loaded, err := jsoncrypt.LoadEncryptedStore[Credential]("/etc/vcenter/credentials.enc", ks)
if err != nil {
panic(err)
}
// Use credentials
if cred, ok := loaded.Get("vcenter-prod"); ok {
fmt.Printf("Connecting to %s as %s\n", cred.Server, cred.Username)
}
// List all stored credentials
fmt.Println("Available credentials:", loaded.ListNames())
}- Key Storage: Never hardcode encryption keys. Use environment variables, secure key files, or a key management service.
- File Permissions: Encrypted files are created with 0600 permissions (owner read/write only).
- Argon2 Parameters: Uses conservative parameters (time=2, memory=64MB, threads=1) suitable for most applications.
- AES-256-GCM: Provides both confidentiality and authenticity.
- Random Salts: Each encryption uses a unique random salt, preventing rainbow table attacks.
NewStore[T]() *Store[T]- Create a new empty storeAdd(name string, item T)- Add or update an itemGet(name string) (T, bool)- Get an item by nameDelete(name string)- Remove an itemListNames() []string- Get all keys sorted alphabeticallySavePlaintext(path string) error- Save as unencrypted JSONSaveEncrypted(path string, ks KeySource) error- Save encrypted with KeySourceSaveEncryptedWithPassword(path string, password string) error- Save encrypted with password
LoadPlaintextStore[T](path string) (*Store[T], error)- Load unencrypted storeLoadEncryptedStore[T](path string, ks KeySource) (*Store[T], error)- Load encrypted store with KeySourceLoadEncryptedStoreWithPassword[T](path string, password string) (*Store[T], error)- Load encrypted store with passwordEncryptString(plaintext string, ks KeySource) (string, error)- Encrypt a string with KeySourceDecryptString(encrypted string, ks KeySource) (string, error)- Decrypt a string with KeySourceEncryptStringWithPassword(plaintext string, password string) (string, error)- Encrypt with passwordDecryptStringWithPassword(encrypted string, password string) (string, error)- Decrypt with passwordGenerateEncryptionKey() (string, error)- Generate a random 32-byte hex key
KeySourceEnv(name string) KeySource- Read key from environment variableKeySourceFile(path string) KeySource- Read key from fileKeySourceDirect(secret string) KeySource- Use provided string as key
MIT