Created
May 19, 2021 12:51
-
-
Save vinsonzou/3576a4d20d3247ec0a1953d488e7d446 to your computer and use it in GitHub Desktop.
encrypt/decrypt by MacOS keychain
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "flag" | |
| "fmt" | |
| "os" | |
| "os/signal" | |
| "runtime" | |
| "syscall" | |
| "github.com/minio/sio" | |
| "github.com/zalando/go-keyring" | |
| "golang.org/x/crypto/scrypt" | |
| ) | |
| const ( | |
| codeOK int = iota // exit successfully | |
| codeError // exit because of error | |
| codeCancel // exit because of interrupt | |
| ) | |
| var ( | |
| cleanChan chan<- int // initialized in init | |
| cleanFn = make([]func(int), 0, 3) | |
| ) | |
| var ( | |
| listFlag bool | |
| decryptFlag bool | |
| ) | |
| func init() { | |
| // 设置flag参数 (变量指针,参数名,默认值,帮助信息) | |
| flag.BoolVar(&decryptFlag, "d", false, fmt.Sprintf("%-8s Decrypt", "")) | |
| flag.Usage = func() { | |
| printFlag := func(f *flag.Flag) { | |
| fmt.Fprintf(os.Stderr, " -%-6s %s\n", f.Name, f.Usage) | |
| } | |
| fmt.Fprintf(os.Stderr, "Usage: %s [FLAGS] [ARGUMENTS...]\n\n", os.Args[0]) | |
| flag.VisitAll(printFlag) | |
| os.Exit(codeOK) | |
| } | |
| cleanCh := make(chan int, 1) | |
| cleanChan = cleanCh | |
| go func() { | |
| code := <-cleanCh | |
| for _, f := range cleanFn { | |
| f(code) | |
| } | |
| os.Exit(code) | |
| }() | |
| // handle user termination | |
| sigChan := make(chan os.Signal, 1) | |
| signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) | |
| go func() { | |
| <-sigChan | |
| cleanChan <- codeCancel // try to exit gracefully | |
| runtime.Goexit() | |
| }() | |
| } | |
| func main() { | |
| flag.Parse() | |
| ciphersuite := []byte{sio.AES_256_GCM} | |
| in, out := parseIOArgs() | |
| key := deriveKey(out, in) | |
| cfg := sio.Config{Key: key, CipherSuites: ciphersuite} | |
| if decryptFlag { | |
| decrypt(out, in, cfg) | |
| } else { | |
| encrypt(out, in, cfg) | |
| } | |
| } | |
| func exit(code int) { | |
| cleanChan <- code | |
| runtime.Goexit() | |
| } | |
| func parseIOArgs() (*os.File, *os.File) { | |
| switch args := flag.Args(); len(args) { | |
| default: | |
| fmt.Fprintf(os.Stderr, "Unknown arguments: %s\n", args[2:]) | |
| exit(codeError) | |
| return nil, nil // make compiler happy | |
| case 0: | |
| return os.Stdin, os.Stdout | |
| case 1: | |
| in, err := os.Open(args[0]) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Failed to open '%s': %v\n", args[0], err) | |
| exit(codeError) | |
| } | |
| cleanFn = append(cleanFn, func(code int) { in.Close() }) | |
| return in, os.Stdout | |
| case 2: | |
| in, err := os.Open(args[0]) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Failed to open '%s': %v\n", args[0], err) | |
| exit(codeError) | |
| } | |
| out, err := os.Create(args[1]) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Failed to create '%s': %v\n", args[1], err) | |
| exit(codeError) | |
| } | |
| cleanFn = append(cleanFn, func(code int) { | |
| out.Close() | |
| if code != codeOK { // remove file on error | |
| os.Remove(out.Name()) | |
| } | |
| }) | |
| return in, out | |
| } | |
| } | |
| func deriveKey(dst, src *os.File) []byte { | |
| service := "go-app" | |
| user := "go-app" | |
| secret, err := keyring.Get(service, user) | |
| if err != nil { | |
| fmt.Fprintln(os.Stderr, "Failed to get secret from keychain") | |
| } | |
| password := []byte(secret) | |
| salt := make([]byte, 32) | |
| key, err := scrypt.Key(password, salt, 32768, 16, 1, 32) | |
| if err != nil { | |
| fmt.Fprintln(os.Stderr, "Failed to derive key from password and salt") | |
| exit(codeError) | |
| } | |
| return key | |
| } | |
| func encrypt(dst, src *os.File, cfg sio.Config) { | |
| if _, err := sio.Encrypt(dst, src, cfg); err != nil { | |
| fmt.Fprintf(os.Stderr, "Failed to encrypt: '%s'\n", src.Name()) | |
| exit(codeError) | |
| } | |
| } | |
| func decrypt(dst, src *os.File, cfg sio.Config) { | |
| if _, err := sio.Decrypt(dst, src, cfg); err != nil { | |
| fmt.Fprintf(os.Stderr, "Failed to decrypt: '%s'\n", src.Name()) | |
| exit(codeError) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment