无法获取证书消息凭据以在我的WCF服务

时间:2015-07-09 19:37:47

标签: c# .net wcf authentication ssl

我制作了一个托管WCF服务的控制台应用程序。我现在正在尝试修改它。这是目前的工作......

Program.cs的

namespace FileRetrievalPoC
{
    class Program
    {
        static void Main(string[] args)
        {
            var address = "https://localhost:8000/FileRetrievalPoC";
            Console.WriteLine("Starting a service at {0}...", address);
            FileRetrievalService.Start(address, StoreLocation.LocalMachine, StoreName.TrustedPeople, "b7ba8f6e4c33ba55053f2e85898b383e40bf8ac9");
            Console.WriteLine("Service started.");
            Console.WriteLine("Press Enter to create a new proxy client and call the Get method.");
            Console.WriteLine("Press Escape to end the application.");
            while (true)
            {
                var key = Console.ReadKey();
                if (key.Key == ConsoleKey.Enter)
                {
                    var proxy = FileRetrievalService.Connect(address, "localhost", "username", "password");
                    Console.WriteLine("Read the following from the server: {0}", proxy.Get(@"C:\Users\User\Desktop\Document.txt"));
                    FileRetrievalService.CloseClients();
                }
                else if (key.Key == ConsoleKey.Escape)
                    break;
            }
            FileRetrievalService.CloseServer();
        }
    }
}

FileRetrievalService.cs

class FileRetrievalService : IFileRetrieval
{

    private static BasicHttpsBinding _binding = new BasicHttpsBinding()
    {
        HostNameComparisonMode = HostNameComparisonMode.Exact,
        Security = new BasicHttpsSecurity()
        {
            Message = new BasicHttpMessageSecurity()
            {
                AlgorithmSuite = SecurityAlgorithmSuite.Basic256Sha256Rsa15,
                ClientCredentialType = BasicHttpMessageCredentialType.UserName
            },
            Mode = BasicHttpsSecurityMode.TransportWithMessageCredential,
            Transport = new HttpTransportSecurity()
            {
                ClientCredentialType = HttpClientCredentialType.Windows,
            }
        }
    };
    private static ChannelFactory<IFileRetrieval> _channelFactory;
    private static ServiceHost _host;

    public static void Start(string address, StoreLocation location, StoreName name, string thumbprint)
    {
        _host = new ServiceHost(typeof(FileRetrievalService));
        _host.Credentials.ServiceCertificate.SetCertificate(location, name, X509FindType.FindByThumbprint, thumbprint);
        _host.AddServiceEndpoint(typeof(IFileRetrieval), _binding, address);
        _host.Open();
    }

    public static void CloseClients()
    {
        if (_channelFactory != null)
            _channelFactory.Close();
        _channelFactory = null;
    }

    public static void CloseServer()
    {
        if (_host != null)
            _host.Close();
        _host = null;
    }

    public static void CloseServerAndClients()
    {
        CloseServer();
        CloseClients();
    }

    public static IFileRetrieval Connect(string address, string domain, string username, string password)
    {
        if (_channelFactory == null)
        {
            _channelFactory = new ChannelFactory<IFileRetrieval>(_binding, address);
            _channelFactory.Credentials.UserName.UserName = domain + '\\' + username;
            _channelFactory.Credentials.UserName.Password = password;
            _channelFactory.Credentials.Windows.ClientCredential = new NetworkCredential(username, password, domain);
        }
        return _channelFactory.CreateChannel();
    }

    public string Get(string path)
    {
        return File.ReadAllText(path);
    }

    public void Set(string path, string contents)
    {
        File.WriteAllText(path, contents);
    }
}

IFileRetrieval.cs

[ServiceContract]
public interface IFileRetrieval
{
    [OperationContract]
    string Get(string path);
    [OperationContract]
    void Set(string path, string contents);
}

