OpenNHP/opennhp · error
decode private key
Error message
decode private key: %w
What it means
The `pubkey` subcommand of nhp-serverd decodes its first CLI argument as a base64 (std encoding) private key before deriving public keys. This error is wrapped when base64.StdEncoding.DecodeString fails — the argument is empty, contains non-base64 characters, or uses URL-safe/-padded variants that std decoding rejects.
Solutions
- Pass the exact std-base64 private key as a single quoted argument: nhp-serverd pubkey 'BASE64KEY'.
- Confirm the key is base64, not hex (32 bytes -> 44 std-base64 chars ending in '='); convert with `printf <hex> | xxd -r -p | base64` if needed.
- Strip quotes, whitespace, and newlines from the value before passing it.
- For URL-safe base64 input, convert padding/alphabet: tr '_-' '/+' first.
- If the key came from opennhp/demo, use the raw nhp_*_private_key field value verbatim.
Example fix
// before (script) nhp-serverd pubkey $NHP_SERVER_PRIVATE_KEY # unquoted, may break // after nhp-serverd pubkey "$NHP_SERVER_PRIVATE_KEY" # quoted single std-base64 argument
Defensive patterns
Strategy: validation
Validate before calling
key := c.Args().First()
if key == "" { return errors.New("missing private key argument") }
if _, err := base64.StdEncoding.DecodeString(key); err != nil {
return fmt.Errorf("private key must be std base64: %w", err)
} Type guard
func isStdBase64(s string) bool {
_, err := base64.StdEncoding.DecodeString(s)
return err == nil && len(s) >= 43
} Try / catch
if err := app.Run(os.Args); err != nil {
if strings.Contains(err.Error(), "decode private key") {
fmt.Fprintln(os.Stderr, "pass the private key as std base64, quoted: nhp-serverd pubkey '<key>'")
os.Exit(1)
}
panic(err)
} Prevention
- Always quote the key argument in shell scripts
- Verify the key is 44-char std base64 (32-byte key) before invoking
- Convert hex or URL-safe encodings before passing
- Never copy keys with trailing newlines or surrounding quotes from JSON/secret stores
When it happens
Trigger: Running `nhp-serverd pubkey` (or `pubkey --both`) with: no argument at all, a hex-encoded key, a URL-safe base64 string (with - and _), a key with whitespace/newlines or quotes copied from a terminal, or a truncated base64 string.
Common situations: Pasting a private key from Secrets Manager JSON with surrounding quotes; scripts passing unquoted values with shell mangling; mixing up std vs URL-safe base64 encodings; using the output of a different tool that emits hex.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- invalid input key
- AuthServiceId is required
- private key parse error
- invalid input key
- private key parse error
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/889e358f3de2a2ee.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/server/main/main.go:131
Name: "pubkey",
Usage: "derive public key(s) from an existing base64 private key",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "curve", Value: false, DisableDefaultText: true, Usage: "output curve25519 public key"},
&cli.BoolFlag{Name: "sm2", Value: false, DisableDefaultText: true, Usage: "output sm2 public key (default)"},
&cli.BoolFlag{Name: "both", Value: false, DisableDefaultText: true, Usage: "output both SM2 and Curve25519 public keys"},
&cli.BoolFlag{Name: "json", Value: false, DisableDefaultText: true, Usage: "output in JSON format"},
},
Action: func(c *cli.Context) error {
emitErr := func(err error) error {
if c.Bool("json") {
json.NewEncoder(os.Stdout).Encode(map[string]string{"error": err.Error()})
return nil
}
return err
}
privBytes, err := base64.StdEncoding.DecodeString(c.Args().First())
if err != nil {
return emitErr(fmt.Errorf("decode private key: %w", err))
}
if c.Bool("both") {
sm2 := core.ECDHFromKey(core.ECC_SM2, privBytes)
curve := core.ECDHFromKey(core.ECC_CURVE25519, privBytes)
if sm2 == nil || curve == nil {
return emitErr(fmt.Errorf("invalid input key"))
}
if c.Bool("json") {
json.NewEncoder(os.Stdout).Encode(map[string]string{
"sm2PublicKey": sm2.PublicKeyBase64(),
"curve25519PublicKey": curve.PublicKeyBase64(),
})
} else {
fmt.Println("SM2 public key: ", sm2.PublicKeyBase64())
fmt.Println("Curve25519 public key:", curve.PublicKeyBase64())
}
return nilView on GitHub (pinned to 6e04ca5ff0)