()
| 100 | } |
| 101 | |
| 102 | func cmdImport() { |
| 103 | fs := flag.NewFlagSet("import", flag.ExitOnError) |
| 104 | alias := fs.String("alias", "", "alias name") |
| 105 | file := fs.String("file", "", "path to private key file") |
| 106 | pass := fs.String("passphrase", "", "DEPRECATED: passphrase in CLI may leak; prefer --ask-passphrase or --passphrase-file or --passphrase-stdin") |
| 107 | ask := fs.Bool("ask-passphrase", false, "read passphrase securely from TTY") |
| 108 | passFile := fs.String("passphrase-file", "", "read passphrase from file path") |
| 109 | passStdin := fs.Bool("passphrase-stdin", false, "read passphrase from stdin") |
| 110 | comment := fs.String("comment", "", "optional comment") |
| 111 | fs.Parse(os.Args[2:]) |
| 112 | |
| 113 | if *alias == "" || *file == "" { |
| 114 | fatal(errors.New("alias and file are required")) |
| 115 | } |
| 116 | |
| 117 | // 1. 读取私钥文件 |
| 118 | b, err := os.ReadFile(*file) |
| 119 | if err != nil { |
| 120 | fatal(err) |
| 121 | } |
| 122 | |
| 123 | // 2. 先询问私钥密码 |
| 124 | p, err := resolvePassphrase(*pass, *ask, *passFile, *passStdin, "Input key passphrase: ") |
| 125 | if err != nil { fatal(err) } |
| 126 | |
| 127 | // 3. 解密私钥并创建记录(验证私钥和密码是否正确) |
| 128 | rec, err := store.NewRecordFromPrivateKeyBytes(*alias, b, p, *comment) |
| 129 | if err != nil { |
| 130 | fatal(err) |
| 131 | } |
| 132 | |
| 133 | // 4. 私钥验证成功后,再要求 Touch ID 获取 master key |
| 134 | mk, err := keychain.LoadMasterKey() |
| 135 | if err != nil { |
| 136 | fatal(err) |
| 137 | } |
| 138 | |
| 139 | // 5. 用 master key 加密并保存私钥 |
| 140 | if err := store.SaveEncryptedRecord(rec, mk); err != nil { |
| 141 | fatal(err) |
| 142 | } |
| 143 | fmt.Printf("imported %s fingerprint=%s\n", rec.Alias, rec.Fingerprint) |
| 144 | } |
| 145 | |
| 146 | func cmdList() { |
| 147 | dir := store.KeysDir() |
no test coverage detected