以上所有代码都有效,因为:

  1. 服务器端绑定ClientCredentialType = BasicHttpMessageCredentialType.UserName
  2. 客户端在_channelFactory.Credentials.UserName对象上设置用户名凭据。
  3. 我想添加更多安全性,并让客户端在连接到服务器时也提供SSL证书。为了尝试这样做,我使用以下逻辑在第1点和第2点的代码中进行了以下替换:

    1. 我尝试在绑定上使用ClientCredentialType = BasicHttpMessageCredentialType.Certificate而不是ClientCredentialType = BasicHttpMessageCredentialType.UserName
    2. Connect方法中,我现在设置客户端的证书(_channelFactory.Credentials.ClientCertificate.SetCertificate(location, name, X509FindType.FindByThumbprint, thumbprint);)并修改方法以获取相应的证书信息(StoreLocation location, StoreName name, string thumbprint),并将完全相同的参数传递给从客户端引用从服务器端呈现的相同证书。我删除了以前设置_channelFactory.Credentials.UserName对象的逻辑。
    3. 这不起作用,我得到以下例外:

        

      从另一方收到了不安全或不正确安全的故障   派对。请参阅内部FaultException以获取故障代码和详细信息。

      它的内部例外说:

        

      验证邮件的安全性时发生错误。

      从哪里开始?我没有使用IIS,所以如何进一步跟踪此错误?我有点失落。我希望有人可以解释可能出错的地方,以及我可以做些什么来修复错误或进一步调试错误。

      编辑:我想出了一种进一步调试它的方法,但它没有比我想象的更有帮助。在App.config内,我添加了以下跟踪:

      <system.diagnostics>
        <sources>
          <source name="System.ServiceModel" switchValue="Verbose, ActivityTracing" propagateActivity="true">
            <listeners>
              <add name="listener" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "C:\Users\YourAccountUsername\Desktop\WCFTraces.svclog" />
            </listeners>
          </source>
          <source name="System.ServiceModel.MessageLogging" switchValue="Verbose, ActivityTracing" propagateActivity="true">
            <listeners>
              <add name="listener" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "C:\Users\YourAccountUsername\Desktop\WCFTraces.svclog" />
            </listeners>
          </source>
        </sources>
      </system.diagnostics>
      

      现在在WCFTraces.svclog内部,我也看到了服务器端的一些例外情况。第一个是:

        

      安全处理器无法在中找到安全标头   信息。这可能是因为消息是不安全的故障或   因为通信方之间存在绑定不匹配。   如果为安全性和服务配置了服务,则会发生这种情况   客户端没有使用安全性。

      考虑到我已经指定了客户端证书,我不确定为什么消息没有安全标头。什么是客户端代码丢失?

      编辑:啊,我也看到了这个错误:

        

      'Basic256Sha256Rsa15'算法的密钥大小要求   套房不符合   具有密钥的'System.IdentityModel.Tokens.X509SecurityToken'令牌   大小'8192'。

      它收到的字节数不足。我的证书有一个8192字节的私钥和公钥,但这不能与我选择的AlgorithmSuite = SecurityAlgorithmSuite.Basic256Sha256Rsa15匹配。有谁知道我应该做什么来允许这个证书?我可以尝试使用256位密钥......或者我的证书上签名算法是512位的问题是什么?

1 个答案:

答案 0 :(得分:1)

我终于明白了。这些错误误导了我一条黑暗的道路,但现在我明白了。

问题

这似乎是一条不成文的规则。当您指定Mode = BasicHttpsSecurityMode.TransportWithMessageCredential以告知它使用邮件和传输凭据,然后您使用证书作为邮件凭据时,

Message = new BasicHttpMessageSecurity()
{
    AlgorithmSuite = SecurityAlgorithmSuite.Basic256Sha256Rsa15,
    ClientCredentialType = BasicHttpMessageCredentialType.Certificate
}

如果密钥的密钥超过4096位,则无法使用证书作为凭据凭证。

解决方案

我使用SHA 512作为签名算法,使用4096位RSA密钥对创建了一个新的自签名证书。