.NET Core ChannelFactory - 将X509Certificate2设置为客户端证书

5

我需要为我的WCF通道设置客户端证书(例如实例,而不是从Windows证书存储中获取),但是我总是遇到异常:

System.InvalidOperationException:“对象是只读的。”

这很奇怪,因为这些属性有一个setter,但如果我分配一个X509Certificate2它就会崩溃。

堆栈跟踪

System.InvalidOperationException
  HResult=0x80131509
  Nachricht = Object is read-only.
  Quelle = System.Private.ServiceModel
  Stapelüberwachung:
   at System.ServiceModel.Security.X509CertificateRecipientClientCredential.ThrowIfImmutable()
   at System.ServiceModel.Security.X509CertificateRecipientClientCredential.set_DefaultCertificate(X509Certificate2 value)

代码

var binding = new BasicHttpsBinding();
var endpoint = new EndpointAddress(new Uri("https://myservice.com"));
var channelFactory = new ChannelFactory<MyService>(binding, endpoint);
var serviceClient = channelFactory.CreateChannel();
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

var token = GetToken(); // Just an method that reads a pfx from disk
channelFactory.Credentials.
    ServiceCertificate.DefaultCertificate = token.Certificate; // throws exception
channelFactory.Credentials.
    ClientCertificate.Certificate = token.Certificate; // throws exception too

更新1

SetCertificate方法会抛出相同的异常:System.InvalidOperationException: "Object is read-only."

using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) 
{
    store.Open(OpenFlags.ReadWrite);
    var x509Certificate2Collection = store.Certificates.Find(X509FindType.FindByThumbprint, token.Certificate.Thumbprint, false);
    if(x509Certificate2Collection.Count == 0)
        store.Add(token.Certificate);
}

channelFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My,X509FindType.FindByThumbprint, token.Certificate.Thumbprint);

更新2

X509CertificateRecipientClientCredential.cs 的实现非常有趣。

public X509Certificate2 DefaultCertificate
{
    get
    {
        return _defaultCertificate;
    }
    set
    {
        ThrowIfImmutable();
        _defaultCertificate = value;
    }
}

internal void MakeReadOnly()
{
    _isReadOnly = true;
    this.Authentication.MakeReadOnly();
    if (_sslCertificateAuthentication != null)
    {
        _sslCertificateAuthentication.MakeReadOnly();
    }
}

private void ThrowIfImmutable()
{
    if (_isReadOnly)
    {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ObjectIsReadOnly)));
    }
}

有些东西在调用internal void MakeReadOnly(),让我的生活变得更加困难。


GetToken() 是什么?请阅读 [ask] 并 [edit] 您的问题以包括 [mcve]。 - Camilo Terevinto
@CamiloTerevinto,我编辑了问题以使其更清晰。 - hdev
1
如果您能将var替换为GetToken()所在行上的实际类型,这将有很大帮助。 - H H
我记得有一个ClientCertificate.SetCerticate(...)方法,你在这里看到了吗?它能用吗?列出你尝试过的任何死胡同。 - H H
而且你不能找出是谁调用了它吗?因为原因也会指向一个绕过它的方法。设置这些证书和凭据类型的方式有很多种。 - H H
@HenkHolterman 找到了 https://github.com/dotnet/wcf/blob/a2c95ad9e5cb2fff465655ae234454cf6f1c0f83/src/System.Private.ServiceModel/src/System/ServiceModel/Description/ClientCredentials.cs#L185 - hdev
1个回答

10

在阅读Github上的ClientCredentials.cs时,我发现了方法MakeReadOnly()

channelFactory.CreateChannel()的调用使得ClientCertificate实例只读,因此在更改语句顺序后它可以工作!

使用WCF进行客户端证书认证:

var binding = new BasicHttpsBinding();
var endpoint = new EndpointAddress(new Uri("https://myservice.com"));
var channelFactory = new ChannelFactory<MyService>(binding, endpoint);
// Must set before CreateChannel()
channelFactory.Credentials.
    ClientCertificate.Certificate = token.Certificate;

var serviceClient = channelFactory.CreateChannel();
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接