WCF、C#:发送签名的 LicenseKey (RSA)

WCF, C#: Send a signed LicenseKey (RSA)

我看了很多教程等等,但我仍然不确定正确的方法是什么。

场景如下:

我们的客户(客户端始终是服务器)在他们的网络中安装我们的网络应用程序。每个客户端都需要一个包含多个模块的许可证密钥。 为确保他们不会篡改许可证密钥(其中包含他们购买的模块等),我想使用非对称密钥对其进行签名。

客户端(客户端始终是服务器)想要在我们的服务器上激活许可证。 服务器知道客户端购买了哪种license/modules。 所以它会创建一个响应,其中应该包含签名的许可证密钥。

响应看起来像这样(客户就是服务 returns)

public class Customer 
{
   public string Identifier {get; set;}
   public LicenseKey LicenseKey {get; set; }

}

public class LicenseKey 
{
   public List<License>{get; set;}
}

public class License 
{
  //actual LicensingData
}

我想做的是签署"LicenseKey"。 客户端收到此许可证密钥并将其存储在数据库中,每 X 分钟验证一次它的完整性。

这是我的 question/problem。

我应该如何签署 LicenseKey,以便仅签署 WCF 响应的 "LicenseKey" 部分,重要的是,可以存储并且 used/verified 在 WCF 请求之外?

我知道 WCF 提供 "ProtectionLevel.Sign",但我必须创建 X509 证书才能使其正常工作,对吗? 这甚至会 work/verify 在 WCF 之外吗?

或者我应该编写一个消息拦截器来手动签署我的 LicenseKey 吗?

感谢您的帮助

由于使用签名保护许可证密钥的完整性是一项业务要求,我认为它应该是密钥数据结构本身的一部分。同样,签名应该在创建密钥时创建,而不是稍后在 WCF 堆栈中应用(这在技术上当然是可能的,但错误的地方恕我直言)。

如何对任意数据进行签名?

  • 创建数据的哈希表示
  • 计算散列上的签名

这是一个使用 RSACryptoServiceProvider 的快速演示。当然,如果需要,也可以使用完整的 X.509 证书。

using System.Security.Cryptography;
using System.Text;

namespace SignatureDemo
{
    public class Demo
    {
        static void Main(string[] args)
        {
            // "Main" is from the client point of view

            LicenseServer server = new LicenseServer();
            Signer signer = new Signer();

            // Obtain a key from the server
            LicenseKey key = server.GetKey("nodots");

            // Verify the signature of the unchanged key, this will result to true
            bool isValid1 = signer.VerifySignature(key, server.PublicKey);

            // Manipulate the license
            key.License.FeatureB = true;

            // Verify the signature of the changed key, this will result to false
            bool isValid2 = signer.VerifySignature(key, server.PublicKey);
        }
    }

    public class LicenseServer
    {
        // Contains both public and private key. This must stay secret!
        private readonly string _keyPair;

        private readonly Signer _signer = new Signer();

        public LicenseServer()
        {
            // Create demo key pair
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                _keyPair = rsa.ToXmlString(true);
                PublicKey = rsa.ToXmlString(false);
            }
        }    

        public string PublicKey
        {
            get;
        }

        public LicenseKey GetKey(string customerName)
        {
            LicenseKey key = new LicenseKey(new License(customerName, true, false));
            key.Signature = _signer.CreateSignature(key, _keyPair);

            return key;
        }
    }

    public class LicenseKey
    {
        public LicenseKey(License license)
        {
            License = license;
        }

        public License License
        {
            get;
            set;
        }

        public byte[] Signature
        {
            get;
            set;
        }
    }

    public class License
    {
        public License(string customerName, bool featureA, bool featureB)
        {
            CustomerName = customerName;
            FeatureA = featureA;
            FeatureB = featureB;
        }

        public string CustomerName
        {
            get;
            set;
        }

        public bool FeatureA
        {
            get;
            set;
        }

        public bool FeatureB
        {
            get;
            set;
        }
    }

    public class Signer
    {
        public byte[] CreateSignature(LicenseKey key, string privateKey)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.FromXmlString(privateKey);
                return rsa.SignData(ComputeHash(key), CryptoConfig.MapNameToOID("SHA256"));
            }
        }

        public bool VerifySignature(LicenseKey key, string publicKey)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.FromXmlString(publicKey);
                return rsa.VerifyData(ComputeHash(key), CryptoConfig.MapNameToOID("SHA256"), key.Signature);
            }
        }

        private static byte[] ComputeHash(LicenseKey key)
        {
            // Create a hash from the given key. 
            // For demo purposes I'm using a very simple string concatenation of the relevant properties.

            string simplifiedHash = string.Concat(key.License.CustomerName, key.License.FeatureA, key.License.FeatureB);
            return Encoding.UTF8.GetBytes(simplifiedHash);
        }
    }
}