SslStream 身份验证在本地系统帐户下失败

SslStream Authentication fails under LOCAL SYSTEM account

我有这个代码:

string certificateFilePath = @"C:\Users\Administrator\Documents\Certificate.pfx";

string certificateFilePassword = "Some Password Here";

X509Certificate clientCertificate = new X509Certificate(certificateFilePath, certificateFilePassword);

TcpClient client = new TcpClient(host, port);

SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true);

X509CertificateCollection clientCertificates = new X509CertificateCollection {clientCertificate};

stream.AuthenticateAsClient(host, clientCertificates, SslProtocols.Tls, false);

当我 运行 控制台应用程序中的代码时,一切正常,stream.IsAuthenticatedstream.IsMutuallyAuthenticated return truestream.LocalCertificate包含正确的证书对象。

然而,当 运行 在 Windows Service (as LOCAL SYSTEM user) 中使用完全相同的代码时,尽管 stream.IsAuthenticated returns truestream.IsMutuallyAuthenticated returns falsestream.LocalCertificate returns null.

在这两种情况下都会发生这种情况,在第一行 运行 之后 clientCertificate 加载正确的证书数据并包含证书 SubjectIssuer 的正确信息.

我也试过使用以下代码强制 SslStream 选择证书:

string certificateFilePath = @"C:\Users\Administrator\Documents\Certificate.pfx";

string certificateFilePassword = "Some Password Here";

X509Certificate clientCertificate = new X509Certificate(certificateFilePath, certificateFilePassword);

TcpClient client = new TcpClient(host, port);

SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true, (sender, host, certificates, certificate, issuers) => clientCertificate);

X509CertificateCollection clientCertificates = new X509CertificateCollection {clientCertificate};

stream.AuthenticateAsClient(host, clientCertificates, SslProtocols.Tls, false);

但是代码仍然无效,stream.IsMutuallyAuthenticated returns falsestream.LocalCertificate returns null.

这几天我一直在探索这个问题,但我想不通。非常感谢任何帮助。

编辑: 在使用 WinHttpCertCfg 工具尝试证书后,结果发现与 similar question(s) 不同,LOCAL SYSTEM 帐户已经可以访问目标证书的私钥,如下图所示: 所以问题还是没有解决。

我们用于此类服务问题的一种调试技术是使用 PsExec 这样的实用程序。这将允许您运行一个交互过程作为必要的服务帐户。

-s Run the process in the System account.

帮助内容会说"remote process",但它也可以用于本地进程

例如, 在 运行 在具有管理员权限的命令提示符中使用以下命令后,以下命令将作为系统帐户为您提供命令提示符

PsExec.exe -s cmd

在命令window中,可以使用WhoAmI命令查看

C:\Windows\system32>whoami
nt authority\system

这将使您能够进行一些交互式测试以尝试不同的组合。

SYSTEM 帐户在本地机器上有 maximum possible privilege

您还会看到为 运行 服务创建自定义帐户是推荐的选项。但是,这有维护另一个密码的开销。在较新版本的 Windows 中有一个 managed service accounts。这可能是更好的选择。

the managed service account and the virtual account—are designed to provide applications with the isolation of their own accounts, while eliminating the need for an administrator to manually administer the SPN and credentials for these accounts.

我在玩 X509 时终于让代码工作了 类。

这是对我有用的代码:

string host = "The Host";

int port = 777;

string certificateFilePath = @"C:\Users\Administrator\Documents\Certificate.pfx";

string certificateFilePassword = "Some Password Here";

X509Certificate clientCertificate = new X509Certificate(certificateFilePath, certificateFilePassword);

X509Certificate2 clientCertificate2 = new X509Certificate2(clientCertificate); //<== Create a X509Certificate2 object from the X509Certificate which was loaded from the file. The clientCertificate2 loads the proper data

TcpClient client = new TcpClient(host, port);

SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true);

X509CertificateCollection clientCertificates = new X509CertificateCollection { clientCertificate2 }; //<== Using the clientCertificate2 which has loaded the proper data instead of the clientCertificate object

stream.AuthenticateAsClient(host, clientCertificates, SslProtocols.Tls, false);

这样我的代码就可以从系统中找到正确的 X509Store、证书和私钥。

我是凭经验想出来的。不过,我在 MSDN 上找不到关于为什么它应该像这样的明确解释。