簡體   English   中英

無法獲得證書消息憑據以在我的WCF服務中工作

[英]Can't get certificate message credentials to work in my WCF service

我制作了一個承載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對象上設置用戶名憑據。

我想增加安全性,並讓客戶端在連接到服務器時也提供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對象的先前邏輯。

這不起作用,並且出現以下異常:

接收到來自另一方的不安全或不正確安全的故障。 有關錯誤代碼和詳細信息,請參見內部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內部,我也從服務器端看到了很多異常。 第一個是:

安全處理器無法在消息中找到安全標頭。 這可能是因為該消息是不安全的故障,或者是由於通信雙方之間存在綁定不匹配。 如果將服務配置為具有安全性,並且客戶端未使用安全性,則會發生這種情況。

考慮到我已經指定了客戶端證書,我不確定為什么該消息沒有安全標頭。 客戶端代碼丟失了什么?

編輯 :啊,我也看到此錯誤:

密鑰大小為'8192'的'System.IdentityModel.Tokens.X509SecurityToken'令牌不滿足'Basic256Sha256Rsa15'算法套件的密鑰大小要求。

它的接收字節不足。 我的證書具有8192字節的私鑰和公鑰,但這不能與我選擇的AlgorithmSuite = SecurityAlgorithmSuite.Basic256Sha256Rsa15很好地匹配。 有人知道我在這里應該怎么允許這個證書嗎? 我可以嘗試為此使用256位密鑰...還是我的證書上的簽名算法是512位的問題?

我終於知道了。 錯誤使我誤入了一條黑暗的道路,但現在我明白了。

問題

這似乎是一條未成文的規則。 當您指定Mode = BasicHttpsSecurityMode.TransportWithMessageCredential告訴它同時使用消息和傳輸憑據時,然后使用證書作為消息憑據,

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

如果證書的密鑰超過4096位,則不能使用證書作為消息憑據。

解決方案

我使用SHA 512作為簽名算法,使用4096位RSA密鑰對制作了一個新的自簽名證書。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM