0

我有这个 WCF 网络服务,它提供了一个非常基本的查找服务(因此在服务器端没有更改任何数据)。该服务提供的信息是保密的,因此我必须采取适当的安全措施。

要做的标准事情是通过在服务绑定中要求有效的客户端证书来提供传输和消息安全性。我创建并颁发这些证书,以便控制谁可以使用此服务。但是(据我说,请在这里证明我错了)没有什么能阻止客户与其他客户(我没有授予他们访问我的服务的权限)交换他们的客户证书。只要提供的客户端证书有效,服务就会接受连接。添加的用户名/密码机制不会改变这一点,因为这些凭据也可以交换。

您可能会说客户在法律上有义务为自己保留证书,但这并不能为我提供足够的安全性。有没有办法专门控制谁与我的服务连接,而不必诉诸基于 IP 的访问控制(因为我的客户端使用 DHCP 颁发的 IP 地址,所以无法将 IP 耦合到客户端/证书)。

我最初的问题是以某种方式使用由在其证书存储中安装客户端证书(如 GUID)的客户端生成的唯一标识符,并在服务器端注册它,以便只有证书主题和该唯一标识符的组合可以访问服务。但我不知道是否有一种机制可以做到这一点。

编辑:由于我无法控制客户端,因此无法通过更改客户端的代码轻松解决此问题。

我真的很感谢任何指示/答案!

4

1 回答 1

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);
        }

感谢这篇文章,一个帮助类来获取唯一的 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);
于 2012-05-31T13:14:46.837 回答