BigLitho/Large.Lito.Database/Make3_Treko3D/ClassLibrary/Security/RSA.cs

632 lines
23 KiB
C#

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
{
/// <summary>
/// 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.
/// </summary>
public class License
{
#region Properties
/// <summary>
/// the license terms. obscured.
/// </summary>
public string LicenseTerms { get; set; }
/// <summary>
/// the signature.
/// </summary>
public string Signature { get; set; }
#endregion
#region Methods
/// <summary>
/// saves the license to an xml file.
/// </summary>
/// <param name="fileName"></param>
public void Save(String fileName)
{
Serializer.Save<License>(this, fileName);
}
/// <summary>
/// saves the license to a stream as xml.
/// </summary>
/// <param name="stream"></param>
public void Save(Stream stream)
{
Serializer.Save<License>(this, stream);
}
/// <summary>
/// create a license object from a license file.
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static License Load(String fileName)
{
// read the filename:
return Serializer.Load<License>(new FileInfo(fileName));
}
/// <summary>
/// load a license from stream xml data.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static License Load(Stream data)
{
// read the data stream:
return Serializer.Load<License>(data);
}
#endregion
}
/// <summary>
/// handles license authorization.
/// </summary>
public class LicenseAuthorization
{
/// <summary>
/// terms of the license agreement: it's not encrypted (but is obscured)
/// </summary>
[Serializable]
internal class LicenseTerms
{
/// <summary>
/// start date of the license agreement.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// registered user name for the license agreement.
/// </summary>
public String UserName { get; set; }
/// <summary>
/// the assembly name of the product that is licensed.
/// </summary>
public String ProductName { get; set; }
/// <summary>
/// the last date on which the software can be used on this license.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// the Fingerprint of user's computer.
/// </summary>
public String FingerPrint { get; set; }
/// <summary>
/// Expired on user's computer.
/// </summary>
public bool Expired { get; set; }
/// <summary>
/// License for technology on user's computer.
/// </summary>
public bool Treko { get; set; }
/// <summary>
/// License for technology on user's computer.
/// </summary>
public bool Treko3D { get; set; }
/// <summary>
/// returns the license terms as an obscure (not human readable) string.
/// </summary>
/// <returns></returns>
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());
}
}
/// <summary>
/// returns a binary representation of the license terms.
/// </summary>
/// <returns></returns>
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();
}
}
/// <summary>
/// create a new license-terms object from a string-representation of the binary
/// serialization of the licence-terms.
/// </summary>
/// <param name="licenseTerms"></param>
/// <returns></returns>
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
/// <summary>
/// start date of the license agreement.
/// </summary>
public static DateTime StartDate { get; set; }
/// <summary>
/// registered user name for the license agreement.
/// </summary>
public static String UserName { get; set; }
/// <summary>
/// registered Expired for the license agreement.
/// </summary>
public static bool Expired { get; set; }
/// <summary>
/// registered Treko for the license agreement.
/// </summary>
public static bool Treko { get; set; }
/// <summary>
/// registered Treko3D for the license agreement.
/// </summary>
public static bool Treko3D { get; set; }
/// <summary>
/// the assembly name of the product that is licensed.
/// </summary>
public static String ProductName { get; set; }
/// <summary>
/// the last date on which the software can be used on this license.
/// </summary>
public static DateTime EndDate { get; set; }
public static string Fingerprint { get; set; }
#endregion
/// <summary>
/// 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.
/// </summary>
/// <param name="outputFolder"></param>
/// <param name="userNames"></param>
/// <param name="endDates"></param>
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++]);
}
}
/// <summary>
/// generate the public and private key files in the specified folder.
/// </summary>
/// <param name="outputFolder"></param>
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);
}
/// <summary>
/// generate a user-license file.
/// </summary>
/// <param name="licenseResourceFolder"></param>
/// <param name="userName"></param>
/// <param name="endDate"></param>
/// <param name="licExpired"></param>
/// <param name="licTreko"></param>
/// <param name="licTreko3D"></param>
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);
}
}
/// <summary>
/// runs a test of the licensing system from:
/// C:\temp\user2.lic and C:\temp\user2_publicKey.xml
/// </summary>
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;
}
}
/// <summary>
/// generate the required files for TestTestLicense()
/// C:\temp\user2.lic and C:\temp\user2_publicKey.xml
/// </summary>
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";
}
/// <summary>
/// generate the required files for TestTestLicense()
/// C:\temp\user2.lic and C:\temp\user2_publicKey.xml
/// </summary>
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;
}
/// <summary>
/// generate a new, private key. this will be the master key for generating license files.
/// </summary>
/// <returns></returns>
public static String GeneratePrivateKey()
{
DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
return dsa.ToXmlString(true);
}
/// <summary>
/// get the public key from a private key. this key must be distributed with the application.
/// </summary>
/// <param name="privateKey"></param>
/// <returns></returns>
public static String GetPublicKey(String privateKey)
{
DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
dsa.FromXmlString(privateKey);
return dsa.ToXmlString(false);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="start">applicable start date for the license file.</param>
/// <param name="end">applicable end date for the license file</param>
/// <param name="productName">applicable product name</param>
/// <param name="userName">user-name</param>
/// <param name="fingerprint">computer's fingerprint</param>
/// <param name="privateKey">the private key (in XML form)</param>
/// <returns>secure, public license, validated with the public part of the key</returns>
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)
};
}
/// <summary>
/// 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.
/// </summary>
/// <param name="start">applicable start date for the license file.</param>
/// <param name="end">applicable end date for the license file</param>
/// <param name="productName">applicable product name</param>
/// <param name="userName">user-name</param>
/// <param name="fingerprint">computer's fingerprint</param>
/// <param name="privateKey">the private key (in XML form)</param>
/// <returns>secure, public license, validated with the public part of the key</returns>
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)
};
}
/// <summary>
/// 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.
/// </summary>
/// <param name="license">
/// the license object.
/// </param>
/// <exception cref="SecurityException">thrown if the license is invalid or expired</exception>
/// <returns></returns>
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!");
}
}
/// <summary>
/// validate license file and return the license terms.
/// </summary>
/// <param name="license"></param>
/// <param name="publicKey"></param>
/// <returns></returns>
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!");
}
}
}