aboutsummaryrefslogtreecommitdiff
path: root/cmd/ssh-cert-info
diff options
context:
space:
mode:
authorFranck Cuny <fcuny@roblox.com>2025-09-06 13:05:00 -0700
committerFranck Cuny <fcuny@roblox.com>2025-09-06 13:05:00 -0700
commit34ad93bfff38348318575ac044c1711f05eae78b (patch)
tree2b7336e63e40fbb162efe88c6f56fd110f30d6e0 /cmd/ssh-cert-info
parentadd a tool to check validity of SSH certificates (diff)
downloadx-34ad93bfff38348318575ac044c1711f05eae78b.tar.gz
ssh-cert-info in go
Diffstat (limited to 'cmd/ssh-cert-info')
-rw-r--r--cmd/ssh-cert-info/README.org1
-rw-r--r--cmd/ssh-cert-info/main.go162
2 files changed, 163 insertions, 0 deletions
diff --git a/cmd/ssh-cert-info/README.org b/cmd/ssh-cert-info/README.org
new file mode 100644
index 0000000..9d10f5f
--- /dev/null
+++ b/cmd/ssh-cert-info/README.org
@@ -0,0 +1 @@
+A quick way to check information about various SSH certificates on my machine.
diff --git a/cmd/ssh-cert-info/main.go b/cmd/ssh-cert-info/main.go
new file mode 100644
index 0000000..957dd93
--- /dev/null
+++ b/cmd/ssh-cert-info/main.go
@@ -0,0 +1,162 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "golang.org/x/crypto/ssh"
+)
+
+func main() {
+ sshDir, err := getSSHDirectory()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error getting SSH directory: %v\n", err)
+ os.Exit(1)
+ }
+
+ certFiles, err := findCertificateFiles(sshDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error reading SSH directory %s: %v\n", sshDir, err)
+ os.Exit(1)
+ }
+
+ if len(certFiles) == 0 {
+ fmt.Printf("No SSH certificates found in %s\n", sshDir)
+ return
+ }
+
+ fmt.Println("SSH Certificate Report")
+ fmt.Println("======================")
+ fmt.Println()
+
+ for _, certPath := range certFiles {
+ checkCertificate(certPath)
+ fmt.Println()
+ }
+}
+
+func getSSHDirectory() (string, error) {
+ home, err := os.UserHomeDir()
+ if err != nil {
+ return "", fmt.Errorf("failed to get home directory: %w", err)
+ }
+ return filepath.Join(home, ".ssh"), nil
+}
+
+func findCertificateFiles(sshDir string) ([]string, error) {
+ entries, err := os.ReadDir(sshDir)
+ if err != nil {
+ return nil, err
+ }
+
+ var certFiles []string
+ for _, entry := range entries {
+ if entry.IsDir() {
+ continue
+ }
+
+ name := entry.Name()
+ if strings.HasSuffix(name, ".pub") && strings.Contains(name, "-cert.pub") {
+ certFiles = append(certFiles, filepath.Join(sshDir, name))
+ }
+ }
+
+ return certFiles, nil
+}
+
+func checkCertificate(certPath string) {
+ fmt.Printf("Certificate: %s\n", certPath)
+
+ certData, err := os.ReadFile(certPath)
+ if err != nil {
+ fmt.Printf(" Error: Failed to read certificate file: %v\n", err)
+ return
+ }
+
+ pubKey, _, _, _, err := ssh.ParseAuthorizedKey(certData)
+ if err != nil {
+ fmt.Printf(" Error: Failed to parse certificate: %v\n", err)
+ return
+ }
+
+ cert, ok := pubKey.(*ssh.Certificate)
+ if !ok {
+ fmt.Printf(" Error: Not an SSH certificate\n")
+ return
+ }
+
+ displayCertificateInfo(cert)
+}
+
+func displayCertificateInfo(cert *ssh.Certificate) {
+ certType := "Unknown"
+ switch cert.CertType {
+ case ssh.UserCert:
+ certType = "ssh-ed25519-cert-v01@openssh.com user certificate"
+ case ssh.HostCert:
+ certType = "ssh-ed25519-cert-v01@openssh.com host certificate"
+ }
+ fmt.Printf(" Type: %s\n", certType)
+
+ fmt.Printf(" Public key: %s %s\n", cert.Key.Type(), cert.KeyId)
+
+ fmt.Printf(" Signing CA: %s\n", cert.SignatureKey.Type())
+
+ fmt.Printf(" Key ID: %s\n", cert.KeyId)
+
+ fmt.Printf(" Serial: %d\n", cert.Serial)
+
+ validFrom := time.Unix(int64(cert.ValidAfter), 0)
+ validTo := time.Unix(int64(cert.ValidBefore), 0)
+
+ fmt.Printf(" Valid from: %s\n", validFrom.Format("2006-01-02T15:04:05"))
+
+ if cert.ValidBefore == ssh.CertTimeInfinity {
+ fmt.Printf(" Valid to: forever\n")
+ fmt.Println(" ✓ Certificate is currently valid")
+ } else {
+ fmt.Printf(" Valid to: %s\n", validTo.Format("2006-01-02T15:04:05"))
+ checkCertificateExpiration(validTo)
+ }
+
+ if len(cert.ValidPrincipals) > 0 {
+ fmt.Println(" Principals:")
+ for _, principal := range cert.ValidPrincipals {
+ fmt.Printf(" %s\n", principal)
+ }
+ }
+
+ if len(cert.CriticalOptions) > 0 {
+ fmt.Println(" Critical Options:")
+ for key, value := range cert.CriticalOptions {
+ if value == "" {
+ fmt.Printf(" %s\n", key)
+ } else {
+ fmt.Printf(" %s %s\n", key, value)
+ }
+ }
+ }
+
+ if len(cert.Extensions) > 0 {
+ fmt.Println(" Extensions:")
+ for key, value := range cert.Extensions {
+ if value == "" {
+ fmt.Printf(" %s\n", key)
+ } else {
+ fmt.Printf(" %s %s\n", key, value)
+ }
+ }
+ }
+}
+
+func checkCertificateExpiration(validTo time.Time) {
+ now := time.Now()
+ if validTo.Before(now) {
+ fmt.Println(" ⚠️ WARNING: Certificate has EXPIRED!")
+ } else {
+ fmt.Println(" ✓ Certificate is currently valid")
+ }
+}