|
| 1 | +--- |
| 2 | +title: View all certificates in User store |
| 3 | +date: 2025-11-26 17:04:00 +0100 |
| 4 | +categories: [dotnet, certificates] |
| 5 | +tags: [dotnet, certificates, thumbprints] # TAG names should always be lowercase |
| 6 | +--- |
| 7 | + |
| 8 | +This short post shows how to list certificates from the Current User (Personal) certificate store using C#. The code below prints a table with certificate thumbprints and the certificate common name (CN). It uses `Spectre.Console` to render a friendly table but the certificate logic itself does not depend on that library. |
| 9 | + |
| 10 | +Usage notes: |
| 11 | + |
| 12 | +- The snippet reads from the Current User personal store (`StoreName.My`, `StoreLocation.CurrentUser`). |
| 13 | +- Thumbprints are printed in-dim to make them easy to copy; CN is truncated to 40 characters for readability. |
| 14 | + |
| 15 | +```charp |
| 16 | +#: package Spectre.Console@0.49.1 |
| 17 | +
|
| 18 | +using System; |
| 19 | +using System.Security.Cryptography.X509Certificates; |
| 20 | +using Spectre.Console; |
| 21 | +using System.Text.RegularExpressions; |
| 22 | +using System.Linq; |
| 23 | +
|
| 24 | +// Query the Current User - Personal certificate store |
| 25 | +var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); |
| 26 | +
|
| 27 | +try |
| 28 | +{ |
| 29 | + // Open the certificate store in read-only mode |
| 30 | + store.Open(OpenFlags.ReadOnly); |
| 31 | + |
| 32 | + // Get all certificates from the store |
| 33 | + var certificates = store.Certificates; |
| 34 | + |
| 35 | + if (certificates.Count == 0) |
| 36 | + { |
| 37 | + AnsiConsole.MarkupLine("[yellow]No certificates found in the Current User - Personal store.[/]"); |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + // Create a table to display the certificates |
| 42 | + var table = new Table(); |
| 43 | + table.AddColumn("[bold]Thumbprint[/]"); |
| 44 | + table.AddColumn("[bold]Common Name (CN)[/]"); |
| 45 | + |
| 46 | + // Sort certificates alphabetically by thumbprint and add to table |
| 47 | + foreach (X509Certificate2 cert in certificates.Cast<X509Certificate2>().OrderBy(c => c.Thumbprint)) |
| 48 | + { |
| 49 | + // Extract CN from subject using regex |
| 50 | + var subject = cert.Subject ?? ""; |
| 51 | + var cnMatch = Regex.Match(subject, @"CN=([^,]+)"); |
| 52 | + var commonName = cnMatch.Success ? cnMatch.Groups[1].Value.Trim() : "N/A"; |
| 53 | + |
| 54 | + // Trim CN to 40 characters |
| 55 | + if (commonName.Length > 40) |
| 56 | + { |
| 57 | + commonName = commonName.Substring(0, 40) + "..."; |
| 58 | + } |
| 59 | + |
| 60 | + var thumbprint = cert.Thumbprint ?? "N/A"; |
| 61 | + |
| 62 | + table.AddRow( |
| 63 | + $"[dim]{thumbprint}[/]", |
| 64 | + commonName.EscapeMarkup() |
| 65 | + ); |
| 66 | + } |
| 67 | + |
| 68 | + // Display header |
| 69 | + AnsiConsole.MarkupLine($"[green]Found {certificates.Count} certificate(s) in Current User - Personal store:[/]"); |
| 70 | + AnsiConsole.WriteLine(); |
| 71 | + |
| 72 | + // Render the table |
| 73 | + AnsiConsole.Write(table); |
| 74 | +} |
| 75 | +catch (Exception ex) |
| 76 | +{ |
| 77 | + AnsiConsole.MarkupLine($"[red]Error accessing certificate store: {ex.Message}[/]"); |
| 78 | +} |
| 79 | +finally |
| 80 | +{ |
| 81 | + // Always close the store |
| 82 | + store.Close(); |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +Run with the new single-file `dotnet run` feature (example assumes the file is named `list-certs.cs`): |
| 87 | + |
| 88 | +```pwsh |
| 89 | +dotnet run .\list-certs.cs |
| 90 | +``` |
0 commit comments