[英]How to configure a wcf service client to use both certificate and username credentials at the message level?
[英]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);
}
以上所有代码均有效,因为:
ClientCredentialType = BasicHttpMessageCredentialType.UserName
。 _channelFactory.Credentials.UserName
对象上设置用户名凭据。 我想增加安全性,并让客户端在连接到服务器时也提供SSL证书。 为了尝试执行此操作,我在代码中用以下逻辑替换了点1和2:
ClientCredentialType = BasicHttpMessageCredentialType.Certificate
而不是ClientCredentialType = BasicHttpMessageCredentialType.UserName
。 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.