ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TLS客户端凭据错误10013:Schannel 36871私钥权限排查

TLS客户端凭据错误10013:Schannel 36871私钥权限排查 创建TLS客户端凭据时发生严重错误。内部错误状态为 10013。这个报错我最近又碰到一次现场是Windows Server 2019 IIS业务应用调用外部HTTPS接口时突然全部超时系统日志里Schannel来源疯狂刷36871。很多人看到TLS、客户端凭据、严重错误这几个词第一反应是证书过期或者协议版本不对但10013这个内部状态往往把方向指向另一件事权限。它不是说TLS协议本身谈不拢而是Windows在准备客户端凭据时底层访问被拒绝。客户端凭据可以理解成Windows替应用程序去握手的“身份证”这张身份证由证书和私钥组成Schannel在创建它的时候需要读取私钥如果读取动作被ACL挡在门外就会抛出内部状态10013。这个错误在IIS、.NET、WinHTTP、SQL Server、Exchange、甚至某些桌面客户端里都可能出现表现却五花八门有的服务直接不可用有的只是间歇性失败有的只在重启后第一笔请求失败。下面我把这次处理记录拆开讲包括错误链路、排查顺序、修复动作和踩过的坑尽量让遇到同类问题的朋友能直接抄作业。1. 先把10013翻译成人话它到底卡在哪一步1.1 Schannel创建客户端凭据的幕后流程Windows里负责TLS/SSL握手的主要组件是Schannel它不像OpenSSL那样直接暴露API给应用而是通过SSPI接口让应用程序调用。当应用程序要作为客户端去连接HTTPS服务时它会调用AcquireCredentialsHandle并指定要使用某个证书。Schannel收到请求后会去证书存储里找到对应证书然后尝试打开私钥容器读取私钥句柄最后组装成客户端凭据。这个过程中任何一步失败都会在Schannel事件日志里留下记录事件ID 36871就是典型的一条“创建 TLS 客户端凭据时发生严重错误。内部错误状态为 10013。”这里的10013不是Win32的“访问被拒绝”码本身而是Schannel内部状态映射后的结果但它确实常常对应到底层权限问题。换句话说证书存在、私钥也存在但当前进程的身份没有权限打开私钥。很多人会误以为“证书已经导入IIS能选到就代表权限没问题”实际上证书管理单元里能看到证书只说明当前登录的管理员有权限不代表应用池账户有权限。Schannel是以应用程序的身份去读私钥的如果应用池账户是ApplicationPoolIdentity实际身份是“IIS AppPool\应用池名称”而私钥文件的ACL里默认可能只有SYSTEM、Administrators和创建者这就直接导致10013。还有一种情况是证书从旧服务器导出再导入私钥容器的ACL没有跟着迁移也会出现同样问题。所以看到这个报错第一步不是去改TLS版本而是先确认私钥权限。1.2 为什么10013不是普通的“连不上”普通的TLS连接失败比如协议版本不匹配、密码套件不兼容、证书过期、证书链不完整通常会在事件日志里看到36870、36874、36875、36876等事件提示“收到TLS 1.0连接请求但服务器不支持”或者“证书链不完整”。而36871加上10013更多是“凭据准备阶段”就失败了还没走到真正的网络握手。你可以把它类比成你要进公司大楼门禁系统先要读取你的工牌芯片结果读取器没有权限访问芯片数据于是门禁直接报“内部错误”而不是“工牌过期”。所以这类问题的排查重心应该放在证书私钥、服务账户、ACL、证书存储权限、Schannel配置上而不是一上来就抓包看TLS握手。当然抓包也有用但如果客户端连Client Hello都没发出去抓包只能看到TCP连接建立后没有后续这时候看Schannel日志更直接。另外10013在Windows不同组件里可能被包装成不同错误码比如.NET可能抛“CryptographicException: 创建TLS客户端凭据时发生严重错误”WinHTTP可能返回12175或12175的变体IIS日志里可能只看到500。因此定位时要交叉看系统日志、应用日志和CAPI2日志不能只盯一个地方。2. 我的排查顺序从日志到证书权限2.1 用事件查看器和CAPI2日志抓现场我遇到这类问题时第一步永远是打开事件查看器筛选系统日志里的Schannel来源。命令行走一遍最快Get-WinEvent -FilterHashtable {LogNameSystem; ProviderNameSchannel} -MaxEvents 100 | Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-List如果看到36871并且消息里明确写了“内部错误状态为 10013”基本可以锁定凭据创建失败。接着启用CAPI2日志路径是“事件查看器 - 应用程序和服务日志 - Microsoft - Windows - CAPI2 - Operational”右键启用日志。启用后重现一次业务请求再回来看CAPI2事件。CAPI2会记录证书链构建、私钥访问、证书存储读取等细节经常能看到“无法打开私钥容器”或者“访问被拒绝”的明确提示。这一步非常关键因为Schannel的10013只是一个结果CAPI2能告诉你到底是哪张证书、哪个私钥、哪个身份被拒绝了。有些环境里CAPI2日志默认没启用启用后记得观察完再关闭避免日志膨胀。如果CAPI2日志里出现“CryptAcquireCertificatePrivateKey failed”错误码0x8009000B或0x80070005那就直接跳到私钥权限章节。另外如果是IIS应用还可以看IIS日志和HTTPERR日志但不要只看HTTP状态码因为TLS凭据失败可能发生在HTTP层之前。我一般会同时开一个PowerShell窗口循环检测应用池状态和Schannel事件这样重现时能立刻抓到时间点。2.2 用certutil和PowerShell确认证书与私钥状态锁定时间点后下一步是确认证书和私钥的可用性。先列出本地计算机个人存储里的证书Get-ChildItem Cert:\LocalMachine\My | Select-Object Subject, Thumbprint, HasPrivateKey, NotAfter, EnhancedKeyUsageList | Format-Table -AutoSize找到业务使用的证书指纹然后检查私钥是否可读。以管理员身份运行certutil -store My 证书指纹如果输出里显示“私钥不存在”或者“缺少私钥”那说明证书导入时没有包含私钥或者私钥被删了。如果显示有私钥但应用池账户读不到certutil以管理员身份可能仍然能读所以还需要模拟应用池身份测试。可以用PowerShell获取私钥的唯一容器名$thumb 证书指纹 $cert Get-Item Cert:\LocalMachine\My\$thumb $cert.HasPrivateKey $rsa [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert) $rsa.Key.UniqueName拿到UniqueName后去C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys找对应文件用icacls查看ACLicacls C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\UniqueName如果ACL里没有应用池账户或者只有管理员和SYSTEM那10013的根因基本就坐实了。如果是CNG密钥路径可能在C:\ProgramData\Microsoft\Crypto\Keys检查方法类似。这里有个细节不要直接给Everyone读权限最小权限原则是只给需要的服务账户读权限。另外如果证书是“不可导出”的某些修复操作会受限必要时重新导入一份带私钥且允许导出的证书再重新授权。3. 高频根因逐项击破3.1 私钥文件ACL没有放行应用池身份这是最常见的根因没有之一。IIS应用池默认使用ApplicationPoolIdentity实际账户名是“IIS AppPool\应用池名称”。比如应用池叫MyAppPool账户就是IIS AppPool\MyAppPool。如果应用以NETWORK SERVICE运行账户就是NETWORK SERVICE。如果应用以自定义域账户运行就是那个域账户。修复时打开证书管理单元certlm.msc找到“个人 - 证书”右键目标证书 - 所有任务 - 管理私钥。在弹出的权限窗口里添加对应的应用池账户至少给“读取”权限。如果“管理私钥”菜单灰掉说明证书没有私钥或者当前用户没有权限需要先用管理员身份操作。也可以用命令行icacls C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\UniqueName /grant IIS AppPool\MyAppPool:R授予后重启应用池Restart-WebAppPool -Name MyAppPool注意有些环境里应用池账户名需要写成IIS AppPool\MyAppPool不能只写MyAppPool否则会提示找不到账户。如果应用池是“无托管代码”或“经典模式”身份可能不同。还有一点如果证书私钥文件所在的父目录ACL有问题比如MachineKeys目录本身没有继承权限也可能导致读取失败。我通常会检查父目录的ACL是否包含SYSTEM和Administrators完全控制但不建议随意改父目录只改具体私钥文件即可。3.2 Schannel协议与密码套件配置被改乱有时候10013并不是私钥权限而是Schannel本身的配置被安全加固脚本改乱了。比如为了满足某些扫描要求有人把TLS 1.0/1.1全部禁用同时又把几乎所有RSA密码套件禁用只留下ECDHE套件但证书私钥是RSA的Schannel在创建客户端凭据时可能找不到可用的套件组合结果报内部错误。检查注册表reg query HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols /s确认TLS 1.2的Client和Server子键下Enabled为1DisabledByDefault为0。如果没有这些键系统默认可能还是启用的。如果需要显式启用reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client /v Enabled /t REG_DWORD /d 1 /f reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client /v DisabledByDefault /t REG_DWORD /d 0 /f reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server /v Enabled /t REG_DWORD /d 1 /f reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server /v DisabledByDefault /t REG_DWORD /d 0 /f改完注册表需要重启服务器或至少重启相关服务Schannel配置不是热加载的。密码套件可以用Get-TlsCipherSuite查看如果发现3DES相关套件被禁用那是为了应对CVE-2016-2183这类信息泄露漏洞属于正常安全加固但不要禁用所有RSA套件。如果确实需要禁用弱套件建议保留TLS_RSA_WITH_AES_128_CBC_SHA或TLS_RSA_WITH_AES_256_CBC_SHA等强套件确保RSA证书还有可用组合。修改密码套件顺序最好通过组策略“SSL密码套件顺序”来做不要手改注册表后忘记备份。3.3 证书链、系统时间和根证书更新虽然10013主要是权限但证书链问题也可能间接导致凭据创建失败。比如中间证书缺失Schannel在构建客户端凭据时无法验证证书链可能返回内部错误。用certutil -verify检查certutil -verify -urlfetch C:\path\to\cert.cer如果提示“证书链不完整”或“无法获取颁发者”需要把中间证书导入“中间证书颁发机构”存储。根证书更新也很重要尤其是离线环境或长期未更新的服务器。可以运行certutil -generateSSTFromWU roots.sst然后手动导入或者直接依赖Windows Update自动更新。系统时间偏差过大也会导致证书验证失败用w32tm /resync同步时间。我遇到过一台虚拟机从快照恢复后时间倒退导致所有TLS客户端请求失败事件日志里既有36871也有证书过期提示同步时间后立刻恢复。所以排查时不要忽略时间同步这个看似低级的点。另外如果证书本身已过期或者证书的“客户端身份验证”EKU缺失也会导致Schannel拒绝使用该凭据。检查证书的增强密钥用法确保包含“客户端身份验证”或“任何目的”。3.4 组策略、FIPS与应用池账户配置企业环境里组策略可能强制启用FIPS兼容算法这会影响Schannel的密码套件选择。检查reg query HKLM\SYSTEM\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy /v Enabled如果Enabled为1而你的证书或应用依赖非FIPS算法就可能出现问题。可以尝试临时关闭FIPS测试但生产环境要评估合规要求。另一个常见问题是应用池的“加载用户配置文件”选项。如果证书私钥被放在了用户配置文件的密钥容器里而应用池没有加载用户配置文件就读取不到。在IIS管理器中应用池高级设置里把“加载用户配置文件”设为True然后重启应用池。还有如果应用是以服务方式运行服务的登录身份可能不是你以为的那个账户。用sc qc 服务名查看服务账户或者用任务管理器查看进程身份。有些服务用LocalSystem运行但证书私钥ACL里没有SYSTEM读权限也会10013。不要假设一定要实际确认进程身份。4. 实操修复从备份到验证的完整记录4.1 备份证书与私钥避免越修越乱动手之前先备份。证书和私钥一旦弄丢重新签发和部署的成本很高。在证书管理单元里导出证书选择“是导出私钥”设置一个临时密码保存为PFX文件。如果私钥标记为不可导出可以先尝试用certutil -exportPFX命令certutil -exportPFX -p 临时密码 My 证书指纹 C:\backup\cert.pfx如果提示不可导出可能需要重新导入证书并勾选“允许导出私钥”或者联系证书颁发机构重新签发。备份完PFX后再导出证书的ACL信息ACL不能直接导出但可以记录当前的权限列表。可以用icacls把私钥文件权限保存到文本icacls C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\UniqueName C:\backup\key-acl.txt同时记录注册表中Schannel相关的键值reg export HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL C:\backup\schannel.reg这样即使改错了也能快速回滚。我见过有人直接删除MachineKeys下的私钥文件想让它重建结果证书彻底无法使用业务停了几个小时。所以备份是底线。4.2 重置私钥权限的三种可靠方法第一种是图形界面certlm.msc- 个人 - 证书 - 所有任务 - 管理私钥 - 添加账户 - 输入IIS AppPool\MyAppPool- 检查名称 - 确定 - 勾选“读取”。这种方法最直观适合不熟悉命令行的朋友。第二种是icacls命令行前面已经给出优点是可以批量处理也可以写成脚本。第三种是certutil -repairstore当私钥容器损坏或ACL异常时可以尝试certutil -repairstore My 证书指纹这个命令会尝试修复证书与私钥的关联但要注意它有时会重新生成私钥容器导致原有权限丢失所以执行后要重新检查并授予应用池读权限。如果环境里有多个应用池共用同一张证书需要给每个应用池账户都授予读权限或者考虑用同一个服务账户运行这些应用池。不要图省事给Everyone读权限私钥泄露风险很高。如果使用负载均衡每台服务器都要做同样的权限修复否则请求转到未修复的节点时又会失败。4.3 修复Schannel注册表并重启相关服务如果确认是Schannel协议配置问题先导出备份再按需修改。通常保留TLS 1.2和TLS 1.3即可TLS 1.0/1.1如果业务允许可以禁用但不要同时把加密套件限制得过死。修改后重启服务器是最稳妥的如果业务不允许重启至少重启以下服务Restart-Service -Name HTTP -Force Restart-Service -Name WinHTTP Web Proxy Auto-Discovery Service -Force Restart-WebAppPool -Name MyAppPool如果是Windows服务调用TLS重启对应服务。注意Schannel没有独立的服务它由内核和LSASS等组件承载所以注册表修改后最好重启。另外如果启用了CAPI2日志记得修复后关闭避免日志占满磁盘。有些安全加固脚本会写入“DisabledByDefault1”这会把TLS 1.2也默认禁用导致大量TLS失败要特别检查。对于CVE-2016-2183这类扫描项正确的做法是禁用3DES和RC4等弱套件而不是禁用整个协议族。可以使用Disable-TlsCipherSuite -Name TLS_RSA_WITH_3DES_EDE_CBC_SHA Disable-TlsCipherSuite -Name TLS_RSA_WITH_RC4_128_SHA执行后再次确认至少还有TLS_RSA_WITH_AES_128_CBC_SHA或TLS_RSA_WITH_AES_256_CBC_SHA可用。4.4 验证TLS客户端凭据是否恢复正常修复完成后不要只看事件日志不再报错最好主动发起一次TLS客户端请求。可以用PowerShell$url https://example.com try { $req [System.Net.HttpWebRequest]::Create($url) $req.Method HEAD $resp $req.GetResponse() Write-Host 成功状态码 $resp.StatusCode $resp.Close() } catch { Write-Host 失败 $_.Exception.Message }如果是IIS应用直接访问业务页面观察是否恢复。同时在事件查看器里筛选Schannel确认没有新的36871。还可以用Test-NetConnection测试端口连通性但端口通不代表TLS凭据创建成功。更彻底的验证是使用SslStream写一个小脚本指定客户端证书$cert Get-Item Cert:\LocalMachine\My\证书指纹 $client New-Object System.Net.Sockets.TcpClient(example.com, 443) $ssl New-Object System.Net.Security.SslStream($client.GetStream(), $false) $ssl.AuthenticateAsClient(example.com, $cert, [System.Security.Authentication.SslProtocols]::Tls12, $false) Write-Host TLS握手成功协议 $ssl.SslProtocol $ssl.Close() $client.Close()如果这个脚本以应用池身份运行也能成功那基本就没问题了。注意测试时不要使用真实敏感站点用公开测试站点即可。5. 常见问题速查表与避坑心得5.1 现象、可能原因、处理动作对照表现象可能原因处理动作Schannel事件36871内部状态10013私钥ACL缺少应用池读权限用certlm.msc或icacls授予IIS AppPool\...读权限仅IIS应用报错其他程序正常应用池身份与应用池名称不匹配确认应用池实际账户重新授权重启后短暂恢复随后又失败组策略刷新覆盖配置或证书服务重新生成权限检查组策略结果集确认私钥ACL是否被重置系统时间偏差大后出现证书链验证失败同步时间更新根证书禁用TLS 1.0/1.1后出现协议或密码套件配置不兼容确保TLS 1.2启用保留强RSA套件多个应用池共用证书部分失败只给了一个应用池权限给所有使用该证书的应用池账户授权证书管理单元里“管理私钥”灰色证书无私钥或当前用户无权限用管理员运行certlm.msc或重新导入带私钥证书CAPI2日志显示0x80070005访问被拒绝按私钥权限问题处理5.2 我踩过的坑和反直觉结论第一个坑以为给IIS_IUSRS就万事大吉。实际上ApplicationPoolIdentity不是IIS_IUSRS组的成员它是虚拟账户必须显式添加IIS AppPool\应用池名称。第二个坑只重启IIS没重启应用池。IIS重启不会重新加载应用池身份权限变更后必须回收应用池。第三个坑用管理员账户测试成功就以为修好了。管理员权限太大必须用应用池身份测试或者直接看业务请求是否恢复。第四个坑盲目运行certutil -repairstore。这个命令有时会重置私钥容器导致原本正常的证书也需要重新授权所以先备份再操作。第五个坑忽略证书链。我曾遇到一个环境私钥权限完全正确但中间证书缺失Schannel偶尔报10013导入中间证书后彻底稳定。第六个坑安全加固脚本一刀切。有些脚本把TLS 1.0/1.1全禁同时把3DES禁用这本身没问题但把所有RSA套件也禁了而证书是RSA的结果客户端凭据创建失败。正确的做法是禁用弱算法保留强RSA套件或者改用ECC证书。第七个坑在负载均衡环境只修一台。请求轮询到其他节点时继续报错所以要么统一修复要么临时把故障节点下线。6. 别混淆10013在不同场景下的面孔6.1 与socket 10013、bind 80端口失败的区别os error 10013经常出现在socket编程里意思是“访问套接字的方式被权限禁止”。比如bind() to 0.0.0.0:80 failed (10013: an attempt was made to access a socket in a way forbidden by its access permissions)这通常是因为非管理员账户尝试绑定80端口或者端口被系统保留、被其他程序占用。解决方法是换端口、以管理员运行、或者用netsh http show urlacl检查URL保留。而TLS客户端凭据的10013是Schannel内部状态跟socket绑定无关。两者都带10013但一个在传输层一个在安全凭据层。排查时先看报错来源如果是Schannel事件就走证书私钥权限如果是应用日志里写bind失败就走端口权限和占用排查。不要把两者混为一谈否则会浪费很多时间。codex桌面版error 10013可能就是应用包装了底层socket错误也可能是证书相关需要看具体日志。判断标准是看错误上下文里有没有“TLS客户端凭据”字样。6.2 与0x80070643、浏览器TLS弃用报错的区分0x80070643-安装时发生严重错误是Windows更新或安装程序常见的错误码通常跟.NET Framework、Visual C运行库、Windows Installer有关和TLS凭据没有直接关系。如果你在安装某个软件时同时看到0x80070643和TLS错误要分开处理先解决安装问题再解决TLS问题。浏览器报错“无法安全地连接到此页面 这可能是因为该站点使用过期的或不安全的 TLS 安全设置”或者“火狐报错 该网站使用了已弃用的 TLS 版本。请升级到 tls 1.2 或 1.3。”这是浏览器作为客户端发现服务器只支持TLS 1.0/1.1或者证书过期、证书链不完整。这类问题需要改服务器配置启用TLS 1.2/1.3更新证书而不是去修客户端私钥权限。还有the tls certificates for the following protocols have expired通常指某些协议使用的证书已过期需要检查证书有效期。把这几类错误分清能避免在错误方向上折腾。我的习惯是先看错误码前缀0x8007开头多是Win32安装类错误36871是Schannel事件浏览器提示是UI层包装三者的处理路径完全不同。6.3 嵌入式TLS与PSK场景的简单对照stm32 mqtt tls加密通信这类场景通常用mbedTLS或类似库客户端凭据是编译进固件的证书和私钥数组不存在Windows ACL问题。如果握手失败优先检查证书格式、时间有效性、根证书、TLS版本、密码套件以及设备时间是否同步。tls psk是预共享密钥模式不使用证书凭据所以一般不会出现“创建TLS客户端凭据”这种错误。如果PSK和证书混合使用配置错误可能导致握手失败但错误信息通常不会映射成Windows Schannel的10013。Wireshark TLS解密在排查这些场景时很有用但前提是你有私钥并且密钥交换算法允许解密比如RSA密钥交换ECDHE前向保密则无法直接用私钥解密。不过对于Windows Schannel的10013抓包往往看不到Client Hello因为凭据还没创建成功所以优先看系统日志和CAPI2日志。最后再分享一个小技巧把Get-WinEvent和icacls检查写成一段脚本每次修复后跑一遍确认事件日志没有新增36871同时确认私钥ACL包含正确的应用池账户。这个习惯让我在后续几次类似故障里从接到报错到恢复业务基本控制在二十分钟以内。
返回列表