Sample ASP.NET code written in C# that responds to a license
request
protected void Page_Load(object sender, EventArgs e)
{
// post parameters
String compID = Request.Form["COMP_ID"]; // you may not receive this parameter if your license type is not Computer Specific
String ID = Request.Form["ID"]; // this is the license ID that user entered into the Registration dialog.
try
{
// Step 1 - Find the license with the specified ID in your database.
// ...
// Step 2 [IMPORTANT] - Decide if the license request is legit
// make sure the license ID sent by the user (ID post value) is in your users database and only then continue generating a license for it
if (false /*< invalid license request ... >*/)
{
SendResponse(500, "Invalid license ID.");
return;
}
// Step 3 - Computing the license digital signature
// if present, add extra license elements like MP and EXP maintaining the alphabetical order
String data = "";
if (compID != null)
data += "COMP_ID=" + compID;
data += "ID=" + ID;
// data is now like 'COMP_ID=1283391946329685ID=john@example.com';
// load the private key. You should obtain this key by exporting from the
// Licensing > Registration page, by using the "Export PEM" Public - Private key pair action.
// You must take safety measures to protect this key (like protecting it with a password) before you upload it to your server.
string sPrivateKeyPEM = File.ReadAllText(Server.MapPath("~/licenseKey.pem"));
RSACryptoServiceProvider rsaProvider = PEMToX509.GetRSA(sPrivateKeyPEM);
if (rsaProvider == null)
{
SendResponse(501, "Keys generator setup failed");
return;
}
// compute signature
byte[] dataToSign = Encoding.Unicode.GetBytes(data);
byte[] signature = rsaProvider.SignData(dataToSign, new SHA1CryptoServiceProvider());
// Step 4 - Respond with the license elements
// if your license has a Maintainance Plan element (or any other elements) add them in alphabetical order
String responseData = "ID=" + ID + /* ";MP" + MP + */ ";SIGN=" + Convert.ToBase64String(signature);
SendResponse(200, responseData);
}
catch
{
SendResponse(501, "Keys generator setup failed");
}
}
private void SendResponse(int statusCode, String message)
{
// Set response data
Context.Response.Clear();
Context.Response.StatusCode = statusCode;
Context.Response.TrySkipIisCustomErrors = true;
Context.Response.Write(message);
// Complete current request
Context.ApplicationInstance.CompleteRequest();
}
// Helper class for using pem key format
internal static class PEMToX509
{
const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
const string KEY_FOOTER = "-----END RSA PRIVATE KEY-----";
internal static RSACryptoServiceProvider GetRSA(string pem)
{
RSACryptoServiceProvider rsa = null;
if (IsPrivateKeyAvailable(pem))
{
string keyFormatted = pem;
int cutIndex = keyFormatted.IndexOf(KEY_HEADER);
keyFormatted = keyFormatted.Substring(cutIndex, keyFormatted.Length - cutIndex);
cutIndex = keyFormatted.IndexOf(KEY_FOOTER);
keyFormatted = keyFormatted.Substring(0, cutIndex + KEY_FOOTER.Length);
keyFormatted = keyFormatted.Replace(KEY_HEADER, "");
keyFormatted = keyFormatted.Replace(KEY_FOOTER, "");
keyFormatted = keyFormatted.Replace("\r", "");
keyFormatted = keyFormatted.Replace("\n", "");
keyFormatted = keyFormatted.Trim();
byte[] privateKeyInDER = System.Convert.FromBase64String(keyFormatted);
rsa = DecodeRSAPrivateKey(privateKeyInDER);
}
return rsa;
}
private static bool IsPrivateKeyAvailable(string privateKeyInPEM)
{
return (privateKeyInPEM != null && privateKeyInPEM.Contains(KEY_HEADER)
&& privateKeyInPEM.Contains(KEY_FOOTER));
}
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider ---
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with private key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception)
{
return null;
}
finally
{
binr.Close();
}
}
private static int GetIntegerSize(BinaryReader binary)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binary.ReadByte();
if (bt != 0x02)
return 0;
bt = binary.ReadByte();
if (bt == 0x81)
count = binary.ReadByte();
else if (bt == 0x82)
{
highbyte = binary.ReadByte();
lowbyte = binary.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
count = bt;
while (binary.ReadByte() == 0x00)
count -= 1;
binary.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
}