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") } }