@page "/certificates" @attribute [Microsoft.AspNetCore.Authorization.Authorize] @rendermode RenderMode.InteractiveServer @using System.Security.Cryptography.X509Certificates @using Microsoft.Extensions.Configuration @inject IConfiguration Config

OPC UA certificates

PKI store layout: {PkiStoreRoot}/own (this server's identity), issuer / trusted (peers we accept), rejected (peers we've turned away). F13a wires SDK auto-creation so the own-store self-signs on first boot.
@if (_rows is null) {

Loading…

} else { @foreach (var store in _rows) {
@store.Label · @store.Certificates.Count entry@(store.Certificates.Count == 1 ? "" : "s")
@if (string.IsNullOrEmpty(store.Path)) {
No path configured.
} else if (!Directory.Exists(store.Path)) {
@store.Path doesn't exist yet. It will be created on first boot.
} else if (store.Certificates.Count == 0) {
No certificates in @store.Path.
} else {
@foreach (var c in store.Certificates) { }
Subject Issuer Thumbprint Not before Not after
@c.Subject @c.Issuer @c.Thumbprint[..16]… @c.NotBefore.ToString("u") @c.NotAfter.ToString("u")
}
} } @code { private List? _rows; protected override void OnInitialized() { var pkiRoot = Config.GetValue("OpcUa:PkiStoreRoot") ?? "pki"; _rows = new() { LoadStore("Own", Path.Combine(pkiRoot, "own", "certs")), LoadStore("Trusted peers", Path.Combine(pkiRoot, "trusted", "certs")), LoadStore("Trusted issuers", Path.Combine(pkiRoot, "issuer", "certs")), LoadStore("Rejected", Path.Combine(pkiRoot, "rejected", "certs")), }; } private static StoreView LoadStore(string label, string path) { var view = new StoreView(label, path, new List()); if (!Directory.Exists(path)) return view; foreach (var file in Directory.EnumerateFiles(path).Where(IsCertFile)) { try { view.Certificates.Add(X509CertificateLoader.LoadCertificateFromFile(file)); } catch { /* ignore unreadable entries */ } } return view; } private static bool IsCertFile(string path) { var ext = Path.GetExtension(path); return ext.Equals(".der", StringComparison.OrdinalIgnoreCase) || ext.Equals(".cer", StringComparison.OrdinalIgnoreCase) || ext.Equals(".crt", StringComparison.OrdinalIgnoreCase); } private sealed record StoreView(string Label, string Path, List Certificates); }