我尝试构建一个使用证书来保护它的wcf服务。当我在我的计算机上运行它(Windows 10)或在我的计算机上托管服务并从其他窗口10站使用它它工作正常,但当部署到服务器(Windows 2012 R2与iis 8.5)时,客户端无法使用该服务,并显示此错误:
该服务未对呼叫者进行身份验证。无法满足安全令牌请求,因为身份验证失败。 通信对象System.ServiceModel.Channels.ServiceChannel不能用于通信,因为它处于Faulted状态。
我制作了一个简单的控制台来测试安全性,对于我遵循指令here的证书。这是我构建的代码
client.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cliente
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("-------------------------------------");
try
{
using (ServerSinSeguridad.ServicioSinSeguridadClient sinSeguridad = new ServerSinSeguridad.ServicioSinSeguridadClient())
{
Console.WriteLine(sinSeguridad.MostrarDatos("Sin Seguridad"));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException.Message);
}
Console.WriteLine("-------------------------------------");
try
{
using (ServerConSeguridad.ServicioConSeguridadClient sinSeguridad = new ServerConSeguridad.ServicioConSeguridadClient())
{
Console.WriteLine(sinSeguridad.MostrarDatos("Con Seguridad"));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException.Message);
}
Console.WriteLine("-------------------------------------");
try
{
using (WSIIsSeguridad.Service1Client sinSeguridad = new WSIIsSeguridad.Service1Client())
{
var c = sinSeguridad.ChannelFactory.CreateChannel();
try
{
Console.WriteLine(c.GetData(123));
}
catch (Exception we)
{
Console.WriteLine(we.Message);
if (we.InnerException != null)
Console.WriteLine(we.InnerException.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException.Message);
}
Console.Read();
}
}
}
client.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="SinSeguridad" />
</basicHttpBinding>
<wsHttpBinding>
<binding name="ConSeguridad">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
<binding name="ConSeguridad1">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ConSeguridad">
<clientCredentials>
<clientCertificate findValue="CN=tempCertCliente"/>
</clientCredentials>
</behavior>
<behavior name="ConSeguridad1">
<clientCredentials>
<clientCertificate findValue="CN=tempCertCliente"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://localhost:8733/SinSeguridadServicio/"
binding="basicHttpBinding" bindingConfiguration="SinSeguridad"
contract="ServerSinSeguridad.IServicioSinSeguridad" name="SinSeguridad" />
<endpoint address="http://localhost:8732/ConSeguridadServicio/"
behaviorConfiguration="ConSeguridad" binding="wsHttpBinding"
bindingConfiguration="ConSeguridad" contract="ServerConSeguridad.IServicioConSeguridad"
name="ConSeguridad">
<identity>
<certificateReference storeName="My" storeLocation="LocalMachine"
x509FindType="FindBySubjectDistinguishedName" findValue="CN=tempCertService" />
</identity>
</endpoint>
<endpoint address="http://localhost:50873/Service1.svc" binding="wsHttpBinding"
behaviorConfiguration="ConSeguridad1"
bindingConfiguration="ConSeguridad1" contract="WSIIsSeguridad.IService1"
name="ConSeguridad1">
<identity>
<certificateReference storeName="My" storeLocation="LocalMachine"
x509FindType="FindBySubjectDistinguishedName" findValue="CN=tempCertService" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
自主wfc服务
Program.cs的
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace ServicioWcf
{
public class ServicioConSeguridad : IServicioConSeguridad
{
public string MostrarDatos(string dato)
{
Console.WriteLine(dato);
return $"Recibido {DateTime.Now}";
}
}
public class ServicioSinSeguridad : IServicioSinSeguridad
{
public string MostrarDatos(string dato)
{
Console.WriteLine(dato);
return $"Recibido {DateTime.Now}";
}
}
[ServiceContract]
public interface IServicioConSeguridad
{
[OperationContract]
string MostrarDatos(string dato);
}
[ServiceContract]
public interface IServicioSinSeguridad
{
[OperationContract]
string MostrarDatos(string dato);
}
class Program
{
static void Main(string[] args)
{
try
{
using (ServiceHost sinSeguridad = new ServiceHost(typeof(ServicioSinSeguridad)))
{
using (ServiceHost conSeguridad = new ServiceHost(typeof(ServicioConSeguridad)))
{
sinSeguridad.Faulted += serviceHost_Faulted;
sinSeguridad.UnknownMessageReceived += serviceHost_UnknownMessageReceived;
sinSeguridad.Open();
conSeguridad.Faulted += serviceHost_Faulted;
conSeguridad.UnknownMessageReceived += serviceHost_UnknownMessageReceived;
conSeguridad.Open();
Console.WriteLine("Listo, Escuchando");
Console.Read();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException.Message);
}
Console.Read();
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception)
Console.WriteLine(((Exception)e.ExceptionObject).Message);
else
Console.WriteLine(e.ExceptionObject.ToString());
}
static void Build(ref ServiceHost serviceHost, Type service)
{
if (serviceHost != null)
{
serviceHost.Close();
}
// Create a ServiceHost for the CalculatorService type and
// provide the base address.
serviceHost = new ServiceHost(service);
// Open the ServiceHostBase to create listeners and start
// listening for messages.
serviceHost.Open();
serviceHost.Faulted += serviceHost_Faulted;
serviceHost.UnknownMessageReceived += serviceHost_UnknownMessageReceived;
}
static void serviceHost_Faulted(object sender, EventArgs e)
{
}
static void serviceHost_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
{
Console.WriteLine(e.Message.ToString());
}
}
}
service.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ServicioSinSeguridad">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
<behavior name="ServicioConSeguridad">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<serviceCredentials>
<serviceCertificate findValue="CN=tempCertService" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="ServicioSinSeguridad" name="ServicioWcf.ServicioSinSeguridad">
<endpoint address="" binding="basicHttpBinding"
name="SinSeguridad" contract="ServicioWcf.IServicioSinSeguridad" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/SinSeguridadServicio/" />
</baseAddresses>
</host>
</service>
<service behaviorConfiguration="ServicioConSeguridad" name="ServicioWcf.ServicioConSeguridad">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBindingSeguridad"
name="ConSeguridad" contract="ServicioWcf.IServicioConSeguridad" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8732/ConSeguridadServicio/" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBindingSeguridad">
<security mode="Message">
<message clientCredentialType="Certificate" negotiateServiceCredential="true" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
IIS的wcf服务
WcfServiceIIS.svc
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfServiceIIS
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract(Namespace = "Esta.Es.Para.Vos")]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}
的web.config
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ServicioConSeguridad">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<serviceCredentials>
<clientCertificate>
<certificate storeLocation="LocalMachine" storeName="My" findValue="CN=tempCertCliente" x509FindType="FindBySubjectDistinguishedName"/>
</clientCertificate>
<serviceCertificate findValue="CN=tempCertService" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="ServicioConSeguridad" name="WcfServiceIIS.Service1">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBindingSeguridad" bindingNamespace="Esta.Es.Para.Vos"
name="ConSeguridad" contract="WcfServiceIIS.IService1" >
<identity>
<certificateReference findValue="CN=tempCertService" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" />
</identity>
</endpoint>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBindingSeguridad">
<readerQuotas maxDepth="32" maxStringContentLength="2147483646" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
<security mode="Message">
<message clientCredentialType="Certificate" negotiateServiceCredential="true" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<!--<protocolMapping>
<add binding="wsHttpBinding" scheme="https" />
</protocolMapping>-->
<!--<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />-->
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>