客户端服务调用上以编程方式创建的Web服务失败

时间:2015-07-09 13:52:37

标签: c# .net web-services wcf ssl

我创建了一个控制台应用程序项目来以编程方式托管Web服务,但是当我尝试为我的Web服务创建客户端代理并在其上调用方法时,我收到以下错误:

  

发出HTTP请求时发生错误   https://localhost:8000/FileRetrievalPoC。这可能是因为事实   使用HTTP.SYS未正确配置服务器证书   在HTTPS的情况下。这也可能是由于不匹配引起的   客户端和服务器之间的安全绑定。

它的内在例外:

  

基础连接已关闭:发生意外错误   发送。

它的内在例外:

  

无法从传输连接中读取数据:现有数据   连接被远程主机强行关闭。

它的内在例外:

  

远程主机强行关闭现有连接

Program.cs的

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.My, "localhost");
        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", "exampleUsername", "examplePassword", StoreLocation.LocalMachine, StoreName.My, "localhost");
                proxy.Get(@"C:\Users\User\Desktop\Document.txt");
                ((IClientChannel)proxy).Close();
            }
            else if (key.Key == ConsoleKey.Escape)
                break;
        }
        FileRetrievalService.Stop();
    }
}

IFileRetrieval.cs

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

FileRetrievalService.cs

class FileRetrievalService : IFileRetrieval
{

    private static BasicHttpsBinding _binding = new BasicHttpsBinding()
    {
        Name = "FileRetrievalPoC",
        HostNameComparisonMode = HostNameComparisonMode.Exact,
        Security = new BasicHttpsSecurity()
        {
            Message = new BasicHttpMessageSecurity()
            {
                AlgorithmSuite = SecurityAlgorithmSuite.Basic256Sha256Rsa15,
                ClientCredentialType = BasicHttpMessageCredentialType.UserName
            },
            Mode = BasicHttpsSecurityMode.TransportWithMessageCredential,
            Transport = new HttpTransportSecurity()
            {
                ClientCredentialType = HttpClientCredentialType.Windows
            }
        },
        SendTimeout = TimeSpan.FromMinutes(1),
        CloseTimeout = TimeSpan.FromMinutes(1),
        OpenTimeout = TimeSpan.FromMinutes(1),
        ReceiveTimeout = TimeSpan.FromMinutes(1)
    };
    private static ChannelFactory<IFileRetrieval> _channelFactory;
    private static ServiceHost _host;

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

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

    public static IFileRetrieval Connect(string address, string domain, string username, string password, StoreLocation location, StoreName name, string subject)
    {
        if (_channelFactory == null)
            _channelFactory = new ChannelFactory<IFileRetrieval>(_binding, address);
        _channelFactory.Credentials.ClientCertificate.SetCertificate(location, name, X509FindType.FindBySubjectName, subject);
        _channelFactory.Credentials.UserName.UserName = username;
        _channelFactory.Credentials.UserName.Password = password;
        _channelFactory.Credentials.Windows.ClientCredential = new NetworkCredential(username, password, domain);
        return _channelFactory.CreateChannel();
    }

    public string Get(string path)
    {
        throw new NotImplementedException();
    }

    public void Set(string path, string contents)
    {
        throw new NotImplementedException();
    }
}

这一切都是以编程方式完成的,我查看了Stack Overflow,但找不到为什么会发生这种情况的充分理由。有谁知道问题是什么?这个源代码,你可以添加到一个新的控制台应用程序并运行它在本地计算机上试用它,看看它发生在你自己身上。它是SSL证书吗?如果是这样,我怎样才能在这里得到更多关于错误原因的详细信息?它不是一个非常有用的例外。

修改:我想我可能错过了这一步,such as using netsh to bind a certificate to my machine's port

1 个答案:

答案 0 :(得分:0)

我的问题是I did not use netsh to bind the certificate to my machine's port。打开管理命令提示符并调用:

netsh http add sslcert ipport=0.0.0.0:8000 appid=<A randomly generated GUID for your application> certhash=<Your localhost certificate's thumbprint from the default MY store, which is under Local Machine -> Personal, which you can get from the MMC Certificates snap-in>

下一步是确保客户端下的受信任人员。至少,对我来说就是这种情况,因为我使用的是我为localhost进行测试而生成的自签名证书。因此,例如,如果您从Comodo或Verisign或其他CA获得证书,则您的证书可能根本不需要此证书,因为根CA通常默认情况下会受信任,因为这些CA的根CA公共证书是在证书MMC管理单元的“受信任的根证书颁发机构”部分中开箱即用。

然后,您需要做的就是确保您的机器凭据正确无误。我正在使用Windows身份验证,因此它尝试断言我的凭据有效(这些在我的代码中在调用Connect方法时指定)。

随着年龄的增长,我发现我倾向于越来越多地回答自己的问题......

修改:您只需要为所有这些使用“受信任的人”商店。如果您确实要这样做,请在上面的代码和StoreName.TrustedPeople命令中使用netsh,指定certstorename=TrustedPeople,否则默认为MY,即{ {1}}存储在证书MMC管理单元中。

此外,要删除已绑定的SSL证书,请使用Personal

此外,我的代码不需要设置客户端证书才能运行,因此可以从netsh http delete sslcert ipport=0.0.0.0:8000方法中删除。如果你们中的任何人计划在生产中使用它,还需要更加紧缩。