You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取并检查SQL Server加密连接证书?代码超时问题排查

问题

需要获取并检查SQL Server加密连接所用的证书,同时实现自定义验证。编写的C#代码在多数场景下可用,但连接SQL Server时出现超时,抛出System.IO.IOException及SocketException(10060)错误。另外ServicePointManager.ServerCertificateValidationCallback无效,因为它仅适用于HTTP客户端。代码如下:

var sqlServer = "sqlserver-fqdn";
using (var client = new TcpClient())
{
    client.SendTimeout = 10000;
    client.ReceiveTimeout = 10000;

    await client.ConnectAsync(sqlServer, 1433).ConfigureAwait(false);

    using (var stream = client.GetStream())
    using (var sslStream = new SslStream(stream, false,
        (sender, certificate, chain, policyErrors) =>
            { /* do some validation */ }, null))
    {
        sslStream.AuthenticateAsClient(sqlServer);
    }
}
解决方案

1. 核心问题:TDS协议的特殊握手流程

SQL Server的加密连接不是建立TCP后直接启动SSL握手,必须先发送TDS预登录包触发加密协商。直接调用SslStream.AuthenticateAsClient会导致SQL Server无响应,最终超时。

2. 适配TDS协议的修正代码

先发送预登录包请求加密,再启动SSL握手,同时实现自定义证书验证:

var sqlServer = "sqlserver-fqdn";
using (var client = new TcpClient())
{
    client.SendTimeout = 10000;
    client.ReceiveTimeout = 10000;
    await client.ConnectAsync(sqlServer, 1433).ConfigureAwait(false);

    using (var stream = client.GetStream())
    {
        // 发送TDS预登录包,请求强制加密
        var preLoginPacket = BuildPreLoginPacket(true);
        await stream.WriteAsync(preLoginPacket, 0, preLoginPacket.Length).ConfigureAwait(false);
        await stream.FlushAsync().ConfigureAwait(false);

        // 读取预登录响应(无需解析,仅确认SQL Server已接收请求)
        var responseBuffer = new byte[1024];
        var bytesRead = await stream.ReadAsync(responseBuffer, 0, responseBuffer.Length).ConfigureAwait(false);
        if (bytesRead == 0)
        {
            throw new InvalidOperationException("未收到SQL Server预登录响应");
        }

        // 初始化SslStream并执行自定义验证
        using (var sslStream = new SslStream(stream, false,
            (sender, certificate, chain, policyErrors) =>
            {
                // 自定义验证逻辑示例
                if (policyErrors == SslPolicyErrors.None)
                    return true;
                
                // 添加你的自定义检查:比如验证证书主题、有效期、颁发者等
                var cert = new X509Certificate2(certificate);
                return cert.Subject.Contains(sqlServer) && cert.NotAfter > DateTime.Now;
            }, null))
        {
            await sslStream.AuthenticateAsClientAsync(sqlServer).ConfigureAwait(false);
            
            // 认证成功后获取服务器证书信息
            var serverCert = sslStream.RemoteCertificate;
            if (serverCert != null)
            {
                Console.WriteLine($"服务器证书主题: {serverCert.Subject}");
                Console.WriteLine($"证书有效期至: {serverCert.NotAfter}");
            }
        }
    }
}

// 构造TDS预登录包,指定加密要求
byte[] BuildPreLoginPacket(bool requireEncryption)
{
    var packet = new List<byte>();
    // 包长度占位(后续填充)
    packet.AddRange(BitConverter.GetBytes((ushort)0));
    // 包类型:预登录
    packet.Add(0x01);
    // 状态位
    packet.Add(0x00);
    // TDS协议版本(7.4,兼容主流SQL Server版本)
    packet.AddRange(new byte[] { 0x04, 0x00, 0x00, 0x01 });
    // 填充长度
    packet.Add(0x00);
    // SPID(忽略)
    packet.AddRange(new byte[] { 0x00, 0x00 });

    // 加密选项:0x01=加密类型,值0x02表示强制加密
    var encryptionOption = new List<byte>
    {
        0x01, // 选项类型
        0x01, 0x00, // 选项长度
        requireEncryption ? (byte)0x02 : (byte)0x01 // 选项值
    };
    packet.AddRange(encryptionOption);

    // 选项结束标记
    packet.Add(0xff);

    // 更新实际包长度
    var lengthBytes = BitConverter.GetBytes((ushort)packet.Count);
    packet[0] = lengthBytes[0];
    packet[1] = lengthBytes[1];

    return packet.ToArray();
}

3. 额外超时排查点

  • 确认SQL Server已启用加密:在SQL Server配置管理器中,检查"SQL Server网络配置" -> 对应实例协议 -> "TCP/IP"属性 -> "标志" -> "强制加密"是否配置正确,且已绑定有效SSL证书。
  • 检查防火墙规则:确保客户端网络可访问SQL Server的1433端口,无拦截。
  • 验证证书信任链:如果使用自签名证书,需将其导入客户端的"受信任的根证书颁发机构"存储。

内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 14:40:23