using System; using System.IO; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using System.Security; using System.Security.Cryptography; using System.Windows.Forms; namespace ClassLibrary.Security { /// /// the license agreement for the software. /// embedded are the license terms (ie start and end dates) and a digital signature used to verify the /// the license terms. this way, the consumer may be able to see what the license terms are, but if they attempt to change them /// (in order to extend thier license) then they will not be able to generate a matching signature. /// public class License { #region Properties /// /// the license terms. obscured. /// public string LicenseTerms { get; set; } /// /// the signature. /// public string Signature { get; set; } #endregion #region Methods /// /// saves the license to an xml file. /// /// public void Save(String fileName) { Serializer.Save(this, fileName); } /// /// saves the license to a stream as xml. /// /// public void Save(Stream stream) { Serializer.Save(this, stream); } /// /// create a license object from a license file. /// /// /// public static License Load(String fileName) { // read the filename: return Serializer.Load(new FileInfo(fileName)); } /// /// load a license from stream xml data. /// /// /// public static License Load(Stream data) { // read the data stream: return Serializer.Load(data); } #endregion } /// /// handles license authorization. /// public class LicenseAuthorization { /// /// terms of the license agreement: it's not encrypted (but is obscured) /// [Serializable] internal class LicenseTerms { /// /// start date of the license agreement. /// public DateTime StartDate { get; set; } /// /// registered user name for the license agreement. /// public String UserName { get; set; } /// /// the assembly name of the product that is licensed. /// public String ProductName { get; set; } /// /// the last date on which the software can be used on this license. /// public DateTime EndDate { get; set; } /// /// the Fingerprint of user's computer. /// public String FingerPrint { get; set; } /// /// Expired on user's computer. /// public bool Expired { get; set; } /// /// License for technology on user's computer. /// public bool Treko { get; set; } /// /// License for technology on user's computer. /// public bool Treko3D { get; set; } /// /// returns the license terms as an obscure (not human readable) string. /// /// public String GetLicenseString() { using (MemoryStream ms = new MemoryStream()) { // create a binary formatter: BinaryFormatter bnfmt = new BinaryFormatter(); // serialize the data to the memory-steam; bnfmt.Serialize(ms, this); // return a base64 string representation of the binary data: return Convert.ToBase64String(ms.GetBuffer()); } } /// /// returns a binary representation of the license terms. /// /// public byte[] GetLicenseData() { using (MemoryStream ms = new MemoryStream()) { // create a binary formatter: BinaryFormatter bnfmt = new BinaryFormatter(); // serialize the data to the memory-steam; bnfmt.Serialize(ms, this); // return a base64 string representation of the binary data: return ms.GetBuffer(); } } /// /// create a new license-terms object from a string-representation of the binary /// serialization of the licence-terms. /// /// /// internal static LicenseTerms FromString(String licenseTerms) { using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(licenseTerms))) { // create a binary formatter: BinaryFormatter bnfmt = new BinaryFormatter(); // serialize the data to the memory-steam; object value = bnfmt.Deserialize(ms); if (value is LicenseTerms) return (LicenseTerms)value; else throw new ApplicationException("Invalid Type!"); } } } #region params only for test /// /// start date of the license agreement. /// public static DateTime StartDate { get; set; } /// /// registered user name for the license agreement. /// public static String UserName { get; set; } /// /// registered Expired for the license agreement. /// public static bool Expired { get; set; } /// /// registered Treko for the license agreement. /// public static bool Treko { get; set; } /// /// registered Treko3D for the license agreement. /// public static bool Treko3D { get; set; } /// /// the assembly name of the product that is licensed. /// public static String ProductName { get; set; } /// /// the last date on which the software can be used on this license. /// public static DateTime EndDate { get; set; } public static string Fingerprint { get; set; } #endregion /// /// builds a user-license pack. This includes the public-key that must be embedded in the application, /// and the private key (which must be kept secure) and a license-file for each user, specific to the /// currently executing assembly, with the specified end date. Start date for the user-license file is /// current date. /// /// /// /// public static void GenerateLicensePack(String outputFolder, String[] userNames, DateTime[] endDates) { // if the key files don't exist..create them: if (!File.Exists(outputFolder + "\\privateKey.xml")) GenerateLicenseResources(outputFolder); // generate each user-license for the current assembly: int i = 0; foreach (String userName in userNames) { // generate each license file: GenerateUserLicenseFile(outputFolder, userName, endDates[i++]); } } /// /// generate the public and private key files in the specified folder. /// /// public static void GenerateLicenseResources(String outputFolder) { // create the directory if it doesn't exist: if (!Directory.Exists(outputFolder)) { Directory.CreateDirectory(outputFolder); } // generate the required key files: String publicKeyFile = outputFolder + "\\publicKey.xml"; String privateKeyFile = outputFolder + "\\privateKey.xml"; // create a new private key: String privateKey = GeneratePrivateKey(); // extract the public part of the key: String publicKey = GetPublicKey(privateKey); // save them: File.WriteAllText(publicKeyFile, publicKey); File.WriteAllText(privateKeyFile, privateKey); } /// /// generate a user-license file. /// /// /// /// /// /// /// public static void GenerateUserLicenseFile(String licenseResourceFolder, String userName, DateTime endDate) { // find and load the private key: String privateKeyFile = licenseResourceFolder + "\\privateKey.xml"; bool licExpired = false; bool licTreko = true; bool licTreko3D = true; // check the key file exists: if (File.Exists(privateKeyFile)) { // load the private key: String privateKey = File.ReadAllText(privateKeyFile); String fingerPrint = FingerPrint.Value(); // generate the license file; License license = CreateLicense( DateTime.Now, endDate, Assembly.GetExecutingAssembly().FullName, userName, licExpired, licTreko, licTreko3D, fingerPrint, privateKey ); // save the license file: license.Save(licenseResourceFolder + "\\" + userName + ".lic"); // show success: MessageBox.Show("User License: " + userName + ".lic Created"); } else { // can't find the key-file: MessageBox.Show("Can't find private-key file: " + privateKeyFile); } } /// /// runs a test of the licensing system from: /// C:\temp\user2.lic and C:\temp\user2_publicKey.xml /// public static bool TestTestLicense() { string workDir = AppDomain.CurrentDomain.BaseDirectory; License l = License.Load(workDir + @"user2.lic"); if (l != null) { try { String pkey = File.ReadAllText(workDir + @"user2_publicKey.xml"); ValidateLicense(l, pkey); //MessageBox.Show("License is Valid"); return true; } catch (SecurityException se) { //MessageBox.Show("License INVALID: " + se.Message); return false; } } else { return false; } } /// /// generate the required files for TestTestLicense() /// C:\temp\user2.lic and C:\temp\user2_publicKey.xml /// public static void SaveDefaultLicense() { String privateKey = GeneratePrivateKey(); String fingerprint = FingerPrint.Default(); String workDir = AppDomain.CurrentDomain.BaseDirectory; DateTime defaultStart = DateTime.Parse("01.01.2018"); DateTime defaultEnd = DateTime.Parse("01.01.2019"); bool licExpired = false; bool licTreko = true; bool licTreko3D = true; License l = CreateLicense(defaultStart, defaultEnd, Assembly.GetExecutingAssembly().FullName, "Simon", licExpired, licTreko, licTreko3D, fingerprint, privateKey); l.Save(workDir + @"user2.lic"); File.WriteAllText(workDir + @"user2_publicKey.xml", GetPublicKey(privateKey)); File.WriteAllText(workDir + @"system_privateKey.xml", privateKey); StartDate = defaultStart; EndDate = defaultEnd; ProductName = Assembly.GetExecutingAssembly().FullName; UserName = "Simon"; } /// /// generate the required files for TestTestLicense() /// C:\temp\user2.lic and C:\temp\user2_publicKey.xml /// public static void SaveTestLicense(string userName, DateTime startDate, DateTime endDate, bool licExpired, bool licTreko, bool licTreko3D) { String privateKey = GeneratePrivateKey(); String fingerprint = FingerPrint.Value(); string workDir = AppDomain.CurrentDomain.BaseDirectory; License l = CreateLicense(startDate, endDate, Assembly.GetExecutingAssembly().FullName, userName, licExpired, licTreko, licTreko3D, fingerprint, privateKey); l.Save(workDir + @"user2.lic"); File.WriteAllText(workDir + @"user2_publicKey.xml", GetPublicKey(privateKey)); File.WriteAllText(workDir + @"system_privateKey.xml", privateKey); StartDate = startDate; EndDate = endDate; ProductName = Assembly.GetExecutingAssembly().FullName; UserName = userName; } /// /// generate a new, private key. this will be the master key for generating license files. /// /// public static String GeneratePrivateKey() { DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); return dsa.ToXmlString(true); } /// /// get the public key from a private key. this key must be distributed with the application. /// /// /// public static String GetPublicKey(String privateKey) { DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); dsa.FromXmlString(privateKey); return dsa.ToXmlString(false); } /// /// use a private key to generate a secure license file. the private key must match the public key accessible to /// the system validating the license. /// /// applicable start date for the license file. /// applicable end date for the license file /// applicable product name /// user-name /// computer's fingerprint /// the private key (in XML form) /// secure, public license, validated with the public part of the key public static License CreateLicense(DateTime start, DateTime end, String productName, String userName, bool licExpired, bool licTreko, bool licTreko3D, String fingerprint, String privateKey) { // create the licence terms: LicenseTerms terms = new LicenseTerms() { StartDate = start, EndDate = end, ProductName = productName, UserName = userName, Expired = licExpired, Treko = licTreko, Treko3D = licTreko3D, FingerPrint = fingerprint }; // create the crypto-service provider: DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); // setup the dsa from the private key: dsa.FromXmlString(privateKey); // get the byte-array of the licence terms: byte[] license = terms.GetLicenseData(); // get the signature: byte[] signature = dsa.SignData(license); // now create the license object: return new License() { LicenseTerms = Convert.ToBase64String(license), Signature = Convert.ToBase64String(signature) }; } /// /// use a private key to generate a secure license file. the private key must match the public key accessible to /// the system validating the license. /// /// applicable start date for the license file. /// applicable end date for the license file /// applicable product name /// user-name /// computer's fingerprint /// the private key (in XML form) /// secure, public license, validated with the public part of the key public static License UpdateLicense(DateTime start, DateTime end, String productName, String userName, bool licExpired, bool licTreko, bool licTreko3D, String fingerprint, String privateKey) { // create the licence terms: LicenseTerms terms = new LicenseTerms() { StartDate = start, EndDate = end, ProductName = productName, UserName = userName, Expired = licExpired, Treko = licTreko, Treko3D = licTreko3D, FingerPrint = fingerprint }; // create the crypto-service provider: DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); // setup the dsa from the private key: dsa.FromXmlString(privateKey); // get the byte-array of the licence terms: byte[] license = terms.GetLicenseData(); // get the signature: byte[] signature = dsa.SignData(license); // now create the license object: return new License() { LicenseTerms = Convert.ToBase64String(license), Signature = Convert.ToBase64String(signature) }; } /// /// validates the license and if the app should run; if the license is valid the /// method will complete, if not it will throw a security exception. /// /// /// the license object. /// /// thrown if the license is invalid or expired /// public static void ValidateLicense(License license, String publicKey) { // get the valid terms for the license: (this checks the digital signature on the license file) LicenseTerms terms = GetValidTerms(license, publicKey); // ensure a valid license-terms object was returned: if (terms != null) { // validate the date-range of the license terms: if (DateTime.Now.CompareTo(terms.EndDate) <= 0) { if (DateTime.Now.CompareTo(terms.StartDate) >= 0) { // date range is valid... check the product name against the current assembly if (Assembly.GetExecutingAssembly().FullName == terms.ProductName) { string fingerPrint = FingerPrint.Value(); if (String.Compare(fingerPrint, terms.FingerPrint) == 0) { #region for test only //StartDate = terms.StartDate; //EndDate = terms.EndDate; //ProductName = terms.ProductName; //Fingerprint = terms.FingerPrint; //UserName = terms.UserName; //Expired = terms.Expired; //Treko = terms.Treko; //Treko3D = terms.Treko3D; #endregion return; } else { // computer's fingerprint doesn't match. throw new SecurityException("Invalid Computer's Fingerprint: " + terms.FingerPrint); } } else { // product name doesn't match. throw new SecurityException("Invalid Product Name: " + terms.ProductName); } } else { // license terms not valid yet. throw new SecurityException("License Terms Not Valid Until: " + terms.StartDate.ToShortDateString()); } } else { // license terms have expired. throw new SecurityException("License Terms Expired On: " + terms.EndDate.ToShortDateString()); } } else { // the license file was not valid. throw new SecurityException("Invalid License File!"); } } /// /// validate license file and return the license terms. /// /// /// /// internal static LicenseTerms GetValidTerms(License license, String publicKey) { // create the crypto-service provider: DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); // setup the provider from the public key: dsa.FromXmlString(publicKey); // get the license terms data: byte[] terms = Convert.FromBase64String(license.LicenseTerms); // get the signature data: byte[] signature = Convert.FromBase64String(license.Signature); // verify that the license-terms match the signature data if (dsa.VerifyData(terms, signature)) return LicenseTerms.FromString(license.LicenseTerms); else throw new SecurityException("Signature Not Verified!"); } } }