没有证书存储的WCF证书

我的团队正在为第三方胖客户端应用程序开发一些WPF插件。 WPF插件使用WCF来使用由许多TIBCO服务发布的Web服务。 胖客户端应用程序维护一个单独的中央数据存储,并使用专有API来访问数据存储。 胖客户端和WPF插件将部署到10,000个工作站上。 我们的客户希望将胖客户端使用的证书保留在中央数据存储中,这样他们就不必担心重新颁发证书(当前重发周期大约需要3个月)并且还有机会授权使用证书。 所提出的架构在中央数据存储和TIBCO服务之间提供了一种forms的共享秘密/认证。

虽然我不一定同意所提议的架构,但我们的团队无法改变它并且必须使用所提供的内容。

基本上我们的客户希望我们在我们的WPF插件中构建一个机制,该机制从中央数据存储(基于该数据存储中的角色允许或拒绝)检索证书到内存然后使用证书创建SSL连接到TIBCO服务。 不允许使用本地计算机的证书存储,并且在每个会话结束时丢弃内存版本。

所以问题是,是否有人知道是否可以将内存中的证书传递给WCF(.NET 3.5)服务以进行SSL传输级加密?

注意:我曾经问了一个类似的问题( 这里 ),但后来删除了它并重新询问了更多信息。

有可能的。 我们使用Mutual Certificate Auth做类似的事情 – 服务证书,在某些情况下,客户证书是从中央机构获取的,作为自动发现/单点登录机制的一部分。

