.net - 无法使用证书通过 Windows 10 1809 中的 com-interop 登录网站

标签 .net excel vba https com-interop

下面的 C# 代码,它登录到一个网站,

  • 在通过 com-interop 从 Excel 2010 VBA 调用计算机 A 时工作
  • 从 C# 控制台应用程序在计算机 B 上调用时有效,但
  • 通过 com-interop 从 Excel 2010 VBA 在计算机 B 上调用时失败

  • 电脑A和电脑B的主要区别是电脑A是windows 10 version 1803,而电脑B是windows 10 version 1809。两台电脑都是Studio 2017,所有情况下目标.Net Framework都是4.6.2。
    using System;
    using System.Runtime.InteropServices;
    using System.IO;
    
    [Guid("97E1D9DB-8478-4E56-9D6D-26D8EF13B100")]
    [ComVisible(true)]
    public interface IToExcel {
        string Do();
    }
    
    [Guid("BBF87E31-77E2-46B6-8093-1689A144BFC6")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public class Main : IToExcel {
        private const string XAPP_ID = "...";
        private const string USERNAME = "...";
        private const string PASSWORD = "...";
        private const string CERT_FILE = @"...";
        private const string CERT_PASSWORD = "...";
        private const string WEBSITE = "https:// ...";
    
        public string Do() {
            System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(new Uri(WEBSITE));
            request.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
            request.Method = "POST";
            request.Accept = "application/json";
            request.Timeout = request.ReadWriteTimeout = 20000;
            request.ContentType = "application/x-www-form-urlencoded";
            request.UseDefaultCredentials = true;
            request.Proxy = null;
            // setup headers
            System.Net.WebHeaderCollection whc = new System.Net.WebHeaderCollection {
                { "X-Application", XAPP_ID },
                { System.Net.HttpRequestHeader.AcceptCharset, "utf-8" },
                { System.Net.HttpRequestHeader.AcceptEncoding, "gzip,deflate" }
            };
            request.Headers.Add(whc);
            // setup certificate
            System.Security.Cryptography.X509Certificates.X509Certificate2 m_x509certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(CERT_FILE, CERT_PASSWORD);
            request.ClientCertificates.Add(m_x509certificate);
            // do call
            using (Stream stream = request.GetRequestStream()) {
                using (StreamWriter writer = new StreamWriter(stream, System.Text.Encoding.Default)) {
                    writer.Write("username=" + USERNAME + "&password=" + PASSWORD);
                }
            }
            string responseData = string.Empty;
            using (System.Net.WebResponse response = request.GetResponse()) {
                using (Stream responseStream = response.GetResponseStream()) {
                    using (StreamReader reader = new StreamReader(responseStream, System.Text.Encoding.UTF8)) {
                        responseData = reader.ReadToEnd();
                    }
                }
            }
            return responseData;
        }
    
    }
    

    在所有情况下,都会返回一个小的 JSON 对象,其中 JSON 对象有一个名为“loginStatus”的字段。当它工作时,“loginStatus”=“SUCCESS”,但当它失败时“loginStatus”=“CERT_AUTH_REQUIRED”。

    我尝试查看 System.Net.ServicePointManager 中的所有设置,但在所有情况下,设置都是相同的:
  • 重用端口:假
  • 服务器证书验证回调:
  • DnsRefresh超时:120000
  • EnableDnsRoundRobin:假
  • Expect100Continue:真
  • 使用NagleAlgorithm:真
  • MaxServicePointIdleTime:100000
  • 默认连接限制:2
  • 最大服务点数:0
  • 安全协议(protocol):Tls、Tls11、Tls12
  • CheckCertificateRevocationList: False
  • 加密策略:RequireEncryption

  • 除此之外,我不知道还有什么要检查的。从 .Net 4.6.2 升级到 4.7.1 没有效果,结果是一样的。

    我想知道这是否是 Windows 1809 中的错误,但由于它在 .Net 控制台应用程序中直接调用时有效,我认为这是一些微妙的配置问题。任何人都可以帮助我从计算机 B 上的 Excel 2010 开始工作吗?

    2019 年 2 月 8 日更新

    正如评论中所建议的,我使用 Fiddler 来查看对网站进行的 https 调用的结构。两个有效的看起来相同,失败的看起来略有不同:

    通话正常
  • TLS 扩展 ec_point_formats = 未压缩 [0x0]
  • 未指定 TLS 扩展 encrypt_then_mac (RFC7366)
  • TLS 扩展 renegotiation_info = 0
  • 未指定密码 TLS_EMPTY_RENEGOTIATION_INFO_SCSV

  • 调用失败
  • TLS 扩展 ec_point_formats = 未压缩 [0x0]、ansiX962_compressed_prime [0x1]、ansiX962_compressed_char2 [0x2]
  • TLS 扩展 encrypt_then_mac (RFC7366) = 空
  • 未指定 TLS 扩展 renegotiation_info
  • 指定密码 TLS_EMPTY_RENEGOTIATION_INFO_SCSV

  • 但现在我有了这些信息,我不确定它是否有帮助。也许有效的调用都是由相同的低级代码生成的(尽管它们在不同版本的 Windows 10 上),而失败的调用是由不同的低级代码生成的。

    2019 年 2 月 10 日更新

    当通过 com-interop 从 Excel 调用时,我让代码在新的 AppDomain 中执行 EXE,而不是直接调用登录代码。当我这样做时,EXE 无法工作并产生与我直接调用登录代码相同的输出。

    下面是一些 Visual Studio 输出窗口,它显示了 EXE 文件运行时加载 DLL 的顺序,就在登录代码执行之前。成功的场景和失败的场景之间的最大区别在于,失败的场景永远不会加载 C:\Windows\System32\ncryptprov.dll。有谁知道是什么导致该 DLL 被加载?



    (Win32): 加载 'C:\Windows\System32\msisip.dll'

    (Win32): 加载 'C:\Windows\System32\coml2.dll' --- 较早从 EXCEL 加载

    (Win32): 加载 'C:\Windows\System32\wshext.dll'

    (Win32): 加载 'C:\Windows\System32\AppxSip.dll'

    (Win32): 加载 'C:\Windows\System32\tdh.dll'

    (Win32): 加载 'C:\Windows\System32\xmllite.dll'

    (Win32): 加载 'C:\Windows\System32\OpcServices.dll'

    (Win32): 加载 'C:\Windows\System32\mintdh.dll'

    (Win32): 加载 'C:\Windows\System32\urlmon.dll' --- 较早从 EXCEL 加载

    (Win32): 加载 'C:\Windows\System32\mintdh.dll'

    (Win32): 卸载 'C:\Windows\System32\mintdh.dll'

    (Win32): Loaded 'C:\Windows\System32\iertutil.dll' --- LOADED EARLIER FROM EXCEL

    (Win32): 加载 'C:\Windows\System32\WindowsPowerShell\v1.0\pwrshsip.dll'

    (Win32): 加载 'C:\Windows\System32\EsdSip.dll'

    (Win32): 加载 'C:\Windows\System32\userenv.dll' --- 较早从 EXCEL 加载

    (Win32): 加载 'C:\Windows\System32\dpapi.dll'

    (Win32): 加载 'C:\Windows\System32\dnsapi.dll'

    (Win32): 加载 'C:\Windows\System32\rasadhlp.dll'

    (Win32): 加载 'C:\Windows\System32\FWPUCLNT.DLL'

    (Win32): 加载 'C:\Windows\System32\secur32.dll'

    (Win32): 加载 'C:\Windows\System32\sspicli.dll' --- 较早从 EXCEL 加载

    (Win32): 加载 'C:\Windows\System32\schannel.dll'

    (Win32): 加载 'C:\Windows\System32\mskeyprotect.dll'

    (Win32): 加载 'C:\Windows\System32\ncrypt.dll'

    (Win32): 加载 'C:\Windows\System32\ntasn1.dll'

    (Win32): 加载了 'C:\Windows\System32\ncryptprov.dll' --- 从未从 EXCEL 加载

    (Win32): 加载 'C:\Windows\System32\ncryptsslp.dll'

    此时C#代码执行

    2019 年 2 月 12 日更新

    非常感谢 Simon Mourier告诉我如何设置 System.Net 诊断。在计算机 B 上运行诊断程序,获得两种情况输出的“System.Net 信息”行开始时相同,但最终存在差异。这是计算机 B 上控制台 EXE 文件的输出(即有效的情况):
    System.Net Information: 0 : [35268] Current OS installation type is 'Client'.
    System.Net Information: 0 : [35268] RAS supported: True
    System.Net Information: 0 : [35268] Associating HttpWebRequest#21454193 with ServicePoint#34640832
    System.Net Information: 0 : [35268] Associating Connection#43332040 with HttpWebRequest#21454193
    System.Net Information: 0 : [35268] Connection#43332040 - Created connection from XXX.XXX.XXX.XXX:53002 to YYY.YYY.YYY.YYY:443.
    System.Net Information: 0 : [35268] TlsStream#54444047::.ctor(host=<TargetWebSite>, #certs=1, checkCertificateRevocationList=False, sslProtocols=Tls12)
    System.Net Information: 0 : [35268] Associating HttpWebRequest#21454193 with ConnectStream#20234383
    System.Net Information: 0 : [35268] HttpWebRequest#21454193 - Request: POST /api/certlogin HTTP/1.1
    System.Net Information: 0 : [35268] ConnectStream#20234383 - Sending headers
    System.Net Information: 0 : [35268] SecureChannel#47891719::.ctor(hostname=<TargetWebSite>, #clientCertificates=1, encryptionPolicy=RequireEncryption)
    System.Net Information: 0 : [35268] Enumerating security packages:
    System.Net Information: 0 : [35268]     Negotiate
    System.Net Information: 0 : [35268]     NegoExtender
    System.Net Information: 0 : [35268]     Kerberos
    System.Net Information: 0 : [35268]     NTLM
    System.Net Information: 0 : [35268]     TSSSP
    System.Net Information: 0 : [35268]     pku2u
    System.Net Information: 0 : [35268]     CloudAP
    System.Net Information: 0 : [35268]     WDigest
    System.Net Information: 0 : [35268]     Schannel
    System.Net Information: 0 : [35268]     Microsoft Unified Security Protocol Provider
    System.Net Information: 0 : [35268]     Default TLS SSP
    System.Net Information: 0 : [35268]     CREDSSP
    System.Net Information: 0 : [35268] SecureChannel#47891719 - Attempting to restart the session using the user-provided certificate: [Version]
    System.Net Information: 0 : [35268] SecureChannel#47891719 - Left with 1 client certificates to choose from.
    System.Net Information: 0 : [35268] SecureChannel#47891719 - Trying to find a matching certificate in the certificate store.
    System.Net Information: 0 : [35268] SecureChannel#47891719 - Locating the private key for the certificate: [Version]
    System.Net Information: 0 : [35268] SecureChannel#47891719 - Certificate is of type X509Certificate2 and contains the private key.
    System.Net Information: 0 : [35268] SecureChannel#47891719::.AcquireClientCredentials, new SecureCredential() (flags=(ValidateManual, NoDefaultCred, SendAuxRecord, UseStrongCrypto), m_ProtocolFlags=(Tls12Client), m_EncryptionPolicy=RequireEncryption)
    System.Net Information: 0 : [35268] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent  = Outbound, scc     = System.Net.SecureCredential)
    System.Net Information: 0 : [35268] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = (null), targetName = <TargetWebSite>, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
    System.Net Information: 0 : [35268] InitializeSecurityContext(In-Buffer length=0, Out-Buffer length=184, returned code=ContinueNeeded).
    System.Net Information: 0 : [35268] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 227c85a89b0:2449d0deff0, targetName = <TargetWebSite>, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
    System.Net Information: 0 : [35268] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=CredentialsNeeded).
    

    但是,当通过 com-interop 从 Excel 2010 运行时,不是最后 4 个 InitializeSecurityContext 行,而是 6 个 InitializeSecurityContext 行,如下所示:
    System.Net Information: 0 : [39988] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = (null), targetName = <TargetWebSite>, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
    System.Net Information: 0 : [39988] InitializeSecurityContext(In-Buffer length=0, Out-Buffer length=184, returned code=ContinueNeeded).
    System.Net Information: 0 : [39988] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 8a8e2f0:2449d0def90, targetName = <TargetWebSite>, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
    System.Net Information: 0 : [39988] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=ContinueNeeded).
    System.Net Information: 0 : [39988] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 8a8e2f0:2449d0def90, targetName = <TargetWebSite>, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
    System.Net Information: 0 : [39988] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=ContinueNeeded).
    

    前两个 InitializeSecurityContext 行是相同的,所以大概关键的区别在于第三个 InitializeSecurityContext 行,其中控制台 EXE 有

    上下文 = 227c85a89b0:2449d0deff0

    但是通过 com-interop executino 失败的运行有

    上下文 = 8a8e2f0:2449d0def90

    在那之后,事情看起来就不一样了,正如人们所期望的那样。有谁知道这种差异意味着什么,以及如何使差异消失,以便 com-interop 执行的行为与 com-interop 执行的行为相同?

    2019 年 2 月 13 日更新

    我在 an MSDN forum 上发布了更多的诊断输出.

    最佳答案

    关注 C# 控制台与 Excel 2010 问题(同一台机器,都作为 64 位进程运行),已添加跟踪,如下所述:https://stackoverflow.com/a/25683524/403671

    Excel 和 Console 的初始跟踪完全相同(有关更多详细信息,请参阅问题)。但是最后的痕迹(发布到 MSDN)展示了这一点:

    安慰:

    System.Net Information: 0 : [35268] Remote certificate: [Version] V3
    [ Lots of lines describing a certificate with [Subject]=<TargetWebSite>, [Issuer]=HydrantID SSL ICA G2, etc]
    System.Net Information: 0 : [35268] SecureChannel#47891719 - Remote certificate was verified as valid by the user.    
    

    电子表格:
    System.Net Information: 0 : [39988] Remote certificate: [Version] V3
    [ Lots of lines describing a certificate with [Subject]=<TargetWebSite>, [Issuer]=Kaspersky, etc]
    System.Net Information: 0 : [39988] SecureChannel#2383799 - Remote certificate was verified as valid by the user.
    

    它显示 Excel 中使用的证书现在由 Kaspersky 颁发,结果证明是在 PC 上运行的防病毒产品。事实上,这是卡巴斯基产品的安全功能 intercept communications ,这会导致许多问题,例如 KIS Interfering with Git , 例如。

    删除此防病毒软件后,一切都会按预期进行。

    关于.net - 无法使用证书通过 Windows 10 1809 中的 com-interop 登录网站,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54550589/

    相关文章:

    c# - 将字节数组转换为 BitmapImage 时为 “Unspecified error”

    excel - 查找列标题、复制数据并将值粘贴到另一个工作簿中

    excel - 自定义订单排序

    java - 将 Excel 转换为 PDF - 使用 iText 的 Java

    ruby roo 从解析中删除列

    excel - VBA提取字符串中的所有字段名

    excel - 如何在用户定义的 VBA 函数中从另一个工作表中提取数据

    .net - LINQ to SQL 中的 StackOverflowException

    c# - DownloadFile 创建 0 字节文件

    .net - 在 .NET 4 中使用 Fluent NHibernate + SQLite 有问题吗?