I opened an Issue on dotnet/corefx and they replied as follows:
If AllowUnknownCertificateAuthority is the only flag set then
chain.Build()
will return true if
The chain correctly terminated in a self-signed certificate (via
ExtraStore, or searched persisted stores)None of the certificates are invalid per the requested revocation
policyAll of the certificates are valid under the (optional)
ApplicationPolicy or CertificatePolicy valuesAll of the certificates’ NotBefore values are at-or-before
VerificationTime and all of the certificates’ NotAfter values are
(at-or-)after VerificationTime.If that flag is not specified then an additional constraint is added:
The self-signed certificate must be registered as trusted on the system (e.g. in the LM\Root store).
So, Build() returns true, you know that a time-valid non-revoked chain
is present. The thing to do at that point is read
chain.ChainElements[chain.ChainElements.Count - 1].Certificate
and
determine if it is a certificate that you trust. I recommend comparing
chainRoot.RawData
to abyte[]
representing a certificate that you
trust as a root in context (that is, byte-for-byte compare rather than
using a thumbprint value).(If other flags are set then other constraints are also relaxed)
So you should do it this way:
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
var isValid = chain.Build(cert);
var chainRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
isValid = isValid && chainRoot.RawData.SequenceEqual(root.RawData);