From 06ebcacb912dd3a4a1e7b646470d4e130b416a49 Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Wed, 8 Apr 2026 10:28:53 -0700 Subject: [PATCH 1/8] tools: Add certificates to sec key-list output --- tools/sec/keychain.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/sec/keychain.go b/tools/sec/keychain.go index 0e495425..0d5dd6f3 100644 --- a/tools/sec/keychain.go +++ b/tools/sec/keychain.go @@ -26,7 +26,7 @@ func (t *ToolKeychain) configure(cmd *cobra.Command) { cmd.AddCommand(&cobra.Command{ GroupID: "keychain", Use: "key-list KEYCHAIN-URI", - Short: "List keys in a keychain", + Short: "List keys and certs in a keychain", Run: t.List, Args: cobra.ExactArgs(1), Example: ` ndnd sec key-list dir:///safe/keys`, @@ -76,7 +76,7 @@ and the default key of the identity will be exported.`, }) } -// (AI GENERATED DESCRIPTION): Lists all identities and their keys in the keychain at the given path, printing each identity name followed by the names of its keys. +// Lists all identities, their keys, and the associated certs in the keychain at the given URI, printing each identity name followed by the names of its keys, then its names of its certs func (*ToolKeychain) List(_ *cobra.Command, args []string) { kc, err := keychain.NewKeyChain(args[0], storage.NewMemoryStore()) if err != nil { @@ -89,6 +89,9 @@ func (*ToolKeychain) List(_ *cobra.Command, args []string) { fmt.Printf("%s\n", id.Name()) for _, key := range id.Keys() { fmt.Printf("==> %s\n", key.KeyName()) + for _, cert := range key.UniqueCerts() { + fmt.Printf(" ==> %s\n", cert) + } } } } From a5d8840b5ad9edbe158f58d34ebe72ea9bc55dc0 Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Wed, 8 Apr 2026 16:45:48 -0700 Subject: [PATCH 2/8] tools: Add sec cert-export --- tools/sec/keychain.go | 47 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/tools/sec/keychain.go b/tools/sec/keychain.go index 0d5dd6f3..2b274e07 100644 --- a/tools/sec/keychain.go +++ b/tools/sec/keychain.go @@ -74,6 +74,16 @@ and the default key of the identity will be exported.`, Example: ` ndnd sec key-export dir:///safe/keys /alice`, Run: t.Export, }) + + cmd.AddCommand(&cobra.Command{ + GroupID: "keychain", + Use: "cert-export KEYCHAIN-URI CERT-NAME", + Short: "Export a certificate from a keychain", + Long: `Export the specified certificate from a keychain.`, + Args: cobra.ExactArgs(2), + Example: ` ndnd sec cert-export dir:///safe/keys /alice/KEY/~%E8t%A5%A3V%88%81/NA/v=0`, + Run: t.ExportCert, + }) } // Lists all identities, their keys, and the associated certs in the keychain at the given URI, printing each identity name followed by the names of its keys, then its names of its certs @@ -96,7 +106,7 @@ func (*ToolKeychain) List(_ *cobra.Command, args []string) { } } -// (AI GENERATED DESCRIPTION): Imports keychain entries from standard input into the keychain named by the first argument, storing them in a memory-based keychain. +// Imports keychain entries from standard input into the keychain named by the first argument func (*ToolKeychain) Import(_ *cobra.Command, args []string) { kc, err := keychain.NewKeyChain(args[0], storage.NewMemoryStore()) if err != nil { @@ -120,7 +130,7 @@ func (*ToolKeychain) Import(_ *cobra.Command, args []string) { } } -// (AI GENERATED DESCRIPTION): Exports a specified key (or an identity’s default key) from a keychain, PEM‑encodes its secret key, and writes it to standard output. +// Exports a specified key (or an identity’s default key) from a keychain, PEM‑encodes its secret key, and writes it to standard output. func (*ToolKeychain) Export(_ *cobra.Command, args []string) { name, err := enc.NameFromStr(args[1]) if err != nil { @@ -135,7 +145,7 @@ func (*ToolKeychain) Export(_ *cobra.Command, args []string) { os.Exit(1) return } - + keyName := name id, err := security.GetIdentityFromKeyName(name) if err != nil { // not a key name @@ -208,6 +218,37 @@ func (*ToolKeychain) DeleteKey(_ *cobra.Command, args []string) { } } +// Exports a specified certificate from a keychain, PEM‑encodes it, and writes it to standard output. +func (*ToolKeychain) ExportCert(_ *cobra.Command, args []string) { + name, err := enc.NameFromStr(args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "Invalid certificate name: %s\n", args[1]) + os.Exit(1) + return + } + + kc, err := keychain.NewKeyChain(args[0], storage.NewMemoryStore()) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to open keychain: %s\n", err) + os.Exit(1) + return + } + + wire, err := kc.Store().Get(name.Prefix(-1), true) + if err != nil || wire == nil { + fmt.Fprintf(os.Stderr, "Certificate not found: %s\n", name) + } + + out, err := security.PemEncode(enc.Wire{wire}.Join()) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to convert certificate to text: %s\n", err) + os.Exit(1) + return + } + + os.Stdout.Write(out) +} + // DeleteCert removes a certificate from the keychain. func (*ToolKeychain) DeleteCert(_ *cobra.Command, args []string) { name, err := enc.NameFromStr(args[1]) From c1d6ed795f2e5f47da459d24cdbfb725d68ea11c Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Thu, 9 Apr 2026 11:59:33 -0700 Subject: [PATCH 3/8] docs: security-util fix sign-cert examples, add cert-export --- docs/security-util.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/security-util.md b/docs/security-util.md index 7e1551b6..b945ee15 100644 --- a/docs/security-util.md +++ b/docs/security-util.md @@ -29,19 +29,19 @@ ndnd sec sign-cert alice.key < alice.key > alice.cert # Generate a key for /ndn/bob and create a certificate signed by /ndn/alice # Sets the issuer field to ALICE ndnd sec keygen /ndn/bob rsa 2048 > bob.key -ndnd sec sign-cert -issuer ALICE alice.key < bob.key > bob1.cert +ndnd sec sign-cert --issuer ALICE alice.key < bob.key > bob1.cert # Create a certificate with a defined validity period -ndnd sec sign-cert -issuer ALICE -start 20150101000000 -end 20350101000000 alice.key < bob.key > bob2.cert +ndnd sec sign-cert --issuer ALICE --start 20150101000000 --end 20350101000000 alice.key < bob.key > bob2.cert # Re-sign the public key in a certificate (or CSR) with a provided key ndnd sec keygen /ndn/carol ed25519 > carol.key -ndnd sec sign-cert -issuer CAROL carol.key < bob1.cert > bob3.cert +ndnd sec sign-cert --issuer CAROL carol.key < bob1.cert > bob3.cert ``` ## `ndnd sec key-list` -List all keys in the keychain. +List all keys and certs in the keychain. ```bash # List all keys in the keychain directory /etc/app/keys @@ -83,6 +83,15 @@ Delete a key (and its certificates) from the keychain. ndnd sec key-delete dir:///etc/app/keys /ndn/bob/KEY/%A6%0Ei%1F%A8J%D4%8E ``` +## `ndnd sec cert-export` + +Export a certificate from the keychain. + +```bash +# Export a specific certificate from a keychain +ndnd sec cert-delete dir:///etc/app/keys /ndn/bob/KEY/%A6%0Ei%1F%A8J%D4%8E/self/v=1 +``` + ## `ndnd sec cert-delete` Delete a certificate from the keychain. From 05725edae25d12c9d7f6f7d4d802a63a1d5c4645 Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Thu, 9 Apr 2026 15:07:16 -0700 Subject: [PATCH 4/8] fix lint --- tools/sec/keychain.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/sec/keychain.go b/tools/sec/keychain.go index 2b274e07..d69ccfba 100644 --- a/tools/sec/keychain.go +++ b/tools/sec/keychain.go @@ -79,7 +79,6 @@ and the default key of the identity will be exported.`, GroupID: "keychain", Use: "cert-export KEYCHAIN-URI CERT-NAME", Short: "Export a certificate from a keychain", - Long: `Export the specified certificate from a keychain.`, Args: cobra.ExactArgs(2), Example: ` ndnd sec cert-export dir:///safe/keys /alice/KEY/~%E8t%A5%A3V%88%81/NA/v=0`, Run: t.ExportCert, @@ -145,7 +144,7 @@ func (*ToolKeychain) Export(_ *cobra.Command, args []string) { os.Exit(1) return } - + keyName := name id, err := security.GetIdentityFromKeyName(name) if err != nil { // not a key name From 630250cda59a80ced77166c4419dac3e8036b6a7 Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Mon, 13 Apr 2026 09:15:03 -0700 Subject: [PATCH 5/8] correct cert-export example --- docs/security-util.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/security-util.md b/docs/security-util.md index b945ee15..1ca7b2ec 100644 --- a/docs/security-util.md +++ b/docs/security-util.md @@ -89,7 +89,7 @@ Export a certificate from the keychain. ```bash # Export a specific certificate from a keychain -ndnd sec cert-delete dir:///etc/app/keys /ndn/bob/KEY/%A6%0Ei%1F%A8J%D4%8E/self/v=1 +ndnd sec cert-export dir:///etc/app/keys /ndn/bob/KEY/%A6%0Ei%1F%A8J%D4%8E/self/v=1 ``` ## `ndnd sec cert-delete` From 1ce6189aff19e5778a4cccda4037f7d3d2ced7aa Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Mon, 13 Apr 2026 09:15:47 -0700 Subject: [PATCH 6/8] fix certificate error return --- tools/sec/keychain.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/sec/keychain.go b/tools/sec/keychain.go index d69ccfba..cbba3d47 100644 --- a/tools/sec/keychain.go +++ b/tools/sec/keychain.go @@ -236,6 +236,8 @@ func (*ToolKeychain) ExportCert(_ *cobra.Command, args []string) { wire, err := kc.Store().Get(name.Prefix(-1), true) if err != nil || wire == nil { fmt.Fprintf(os.Stderr, "Certificate not found: %s\n", name) + os.Exit(1) + return } out, err := security.PemEncode(enc.Wire{wire}.Join()) From 29cb0c3789d2e2b76a1710b1008a4cb631c23f46 Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Mon, 13 Apr 2026 11:48:10 -0700 Subject: [PATCH 7/8] make cert-export not remove last component unless necessary --- tools/sec/keychain.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/sec/keychain.go b/tools/sec/keychain.go index cbba3d47..f2444265 100644 --- a/tools/sec/keychain.go +++ b/tools/sec/keychain.go @@ -233,7 +233,15 @@ func (*ToolKeychain) ExportCert(_ *cobra.Command, args []string) { return } - wire, err := kc.Store().Get(name.Prefix(-1), true) + if name.At(-1).String() == "v=0" { + name = name.Prefix(-1) + } + + wire, err := kc.Store().Get(name, false) + if err == nil && wire == nil { + wire, err = kc.Store().Get(name, true) + } + if err != nil || wire == nil { fmt.Fprintf(os.Stderr, "Certificate not found: %s\n", name) os.Exit(1) From bf7b3d7a7ed83b59e218d91d255fe2f328b9fde9 Mon Sep 17 00:00:00 2001 From: Craig Gu Date: Mon, 13 Apr 2026 11:52:07 -0700 Subject: [PATCH 8/8] cert-export digest component check --- tools/sec/keychain.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/sec/keychain.go b/tools/sec/keychain.go index f2444265..0ede0dd9 100644 --- a/tools/sec/keychain.go +++ b/tools/sec/keychain.go @@ -233,6 +233,10 @@ func (*ToolKeychain) ExportCert(_ *cobra.Command, args []string) { return } + if name.At(-1).Typ == enc.TypeImplicitSha256DigestComponent { + name = name.Prefix(-1) + } + if name.At(-1).String() == "v=0" { name = name.Prefix(-1) }