我有这个WCF web服务,它提供了一个非常基本的查找服务(所以在服务器端没有更改数据)。该服务提供的信息是保密的,因此我必须采取适当的安全措施。
要做的标准事情是通过在服务绑定中要求有效的客户端证书来提供传输和消息安全性。我创建并颁发这些证书,以便控制谁可以使用此服务。但是(据我所知,请在这里证明我错了)没有什么能阻止客户与其他客户(我没有授权访问我的服务)交换客户证书。只要提供的客户端证书有效,服务就会接受连接。添加的用户名/密码机制不会改变这一点,因为这些凭证也可以交换。
您可能会说客户有法律义务将证书保留给自己,但这并不能为我提供足够的安全保障。有没有办法专门控制谁与我的服务连接,而不必诉诸基于IP的访问控制(因为我的客户端使用DHCP发布的IP地址,没有办法将IP耦合到客户端/证书)。
我的初始问题是以某种方式使用客户端生成的唯一标识符,该客户端在其证书存储区中安装客户端证书(如GUID)并在服务器端注册,以便只有证书主题和唯一标识符可以访问该服务。但我不知道是否有机制可以做到这一点。
编辑:由于我无法控制客户端,因此无法通过更改客户端的代码轻松解决此问题。
我非常感谢任何指针/答案!
答案 0 :(得分:2)
我使用IDispatchMessageInspector
。虽然不确定这是否是一种性能最佳的方法,但事实证明它的工作正常。
我的客户使用邮件标头发送其唯一的GUID。 每个GUID对应于每个客户端证书,并且这些证书存储在Windows注册表中。 GUID由客户端生成(我使用UuidCreateSequential())
我将与您分享我的解决方案。这是我的服务代码:
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
IList<IIdentity> identities = OperationContext.Current.ServiceSecurityContext.AuthorizationContext.Properties["Identities"] as IList<IIdentity>;
string clientGuid = request.Headers.GetHeader<string>("Guid", "MyNamespace");
if (clientGuid == null)
throw new FaultException("Client GUID not sent!");
Guid testGuid = Guid.Empty;
if (!Guid.TryParse(clientGuid, out testGuid))
{
throw new FaultException(string.Format("The format of the GUID '{0}' is not valid!", clientGuid));
}
IIdentity X509Identity = identities.Where(x => x.AuthenticationType == "X509").SingleOrDefault();
RegistryKey identityKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\MySoftware\\Identities");
string storedSubjectName = (string)identityKey.GetValue(clientGuid);
if (storedSubjectName == null)
{
string[] valueNames = identityKey.GetValueNames();
for (int idx = 0; idx < valueNames.Count(); idx++)
{
string testCN = (string)identityKey.GetValue(valueNames[idx]);
if (testCN == X509Identity.Name)
{
throw new FaultException(string.Format("Certificate '{0}' has already been registered!", X509Identity.Name));
}
}
identityKey.SetValue(clientGuid, X509Identity.Name, RegistryValueKind.String);
}
else
{
if (storedSubjectName != X509Identity.Name)
throw new FaultException(string.Format("Certificate '{0}' used by the user registered with the certificate '{0}'", X509Identity.Name, storedSubjectName));
}
identityKey.Close();
return null;
}
客户端上的代码:
在应用启动时
RegistryKey guidKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\MySoftware\\Settings");
string clientGuid = (string)guidKey.GetValue("Guid");
if (clientGuid == null)
{
clientGuid = UUID.mGetNewGUID().ToString();
guidKey.SetValue("Guid", clientGuid, RegistryValueKind.String);
}
通过this article 获取唯一UUID的助手类
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;
static class UUID
{
// ==========================================================
// GUID Creation
// ==========================================================
private const int RPC_S_OK = 0;
private const int RPC_S_OUT_OF_MEMORY = 14;
private const int RPC_S_UUID_NO_ADDRESS = 1739;
private const int RPC_S_UUID_LOCAL_ONLY = 1824;
[DllImport("rpcrt4.dll", SetLastError = true)]
public static extern int UuidCreateSequential(ref Guid guid);
[DllImport("rpcrt4.dll", SetLastError = true)]
public static extern int UuidCreate(ref Guid guid);
/// <summary>
/// Creates the machine GUID.
/// </summary>
public static Guid mGetNewGUID()
{
Guid guidMachineGUID = default(Guid);
int intReturnValue = UuidCreateSequential(ref guidMachineGUID);
switch (intReturnValue)
{
case RPC_S_OK:
return guidMachineGUID;
case RPC_S_OUT_OF_MEMORY:
throw new Exception("UuidCreate returned RPC_S_OUT_OF_MEMORY");
case RPC_S_UUID_NO_ADDRESS:
throw new Exception("UuidCreate returned RPC_S_UUID_NO_ADDRESS");
case RPC_S_UUID_LOCAL_ONLY:
throw new Exception("UuidCreate returned RPC_S_UUID_LOCAL_ONLY");
default:
throw new Exception("UuidCreate returned an unexpected value = " + intReturnValue.ToString());
}
}
}
BeforeSendRequest
IClientMessageInspector
var guidHeader = new MessageHeader<string>(Service.ClientGuid);
var untypedGuid = guidHeader.GetUntypedHeader("Guid", "MyNamespace");
request.Headers.Add(untypedGuid);