在使用证书的上下文中并不完全清楚,但在所有情况下,您需要做的是定义您自己的行为和行为元素,该元素派生自获取证书的System.ServiceModel.Description命名空间中的特定行为/元素。 我暂时假设它是一个客户端凭证。 首先,您必须编写行为,如下所示:

 public class MyCredentials : ClientCredentials { public override void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime behavior) { // Assuming GetCertificateFromNetwork retrieves from CDS ClientCertificate.Certificate = GetCertificateFromNetwork(); } protected override ClientCredentials CloneCore() { // ... } } 

现在您需要创建一个可以进入XML配置的元素:

 public class MyCredentialsExtensionElement : ClientCredentialsElement { protected override object CreateBehavior() { return new MyCredentials(); } public override Type BehaviorType { get { return typeof(MyCredentials); } } // Snip other overrides like Properties } 

在此之后,您可以将策略添加到WCF配置:

        

编辑:几乎忘了提,你需要注册扩展名:

        

希望有所帮助。 如果您需要有关所有这些类的安排以及幕后发生的事情的更多详细信息,请尝试阅读使用自定义行为扩展WCF 。

我是那个让Kane(我们的SO lackey!)问原问题的人。 我想我最终会创建一个帐户并发布我们关于Aaronaught发布的答案的调查结果/结果/经验(所以对他的任何评价)。

我们尝试按照上面的建议添加自定义行为,并在端点配置元素上设置behaviourConfiguration以使用它。 我们无法完全解雇代码,因此最终采用了程序化方法。

因为我们有一个设置构建ClientBase对象的包装类,所以我们使用现有的创建函数在构建ClientBase的所有其他部分之后添加行为。

我们遇到了一些问题,即已经为我们的ClientBase定义了ClientCredentials行为,使用用户名和密码而不是我们的证书+用户名和密码进行身份validation。 因此,在添加新的基于证书的行为(注入了用户名和密码)作为测试的临时措施之前,我们以编程方式删除了现有行为。 仍然没有骰子,我们的行为正在被构造并且ApplyClientBehavior被解雇但是当调用Invoke时服务仍然没有结束(由于一堆难以重构的使用语句,我们从未得到真正的exception)。

然后,我们决定不再删除现有的ClientCredentials行为,我们只需将证书注入其中,然后让整个批次正常进行。 魅力的第三次,它现在全部起作用。

我要感谢Aaronaught(如果可以的话我会投票!)让我们走上正确的道路并提供一个经过深思熟虑和有用的答案。

下面是它的一小段代码片段(使用测试.CRT文件)。

  protected override ClientBase CreateClientBase(string endpointConfigurationName) { ClientBase clientBase = new ClientBase(endpointConfigurationName); // Construct yours however you want here // ... ClientCredentials credentials = clientBase.Endpoint.Behaviors.Find(); X509Certificate2 certificate = new X509Certificate2(); byte[] rawCertificateData = File.ReadAllBytes(@"C:\Path\To\YourCert.crt"); certificate.Import(rawCertificateData); credentials.ClientCertificate.Certificate = certificate; return clientBase; } 

另外注意,作为测试的一部分,我们从本地机器商店删除了所有证书,这实际上导致了使用Fiddler的问题。 Fiddler没有检测到我们的客户端证书,因为它纯粹是在内存中而不是在受信任的商店中。 如果我们将它添加回受信任的商店,那么Fiddler又开始玩得很好了。

再次感谢。

Aaronaught有正确的想法,但我不得不做一些修改才能让它发挥作用。 我使用的实现如下。 能够从嵌入式资源获取证书,我添加了更多function。

 using System.IO; using System.Linq; using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.ServiceModel.Configuration; using System.Configuration; using System.ServiceModel.Description; namespace System.ServiceModel.Description { ///  /// Uses a X509 certificate from disk as credentials for the client. ///  public class ClientCertificateCredentialsFromFile : ClientCredentials { public ClientCertificateCredentialsFromFile(CertificateSource certificateSource, string certificateLocation) { if (!Enum.IsDefined(typeof(CertificateSource), certificateSource)) { throw new ArgumentOutOfRangeException(nameof(certificateSource), $"{nameof(certificateSource)} contained an unexpected value."); } if (string.IsNullOrWhiteSpace(certificateLocation)) { throw new ArgumentNullException(nameof(certificateLocation)); } _certificateSource = certificateSource; _certificateLocation = certificateLocation; ClientCertificate.Certificate = certificateSource == CertificateSource.EmbeddedResource ? GetCertificateFromEmbeddedResource(certificateLocation) : GetCertificateFromDisk(certificateLocation); } ///  /// Retrieves a certificate from an embedded resource. ///  /// The certificate location and assembly information. Example: The.Namespace.certificate.cer, Assembly.Name /// A new instance of the embedded certificate. private static X509Certificate2 GetCertificateFromEmbeddedResource(string certificateLocation) { X509Certificate2 result = null; string[] parts = certificateLocation.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 2) { throw new ArgumentException($"{certificateLocation} was expected to have a format of namespace.resource.extension, assemblyName"); } string assemblyName = string.Join(",", parts.Skip(1)); var assembly = Assembly.Load(assemblyName); using (var stream = assembly.GetManifestResourceStream(parts[0])) { var bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); result = new X509Certificate2(bytes); } return result; } ///  /// Retrieves a certificate from disk. ///  /// The file path to the certificate. /// A new instance of the certificate from disk private static X509Certificate2 GetCertificateFromDisk(string certificateLocation) { if (!File.Exists(certificateLocation)) { throw new ArgumentException($"File {certificateLocation} not found."); } return new X509Certificate2(certificateLocation); } ///  /// Used to keep track of the source of the certificate. This is needed when this object is cloned. ///  private readonly CertificateSource _certificateSource; ///  /// Used to keep track of the location of the certificate. This is needed when this object is cloned. ///  private readonly string _certificateLocation; ///  /// Creates a duplicate instance of this object. ///  ///  /// A new instance of the certificate is created. /// A new instance of  protected override ClientCredentials CloneCore() { return new ClientCertificateCredentialsFromFile(_certificateSource, _certificateLocation); } } } namespace System.ServiceModel.Configuration { ///  /// Configuration element for  ///  ///  /// When configuring the behavior an extension has to be registered first. ///  ///  ///  ///  ///  ///  /// ]]> ///  /// Once the behavior is registered it can be used as follows. ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  /// ]]> ///  ///  public class ClientCertificateCredentialsFromFileElement : BehaviorExtensionElement { ///  /// Creates a new  from this configuration element. ///  /// The newly configured  protected override object CreateBehavior() { return new ClientCertificateCredentialsFromFile(Source, Location); } ///  /// Returns typeof(); ///  public override Type BehaviorType { get { return typeof(ClientCertificateCredentialsFromFile); } } ///  /// An attribute used to configure the file location of the certificate to use for the client's credentials. ///  [ConfigurationProperty("location", IsRequired = true)] public string Location { get { return this["location"] as string; } set { this["location"] = value; } } ///  /// An attribute used to configure where the certificate should should be loaded from. ///  [ConfigurationProperty("source", IsRequired = true)] public CertificateSource Source { get { return (CertificateSource)this["source"]; } set { this["source"] = value; } } } ///  /// Used to declare the source of a certificate. ///  public enum CertificateSource { FileOnDisk, EmbeddedResource } } 

使用上面的代码,我能够如下配置我的客户端