//
// Extracts a remote certificate upon request.
//
private static X509Certificate2?GetRemoteCertificate(
SafeDeleteContext?securityContext, bool retrieveChainCertificates, ref X509Chain?chain)
{
if (securityContext == null)
{
return(null);
}
X509Certificate2? result = null;
SafeFreeCertContext?remoteContext = null;
try
{
// SECPKG_ATTR_REMOTE_CERT_CONTEXT will not succeed before TLS handshake completes. Inside the handshake,
// we need to use (more expensive) SECPKG_ATTR_REMOTE_CERT_CHAIN. That one may be unsupported on older
// versions of windows. In that case, we have no option than to return null.
//
// We can use retrieveCollection to distinguish between in-handshake and after-handshake calls, because
// the collection is retrieved for cert validation purposes after the handshake completes.
if (retrieveChainCertificates) // handshake completed
{
SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext);
}
else // in handshake
{
SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CHAIN(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext);
}
if (remoteContext != null && !remoteContext.IsInvalid)
{
result = new X509Certificate2(remoteContext.DangerousGetHandle());
}
}
finally
{
if (remoteContext != null && !remoteContext.IsInvalid)
{
if (retrieveChainCertificates)
{
chain ??= new X509Chain();
UnmanagedCertificateContext.GetRemoteCertificatesFromStoreContext(remoteContext, chain.ChainPolicy.ExtraStore);
}
remoteContext.Dispose();
}
}
if (NetEventSource.Log.IsEnabled())
{
NetEventSource.Log.RemoteCertificate(result);
}
return(result);
}