简体   繁体   中英

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

I read multiple tutorials etc. but I'm still not sure what the correct way is.

Here the scenario:

Our clients (a client is always a server) install our web application in their network. Every client needs a license-key which contains multiple modules. To make sure they won't tamper with the license key (which holds the modules they bought etc.) I want to sign it with an asymmetric key.

A client (a client is always a server) wants to activate a license at our server. the server knows what kind of license/modules the client has purchased. so it creates a response which should contain the signed license key.

the response looks like this (Customer is what the services 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
}

What I want to do is sign the "LicenseKey". The client receives this license key and stores it in a database and every X minutes it verifies the integrity of it.

So here's my question/problem.

How should I sign the LicenseKey so only the "LicenseKey" portion of the WCF Response is signed and, important, can be stored and used/verified outside the WCF-Request?

I know WCF provides "ProtectionLevel.Sign", but I would have to create X509 certificates to get it to work, right? And would this even work/verify outside WCF?

Or should I write a message interceptor to manually sign my LicenseKey?

Thanks for your help

Since it's a business requirement to protect the license key's integrity with a signature I think it should be part of the key data structure itself. Similarly, the signature should be created when the key is created, not applied later on in the WCF stack (which is technically possible of course but the wrong place IMHO).

How to sign arbitray data?

  • Create a hash representation of the data
  • Compute the signature on the hash

Here's a quick demo using RSACryptoServiceProvider . Of course full X.509 certificates could be used as well if needed.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM