Return the right certificate from the reuse check #21
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #20. Two independent defects in the reuse check, both of which handed back a certificate that did not satisfy the request that was made.
Wrong issuer
The reuse search filtered on common name and status only:
So
WEB01issued by a server-authentication profile andWEB01issued by a client-authentication profile are indistinguishable. Pointing the same script at a client-auth profile on a host already holding a server-auth certificate returned the existing one — same subject, wrong extended key usages, no indication anything was off:The search is now scoped by
-CertificateProfileIdor-CertificateAuthorityId. Both filters already existed onInfisicalCertificateSearchQueryand serialize asprofileIds/caIds; the reuse path simply never set them. The subscriber path needs no filter, because a subscriber pins its own common name — a name match is already a subscriber match.A second defect compounded this one.
InfisicalLocalCertificateLookup.FindMatchapplied its serial filter only when the candidate set was non-empty:After scoping, switching profiles correctly returns zero candidates — and that emptiness disabled the filter entirely, degrading to a name-only local match. That is precisely the path that returns another issuer's certificate, so fixing the scope alone would have left the defect in place. A completed search that finds nothing is now a definite "nothing to reuse".
Missing subject alternative names
Extending
-DnsNameor-IpAddressand re-running returned the existing certificate, which lacked the name just added. A candidate must now carry every requested name, and the disqualifying name is reported:Reading SANs back off an installed certificate needed a decoder. netstandard2.0 has no
X509SubjectAlternativeNameExtension, andX509Extension.Formatreturns localized text — on a non-English host every comparison would silently fail — so the extension is decoded from its DER bytes with BouncyCastle, already carried for CSR generation. No new dependency.The rule is coverage rather than equality, since a certificate carrying more names than requested still satisfies the request. DNS compares case-insensitively and IP addresses are normalized through
IPAddress, so::1and0:0:0:0:0:0:0:1are the same name.FindMatchpicks the longest-lived covering candidate rather than picking the newest and then rejecting it, and keeps its original four-argument overload so existing callers are unaffected.Offline behaviour
The lenient name-only fallback survives only for the case where Infisical cannot be reached — failing a renewal because the API is down is worse than a loose match — but it now announces itself as a warning rather than being silent.
Verification
298 tests pass, up from 287. The new SAN tests build certificates through the module's own CSR builder, so the reader is exercised against genuine DER rather than a hand-rolled approximation.
Unit tests alone would not prove the store path works, so the decision matrix was also run against a live
CurrentUser\Mystore holding a certificate withSANPROBE,SANPROBE.contoso.com,10.20.30.40:A further test asserts the issuer scope survives serialization to
profileIdson the wire — a scope that never reaches the API would be worse than none.Full
build.ps1 -RunTestsgreen, including module import, manifest, and help validation across 53 cmdlets.Behaviour change worth knowing
Reuse is stricter in one direction only. Trimming a SAN list still reuses the existing certificate, because it covers everything requested; only extending forces reissue. Use
-Forcewhen the SAN set needs narrowing. Documented in the README and cmdlet help.Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Generated with Claude Code