Creating key generators of Enigma projects for automatic registrar ShareIt!
This tutorial will explain how to generate custom keys generator for the own projects protected with Enigma Protector. Enigma Protector itself has possibility to generate registration keys (through the simple keygen or License Manager) but this requires manual generation, it would be the best to automate this operation. So, we will create custom keys generator and send it to shareit team. Shareit will execute this keygen after successful order and show registration key to the user immediately after purchasing.
All the examples described below are written in Delphi (shareit SDK does not provide us C examples, but anyway, it can be rewritten in C too).
Creating Registration Keys Generator with embeded constants
Creating Registration Keys Generator that loads settings from the project file
Generating Time Limited Keys
Generating Hardware Locked Keys
Send Keygen to ShareIt!
Some missed words FYI
Creating Registration Keys Generator with embeded constants
Keys Generator is just simple executable file that is able to generate registration keys. For keys generation keygen uses special library keygen.dll (it may be found in the installation package of Enigma). Please note, all the projects created in Enigma Protector are unique (because some secure constants that use for keys generating are unique) and it is impossible to make keygen for the any external files protected with Enigma.
The registration keys generator requires few unique constants that are placed in your project file. These constant are unique for every project created in Enigma, probability of its duplication is almost zero. This generation method embeds unique constants into compiled project. The following code illustrates the key generator routine:
function Enigma_GenerateRegistrationKey(ARegistrationName : string) : string;
var
kg : TKeyGenParams;
key_buf : array [0..2047] of char;
dwresult : Cardinal;
begin
// Clear buffer
FillChar(kg, sizeof(kg), #0);
// Get this information from your project file
kg.KeyMode := RM_512;
kg.KeyBase := RB_32;
kg.KeyWithHyphens := true;
kg.PublicKey := '0201B810DA4A1ADD4351378790A98138533067CP4S86R7D8THS45GBCVUM635EPRQRMYRP3DAA5DUPZ6ABDSFP7F5AC' +
'P7ERGH4A7Y6B6NW6NMMBZF83WVER9Y4MMBNLBQDKR7KFVLGLV067CFDQC' +
'WCHGQVVRN24DECEPBL96YJQJTVDCRTNQG3E4WW4GK4GQ5X5L5H88D3XYH' +
'CBRBNASPD3P5CNYFKFHBCSDHHD6WPTCC4XVSM5S88067C2JSTCMVT48C8' +
'HC7SHKGTFJBM28P6XTBCNWHMV6J6KN6W5Q9TQLVR285U6GVCAAUTZLRTP' +
'SRGDQ742B4742XF4MACRR747YDP5FZZ9D';
kg.PrivateKey := '00C98B2SF9UBJA605AJX53GJFXJV8UH4A6PY2L6CV4MAMV7V3ERRVY99Y' +
'72V2P77Z2J3KBPGWR3WXKG5GF9Z6CKXJHY5VUMBTQ66H2MRZPCU00DLFJ' +
'675JTTTNEK00DLFJ675JTTTNEK';
kg.EncryptedConstant := 2113444489;
// Clear key buffer
FillChar(key_buf, sizeof(key_buf), #0);
kg.Key := @key_buf;
// Set key buffer size
kg.KeyLen := sizeof(key_buf);
// Set registration user info
kg.RegInfo := pointer(ARegistrationName);
kg.RegInfoLen := length(ARegistrationName);
// Use key expiration
kg.UseKeyExpiration := true;
kg.ExpirationYear := 2010;
kg.ExpirationMonth := 1;
kg.ExpirationDay := 1;
// Do not use hardware id
kg.UseHardwareLocking := false;
// This key should decrypt #2 and #6 sections
kg.EncryptedSections[1] := true;
kg.EncryptedSections[5] := true;
dwresult := KG_GenerateRegistrationKey(@kg);
if dwresult = EP_NO_ERROR then
begin
Result := pchar(kg.Key);
end else
begin
// Generate error message
case dwresult of
EP_ERROR_UNKNOWN : Result := 'EP_ERROR_UNKNOWN ';
EP_ERROR_KEYBUFFEREMPTY : Result := 'EP_ERROR_KEYBUFFEREMPTY ';
EP_ERROR_KEYBUFFERISLESS : Result := 'EP_ERROR_KEYBUFFERISLESS ';
EP_ERROR_REGINFOEMPTY : Result := 'EP_ERROR_REGINFOEMPTY ';
EP_ERROR_REGINFOTOOLARGE : Result := 'EP_ERROR_REGINFOTOOLARGE ';
EP_ERROR_PRIVATEKEYISNOTSET : Result := 'EP_ERROR_PRIVATEKEYISNOTSET ';
EP_ERROR_PUBLICKEYISNOTSET : Result := 'EP_ERROR_PUBLICKEYISNOTSET ';
EP_ERROR_PRIVATEKEYISINVALID : Result := 'EP_ERROR_PRIVATEKEYISINVALID ';
EP_ERROR_PUBLICKEYISINVALID : Result := 'EP_ERROR_PUBLICKEYISINVALID ';
EP_ERROR_KEYMODEISINVALID : Result := 'EP_ERROR_KEYMODEISINVALID ';
EP_ERROR_KEYBASEISINVALID : Result := 'EP_ERROR_KEYBASEISINVALID ';
EP_ERROR_CURRENTDATEISINVALID : Result := 'EP_ERROR_CURRENTDATEISINVALID ';
EP_ERROR_EXPIRATIONDATEISINVALID : Result := 'EP_ERROR_EXPIRATIONDATEISINVALID';
EP_ERROR_KEYISINVALID : Result := 'EP_ERROR_KEYISINVALID ';
EP_ERROR_HARDWAREID : Result := 'EP_ERROR_HARDWAREID ';
EP_ERROR_HARDWAREBUFFEREMPTY : Result := 'EP_ERROR_HARDWAREBUFFEREMPTY ';
EP_ERROR_HARDWAREIDINVALIDFORKEY : Result := 'EP_ERROR_HARDWAREIDINVALIDFORKEY';
EP_ERROR_PROJECTFILENOTFOUND : Result := 'EP_ERROR_PROJECTFILENOTFOUND ';
EP_ERROR_INVALIDPROJECTFILE : Result := 'EP_ERROR_INVALIDPROJECTFILE ';
else Result := 'Unknown error';
end;
end;
end;
Now, let's explain every code string:
FillChar(kg, sizeof(kg), #0); - clear memory buffer structure that uses for generating registration keys
kg.KeyMode := RM_512; - type of the keys length/safety. Enigma Protector supports few types of key safety RSA 512 (RM_512), RSA 768 (RM_768), RSA 1024 (RM_1024), RSA 2048 (RM_2048), RSA 3076 (RM_3076), RSA 4096 (RM_4096), to understand what value you should use there - open your project file in Enigma and go to "REGISTRATION FEATURES" - "Common" panel and find there "Registration key safety/length" field, it shows what value you should use. For example, if you are using RSA 1024 keys, then use RM_1024 constant for KeyMode parameter.
kg.KeyBase := RB_32; - output type of registration keys. There are 5 output types for registration keys:
| Key Base | Value | Key Symbols | Example |
|---|---|---|---|
| Base 2 | RB_2 | 01 |
0111101100110111000111100110101011101000 |
| Base 8 | RB_8 | 01234567 | 6683343532254367337887327776473815464631 |
| Base 16 | RB_16 | ABCDEF1234567890 | 960F4A566A1FCEDD491837CAC9E6F5 |
| Base 32 | RB_32 | ABCDEFGHJKLMNPQRSTUVWXYZ 2..9 | 8T8TB8TC8ZJWHKT49JXCA3R9 |
| Base 64 | RB_64 | A..Z a..z 0..9 += | VbwprHrqTQ=qv7YUXdn+ |
Please note that key output base is defined into your project file, to know what base you should use for keys generating - open your project file in Enigma and go to "REGISTRATION FEATURES" - "Common" panel and find there "Registration key output base" field, it shows what value you should use. For example, if you are using Base 32, then use RB_32 constant for KeyBase parameter.
kg.KeyWithHyphens := true; - allows to add hyphens to the key (example of the key with hyphens 9S7FN3-AHLSFU-9FE929-TPGTQS).
kg.PublicKey := '0201B810DA4A1ADD4351378790A98138533067CP4S86R7D8THS45GBCVUM635EPRQRMYRP3DAA5DUPZ6ABDSFP7F5AC' + - Public and Private Keys. These unique keys are placed in the project file. How to extract these keys from project file? Go through the following steps:
'P7ERGH4A7Y6B6NW6NMMBZF83WVER9Y4MMBNLBQDKR7KFVLGLV067CFDQC' +
'WCHGQVVRN24DECEPBL96YJQJTVDCRTNQG3E4WW4GK4GQ5X5L5H88D3XYH' +
'CBRBNASPD3P5CNYFKFHBCSDHHD6WPTCC4XVSM5S88067C2JSTCMVT48C8' +
'HC7SHKGTFJBM28P6XTBCNWHMV6J6KN6W5Q9TQLVR285U6GVCAAUTZLRTP' +
'SRGDQ742B4742XF4MACRR747YDP5FZZ9D';
kg.PrivateKey := '00C98B2SF9UBJA605AJX53GJFXJV8UH4A6PY2L6CV4MAMV7V3ERRVY99Y' +
'72V2P77Z2J3KBPGWR3WXKG5GF9Z6CKXJHY5VUMBTQ66H2MRZPCU00DLFJ' +
'675JTTTNEK00DLFJ675JTTTNEK';
- Open project file in notepad (project file with the .enigma extension). Enigma project file is just xml file with the all necessary settings.
- Find there the following branch: EnigmaProject - RegistrationFeatures - Constants. Constants branch contains all necessary unique constants. Now, take a look at the KeyMode parameter (described above), and remember key mode digits. Then, find in Constants branch that describes your KeyMode (for example, if you are using KeyMode = RM_1024 (~RSA 1024) then find Mode1024 branch in project file), in the necessary Mode get your Public and Private Keys.
kg.EncryptedConstant := 2113444489; - one more unique constant that is using for encryption/decryption of crypted sections (note: this value has integer type, not a string!). Get it from project file, in EnigmaProject - RegistrationFeatures - Constants branch, EncryptedConstant value.
FillChar(key_buf, sizeof(key_buf), #0); - fill out key memory buffer. In this buffer will be placed registration keys. Remember, size of key buffer must be at least 2048 bytes. Of course, size of buffer may be less, it depends on KeyBase and KeyMode, for example, KeyBase = RB_2 and KeyMode = RM_4096 give you a key about 2000 symbols length, but KeyBase = RB_64 and KeyMode = RM_512 - 20 symbols.
kg.Key := @key_buf; - set pointer of the key buffer to the key generation struct.
kg.KeyLen := sizeof(key_buf); - define a size of the key buffer. If the size of key buffer will be less than required then keygen function will return an error.
kg.RegInfo := pointer(ARegistrationName); - set pointer of the registration name buffer.
kg.RegInfoLen := length(ARegistrationName); - define a length of registration name buffer in bytes.
kg.UseKeyExpiration := false; - if this value if true then generated registration keys should have expiration date. Otherwise key is not time limited.
kg.UseHardwareLocking := false; - set it to true if the key is generating for the particular hardware id. In our example key is not locked to hardware id.
kg.EncryptedSections[1] := true; - set the crypted sections that should be decrypted with the current key.
kg.EncryptedSections[5] := true;
dwresult := KG_GenerateRegistrationKey(@kg); - call key generation function. Function returns integer result.
if dwresult = EP_NO_ERROR then - get an error code, if there is not any error, key generating fucntion returns EP_NO_ERROR value and kg.Key buffer contains generated key.
begin
Result := pchar(kg.Key);
end else
begin
// Generate error message
case dwresult of
EP_ERROR_UNKNOWN : Result := 'EP_ERROR_UNKNOWN ';
EP_ERROR_KEYBUFFEREMPTY : Result := 'EP_ERROR_KEYBUFFEREMPTY ';
EP_ERROR_KEYBUFFERISLESS : Result := 'EP_ERROR_KEYBUFFERISLESS ';
EP_ERROR_REGINFOEMPTY : Result := 'EP_ERROR_REGINFOEMPTY ';
EP_ERROR_REGINFOTOOLARGE : Result := 'EP_ERROR_REGINFOTOOLARGE ';
EP_ERROR_PRIVATEKEYISNOTSET : Result := 'EP_ERROR_PRIVATEKEYISNOTSET ';
EP_ERROR_PUBLICKEYISNOTSET : Result := 'EP_ERROR_PUBLICKEYISNOTSET ';
EP_ERROR_PRIVATEKEYISINVALID : Result := 'EP_ERROR_PRIVATEKEYISINVALID ';
EP_ERROR_PUBLICKEYISINVALID : Result := 'EP_ERROR_PUBLICKEYISINVALID ';
EP_ERROR_KEYMODEISINVALID : Result := 'EP_ERROR_KEYMODEISINVALID ';
EP_ERROR_KEYBASEISINVALID : Result := 'EP_ERROR_KEYBASEISINVALID ';
EP_ERROR_CURRENTDATEISINVALID : Result := 'EP_ERROR_CURRENTDATEISINVALID ';
EP_ERROR_EXPIRATIONDATEISINVALID : Result := 'EP_ERROR_EXPIRATIONDATEISINVALID';
EP_ERROR_KEYISINVALID : Result := 'EP_ERROR_KEYISINVALID ';
EP_ERROR_HARDWAREID : Result := 'EP_ERROR_HARDWAREID ';
EP_ERROR_HARDWAREBUFFEREMPTY : Result := 'EP_ERROR_HARDWAREBUFFEREMPTY ';
EP_ERROR_HARDWAREIDINVALIDFORKEY : Result := 'EP_ERROR_HARDWAREIDINVALIDFORKEY';
EP_ERROR_PROJECTFILENOTFOUND : Result := 'EP_ERROR_PROJECTFILENOTFOUND ';
EP_ERROR_INVALIDPROJECTFILE : Result := 'EP_ERROR_INVALIDPROJECTFILE ';
else Result := 'Unknown error';
end;
Creating Registration Keys Generator that loads settings from the project file
This kind of keys generator is similar as above but does not require defining of unique constants and key parameters (Private and Public Keys, EncryptedConstant, KeyBase and KeyMode). Keys generation routine reads all parameters from the project file automatically. Take a look at the code below. Note, here we are using KG_GenerateRegistrationKeyFromProject instead of KG_GenerateRegistrationKey (as in previous example) and KG_GenerateRegistrationKeyFromProject requires define a project file name. Simply, you may use my compiled example, just place there own project file.
function Enigma_GenerateRegistrationKey(ARegistrationName : string) : string;
var
kg : TKeyGenParams;
key_buf : array [0..2047] of char;
dwresult : Cardinal;
begin
// Clear buffer
FillChar(kg, sizeof(kg), #0);
// Get this information from your project file
kg.KeyWithHyphens := true;
// Clear key buffer
FillChar(key_buf, sizeof(key_buf), #0);
kg.Key := @key_buf;
// Set key buffer size
kg.KeyLen := sizeof(key_buf);
// Set registration user info
kg.RegInfo := pointer(ARegistrationName);
kg.RegInfoLen := length(ARegistrationName);
// Use key expiration
kg.UseKeyExpiration := true;
kg.ExpirationYear := 2010;
kg.ExpirationMonth := 1;
kg.ExpirationDay := 1;
// Do not use hardware id
kg.UseHardwareLocking := false;
// This key should decrypt #2 and #6 sections
kg.EncryptedSections[1] := true;
kg.EncryptedSections[5] := true;
dwresult := KG_GenerateRegistrationKeyFromProject('project.enigma', @kg);
if dwresult = EP_NO_ERROR then
begin
Result := pchar(kg.Key);
end else
begin
// Generate error message
case dwresult of
EP_ERROR_UNKNOWN : Result := 'EP_ERROR_UNKNOWN ';
EP_ERROR_KEYBUFFEREMPTY : Result := 'EP_ERROR_KEYBUFFEREMPTY ';
EP_ERROR_KEYBUFFERISLESS : Result := 'EP_ERROR_KEYBUFFERISLESS ';
EP_ERROR_REGINFOEMPTY : Result := 'EP_ERROR_REGINFOEMPTY ';
EP_ERROR_REGINFOTOOLARGE : Result := 'EP_ERROR_REGINFOTOOLARGE ';
EP_ERROR_PRIVATEKEYISNOTSET : Result := 'EP_ERROR_PRIVATEKEYISNOTSET ';
EP_ERROR_PUBLICKEYISNOTSET : Result := 'EP_ERROR_PUBLICKEYISNOTSET ';
EP_ERROR_PRIVATEKEYISINVALID : Result := 'EP_ERROR_PRIVATEKEYISINVALID ';
EP_ERROR_PUBLICKEYISINVALID : Result := 'EP_ERROR_PUBLICKEYISINVALID ';
EP_ERROR_KEYMODEISINVALID : Result := 'EP_ERROR_KEYMODEISINVALID ';
EP_ERROR_KEYBASEISINVALID : Result := 'EP_ERROR_KEYBASEISINVALID ';
EP_ERROR_CURRENTDATEISINVALID : Result := 'EP_ERROR_CURRENTDATEISINVALID ';
EP_ERROR_EXPIRATIONDATEISINVALID : Result := 'EP_ERROR_EXPIRATIONDATEISINVALID';
EP_ERROR_KEYISINVALID : Result := 'EP_ERROR_KEYISINVALID ';
EP_ERROR_HARDWAREID : Result := 'EP_ERROR_HARDWAREID ';
EP_ERROR_HARDWAREBUFFEREMPTY : Result := 'EP_ERROR_HARDWAREBUFFEREMPTY ';
EP_ERROR_HARDWAREIDINVALIDFORKEY : Result := 'EP_ERROR_HARDWAREIDINVALIDFORKEY';
EP_ERROR_PROJECTFILENOTFOUND : Result := 'EP_ERROR_PROJECTFILENOTFOUND ';
EP_ERROR_INVALIDPROJECTFILE : Result := 'EP_ERROR_INVALIDPROJECTFILE ';
else Result := 'Unknown error';
end;
end;
end;
Download sources
Generating Time Limited Keys
To generate time limited registration keys, we need to enable UseKeyExpiration property and setup key expiration date.
This example shows how to set fixed expiration date. The keys generated with such expiration date will expire at 01/01/2010 (DD/MM/YYYY)
kg.UseKeyExpiration := true;
kg.ExpirationYear := 2010;
kg.ExpirationMonth := 1;
kg.ExpirationDay := 1;
If there is need to setup variable expiration date, it can be done very simply too. Such keys will expire after 1 year from key creation date.
kg.UseKeyExpiration := true;
kg.ExpirationYear := YearOf(Now) + 1;
kg.ExpirationMonth := MonthOf(Now);
kg.ExpirationDay := DayOf(Now);
Or the same example but with using of GetSystemTime Windows API.
kg.UseKeyExpiration := true;
GetSystemTime(lpSystemTime);
kg.ExpirationYear := lpSystemTime.wYear + 1;
kg.ExpirationMonth := lpSystemTime.wMonth;
kg.ExpirationDay := lpSystemTime.wDay;
Generating Hardware Locked Keys
When the key should be locked to particular Hardware ID we need to get user's Hardware ID before generating key. How to get it? As I always promise to software developers to avoid cracking of their work - never distribute full functional version. The best way to avoid cracking is distributing of functions limited DEMO version, that does not require registration, but for registered user - FULL version of the program which should have registration. So, let's think you have two versions of your program:
- DEMO version, this version has restricted functionality (for example, some functions are not available as in full version) and it is placed in your site. Anybody can download it and decide - purchase it or no.
- FULL version, this version requires registration by means registration name and key, it has full functionality and you send this version to registered users ONLY.
Now return to keygen, we should get user's hardware id before the user purchases and get full version. In this case we need to mechasim that returns user's hardware id, for this purpose we can protect DEMO version with the same project file as a full version (we need this because DEMO version should return same hardware id as full version, this possible only by protecting DEMO with the same project as FULL version), plus, this DEMO should show hardware id (hardware id may be shown by means Enigma API EP_RegHardwareID, or embed Enigma's registration dialog, take a look at the REGISTRTAION FEATURES - Registration Dialog panel in Enigma).
Usual purchasing dialog of shareit does not support entering of hardware id, but we can configure it! There are 2 special unnamed fields that we can enable in purchasing dialog, its are ADDITIONAL1 and ADDITIONAL2. In the shareit control we need to enable ADDITIONAL1 field and set it's text - Hardware ID. This field should be required. After this, we made possibility to enter hardware id to the shareit purchasing dialog.
In the keygen we setup that key should be hardware locked and hardware id is placed in the ADDITIONAL1 field.
kg.UseHardwareLocking := true; - key should be hardware locked.
kg.HardwareID := pointer(HardwareId); - set pointer to the hardware id string.
Finally, how it works:
- User gets DEMO version of your application (protected with the same project as FULL version).
- User decided to purchase you software and is going to the shareit purchasing page.
- Shareit asked user to enter own hardware id. User opens DEMO version (that shows hardware it), and copy-paste it.
- Shareit gets this hardware id and uses it for generating registration key.
- Then we should let user know where to get FULL version. For example, this information we may setup in shareit control panel, this information will be shown to user after successful purchasing together with the registration key.
- User downloads/installs/run FULL version, it asks for registration name and key, user enters the registration name that was entered in shareit purchasing dialog and registration key that was generated with our keygen.
- Everything is done and everything is automated!
Send Keygen to ShareIt!
After keygen for the project has been created we have to send it to shareit support team and ask to enable this keygen for our account. Email address on that should be sent email is authors@shareit.com I will explain a little what should contain this email:
- Information about your shareit account (how is account owner, account number).
- Few words that we are asking about adding keygen and unique shareit numbers of the software(s) for that this keygen will work.
- Attached keygen: keygen.exe (example.exe), keygen.dll (and project.enigma if you are using generation with project file).
- Need to explain what files keygen is using: keygen.dll (and project.enigma if you are using generation with project file).
- What encoding can be used for keys generating. For now, keygen does NOT support UTF-8 encoding.
When shareit team replies that keygen was applied, just for testing (to be sure that all is working well) make a test order from the shareit control panel.
Some missed words FYI
In the previous topic I wrote that UTF-8 is not supported, really, it can be done! You may send input parameter RegInfo for keygen.dll not as ansi string like were using in the above examples, but in wide char mode (unicode), remember, RegInfoLen should be in 2 times greater for unicode string than length if ansi, it is due to ansi string uses one byte for one symbol conversion but unicode - 2 bytes per each symbol. Then, key check function EP_RegCheckAndSaveKey, or EP_RegCheckKey should have RegName parameter in unicode style too.
Second idea, I explain how to generate registration key as string, but shareit gives us possibility to generate text files with the key also.
All of the examples are using crypted sections, but if you do not need to use it - eliminate these strings from the keygen, remember, if you use crypted sections for the key - it becomes larger.
Downloads:
Simple Keygen
Keygen from Project File
Keygen with Expiration Date 1
Keygen with Expiration Date 2
Keygen with Expiration Date 3
Keygen with Hardware ID
If you have any suggestions/comments you may post it in out support forum Forum: Automatic Keygen for Shareit!
This article is written for educational purposes only. The author does not carry any warranties/liability for using this information.
Author: Vladimir Sukhov
Date: 3 March 2009