Encryption of a password - delphi

i've managed to do a simple encryption of a password entered using the following code which then displays the encrypted password in a labels caption,
procedure TfrmLogin.edtAddPasswordClick(Sender: TObject);
var
NormalPassword, EncryptedPassword: string;
PasswordChar: Char;
EncryptedCharValue: string;
CharPtr: Integer;
Ptr, n: Integer;
begin
NormalPassword := Edit1.text;
EncryptedPassword := '';
for CharPtr := 1 to Length(NormalPassword) do
begin
PasswordChar := NormalPassword[CharPtr];
EncryptedCharValue := IntToStr (Ord(PasswordChar) * 5 + 14);
EncryptedPassword := EncryptedPassword + EncryptedCharValue;
Label1.Caption := EncryptedPassword;
end;
end;
The problem is that i would like to convert the encrypted password displayed in label1.caption back into its original form on the click of another button and i can't work out how this could be done. any suggestions?

Instead of create your own algorithm to hash( or encrypt) a password, try using a well tested and reliable algorithm like SHA1, MD5, and so on.
Back to your question to convert the encrypted value to the original, all you must do is reverse your algorithm , try this sample.
var
NormalPassword, EncryptedPassword: String;
PasswordChar : char;
EncryptedCharValue : String;
CharPtr : Integer;
begin
NormalPassword :='';
EncryptedPassword := Label1.Caption; //here is stored the encrypted password
CharPtr := 1;
while CharPtr< length(EncryptedPassword) do
Begin
EncryptedCharValue:=Copy(EncryptedPassword, CharPtr, 3);
Inc(CharPtr, 3);
PasswordChar := Chr((StrToint(EncryptedCharValue)-14) div 5);
NormalPassword :=NormalPassword+ PasswordChar;
end;
Label2.Caption := NormalPassword;
end;

I know this is for homework and the idea is to get the reversing code, and others are providing you too much detail for that purpose, but I need to give this as an answer because its concept is too important to say in a note:
If you are truly talking about a password, then you must not make the password reversable. Users expect their passwords to be safe and secure and non-reversable.
If the reason why you want to do this is because you want to send them their password if they forget it, then the answer is don't.
When a person loses or forgets their password, you should not provide it back to them, because that proves it is insecure. Instead, the proper thing to do is, after ensuring they are the user who signed up (via email address or other means), then allow them to enter a new password of their choice.

If you're determined to do it the way you've described, you could make it reversible by changing the line:
EncryptedCharValue := IntToStr (Ord(PasswordChar) * 5 + 14);
to
EncryptedCharValue := format('%.4d', [Ord(PasswordChar) * 5 + 14]);
That would allow you later to pull the string apart in four-character chunks, subtract 14, divide by 5, and turn it back into a character.
I stand by my earlier comment though - if you actually have a use-case that requires reversible security, use a stronger algorithm, for example as discussed in this question.
[Edit: four chars is clearly more robust]

You function is a very simple hashing algorithm that cannot be reversed. It does not make sense to store passwords in a way that can be reversed, since it does not add any additional layer of security (except you use a complicated scheme based on asymmetric crypto)

simple hash algorithm :
How do I hash a string with Delphi?
hash with secret key algorithm(CRAM,HMac):
HMAC-SHA256 in Delphi

Related

How do i create/use an IBuffer?

I want to use functions of the Winapi.Security.Cryptography.Core_IAsymmetricKeyAlgorithmProvider and it has a parameter of type IBuffer.
i dont know how to use an IBuffer or how to give data to it.
i tried allocationg memory and giving the function a pwide char but the ide already told me this wont work.
I also didnt find anything useful online.
Winapi.Security.Cryptography contains wrappers for
Windows.Security.Cryptography.Core (Windows UWP)
Check this page first:
https://learn.microsoft.com/en-us/uwp/api/windows.security.cryptography.core.asymmetrickeyalgorithmprovider?view=winrt-22621
There is a C# app example that demonstrates how to use this interface.
Details about IBuffer can be found on this page:
https://learn.microsoft.com/en-us/uwp/api/windows.storage.streams.ibuffer?view=winrt-22621
This is probably your first step, if you want encrypt with RSA PKCS1:
uses
System.Win.WinRT,
Winapi.WinRT,
Winapi.Security.Cryptography,
Winapi.Storage.Streams;
const
KeyLength = 512;
var
objAlgProv: Core_IAsymmetricKeyAlgorithmProvider;
data: TBytes;
cleardata, encrypted: IBuffer;
key: Core_ICryptographicKey;
hs: HSTRING;
EncryptedBase64: string;
begin
data := TEncoding.UTF8.GetBytes('clear text');
objAlgProv := TCore_AsymmetricKeyAlgorithmProvider.OpenAlgorithm(TCore_AsymmetricAlgorithmNames.RsaPkcs1);
key := objAlgProv.CreateKeyPair(KeyLength);
cleardata := TCryptographicBuffer.CreateFromByteArray(length(data), #data[0]);
encrypted := TCore_CryptographicEngine.Encrypt(key, cleardata, nil {IV});
hs := TCryptographicBuffer.EncodeToBase64String(encrypted);
EncryptedBase64 := TWindowsString.HStringToString(hs);
end;
If you have further trouble translating the solution into Delphi, show your code an explain the details what you need to know.

LockBox 3 Encrypting not matching online tool, suspect padding or key problem

I have a client providing an API that dictates the data to send to them must be encrypted with AES, 128-bit key, ECB mode, and PKCS5Padding. I'm trying to use LockBox 3 in Delphi 10.3 Rio and am not getting the same encrypted string as an online test tool they pointed to for verification. It is close, but not quite there.
With lots of reading here about Unicode, PKCS5Padding, and related questions, I've come to the end of what to try. I must admit I don't do very much with encryption and have been reading as much as I can to get my head around this before I ask questions.
There are a couple things I'd like confirmation on:
The difference between a password and a key. I've read that LB3 uses a password to generate a key, but I have specific instructions from the client on how the key is to be generated so I've made my own Base64-encoded key and am calling InitFromStream to initialize it. I believe this takes the place of setting a password, is that right? Or maybe passwords are only used by Asymetric cipers (not Symetric ones, like AES)?
PKCS5Padding: I was worried by something I read on the LB3 Help site that said padding is done intelligently depending on the choice of cipher, chaining mode, etc. So does that mean there's no way to force it to use a specific padding method? I've converted the data to a byte array and implemented by own PKCS5Padding but I think LB3 may still be padding beyond that. (I've tried looking through the code and have not found any evidence this is what it's doing.)
Should I use a different encryption library in Delphi to accomplish this? I've checked out DelphiEncryptionCompendium and DcPCryptV2 but I found LB3 seems to have the most support and I felt it was the easiest to work with, especially in my Unicode version of Delphi. Plus I have used LockBox 2 quite a bit in years past, so I figured it would be more familiar (this turned out not to be the case).
To illustrate what I've tried, I extracted my code from the project to a console application. Perhaps my assumptions above are correct and there's a glaring error in my code or a LB3 parameter I don't understand that someone will point out:
program LB3ConsoleTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Classes, System.NetEncoding,
uTPLb_Codec, uTPLb_CryptographicLibrary,
uTPLb_StreamUtils, uTPLb_Constants;
var
Codec: TCodec;
CryptographicLibrary: TCryptographicLibrary;
function PKCS5PadStringToBytes(RawData: string; const PadSize: Integer): TBytes;
{ implement our own block padding }
var
DataLen: Integer;
PKCS5PaddingCount: ShortInt;
begin
Result := TEncoding.UTF8.GetBytes(RawData);
DataLen := Length(RawData);
PKCS5PaddingCount := PadSize - DataLen mod PadSize;
if PKCS5PaddingCount = 0 then
PKCS5PaddingCount := PadSize;
Inc(DataLen, PKCS5PaddingCount);
SetLength(Result, DataLen);
FillChar(Result[DataLen - PKCS5PaddingCount], PKCS5PaddingCount, PKCS5PaddingCount);
end;
procedure InitializeAESKey(const AESKey: string);
{ convert the string to a byte array,
use that to initialize a ByteStream,
and call LB3's InitFromStream }
var
AESKeyBytes: TBytes;
AESKeyStream: TBytesStream;
begin
AESKeyBytes := TEncoding.UTF8.GetBytes(AESKey);
AESKeyStream := TBytesStream.Create(AESKeyBytes);
Codec.InitFromStream(AESKeyStream);
end;
const
RawData = '{"invoice_id":"456456000018047","clerk_id":"0023000130234234","trans_amount":1150034534,"cust_code":"19455605000987890641","trans_type":"TYPE1"}';
AESKeyStr = 'CEAA31AD1EE4BDC8';
var
DataBytes: TBytes;
DataStream: TBytesStream;
ResultStream: TBytesStream;
ResultBytes: TBytes;
Base64Encoder: TBase64Encoding;
begin
// create the LockBox3 objects
Codec := TCodec.Create(nil);
CryptographicLibrary := TCryptographicLibrary.Create(nil);
try
// setup LB3 for AES, 128-bit key, ECB
Codec.CryptoLibrary := CryptographicLibrary;
Codec.StreamCipherId := uTPLb_Constants.BlockCipher_ProgId;
Codec.BlockCipherId := Format(uTPLb_Constants.AES_ProgId, [128]);
Codec.ChainModeId := uTPLb_Constants.ECB_ProgId;
// prep the data, the key, and the resulting stream
DataBytes := PKCS5PadStringToBytes(RawData, 8);
DataStream := TBytesStream.Create(DataBytes);
InitializeAESKey(AESKeyStr);
ResultStream := TBytesStream.Create;
// ENCRYPT!
Codec.EncryptStream(DataStream, ResultStream);
// take the result stream, convert it to a byte array
ResultStream.Seek(0, soFromBeginning);
ResultBytes := Stream_to_Bytes(ResultStream);
// convert the byte array to a Base64-encoded string and display
Base64Encoder := TBase64Encoding.Create(0);
Writeln(Base64Encoder.EncodeBytesToString(ResultBytes));
Readln;
finally
Codec.Free;
CryptographicLibrary.Free;
end;
end.
This program generates an encrypted string 216 characters long with only the last 25 different than what the online tool produces.
Why?
AES uses 16-bytes blocks, not 8-bytes.
So you need PKCS7 with 16 bytes padding, not PKCS5 which is fixed to 8 bytes.
Please try
DataBytes := PKCS5PadStringToBytes(RawData, 16);
Also consider changing the chaining mode away from ECB, which is pretty weak, so is to be avoided for any serious work.

Universal approach to send virtual key codes with Delphi

I am trying to write international program and need to send some text to "other text edit programs" like word or notepad or a browser. On the other hand I am not sure that I can find an international way(because of the different keyboard layouts)
it would be nice to use a code like below
SendMessage(FindActiveWindowsHWND,WM_SETTEXT,0,Integer(PChar('My String')));
and I dont have function like FindActiveWindowsHWND
Edit: The code I am tried but not satisfied so far;
procedure FindActiveWindowsHWND();
var
ThreadInfo: TGUIThreadInfo;
activewindowsHwnd: HWND;
begin
GetGUIThreadInfo(0,ThreadInfo);
activewindowsHwnd:= ThreadInfo.hwndActive; (or ThreadInfo.hwndFocus);
end;
also I used Sendinput function like this
procedure SendKey(vKey: SmallInt; booDown: boolean);
var
GInput: array[0..0] of tagINPUT; //GENERALINPUT;
// doesn't have to be array :)
begin
GInput[0].Itype := INPUT_KEYBOARD;
GInput[0].ki.wVk := vKey;
GInput[0].ki.wScan := 0;
GInput[0].ki.time := 0;
GInput[0].ki.dwExtraInfo := 0;
if not booDown then
GInput[0].ki.dwFlags := KEYEVENTF_KEYUP
else
GInput[0].ki.dwFlags := 0;
SendInput(1, GInput[0], SizeOf(GInput));
end;
then
SendKey(65,true); //to send an "A" for example
but instead it sent an "a" and when I try to send an "a" using SendKey(97,true) it sent "1".
it is really interesting that I have to send shift key down to write uppercase letters
You can use GetGUIThreadInfo() to get the HWND of the currently focused window in another process. Not all window types accept WM_SETTEXT, though. You could use SendInput() to put Unicode characters into the keyboard queue, though. Or use the Automation API, like David said, though not all window types implement that.

What protection scheme for my passwords?

I'm developing a software (for personal use) with Delphi.
But I have a problem, this is it :
-> There's a main password to access a some other password file.
-> When storing these password, i use as key the main password. I think, it's ok.
-> But how protect the main password, and allow modification of it ???
If I use a constant key (so stored in the code, in binary), It can be disassembled !
So, I'm crazy or there's a way to make this possible : Protect main password and derived passwords.
(Main password (choosen by user) -> use it as key when encrypting user data (other password and usernames related).
Thank you for your helps.
Excuse my bad english.
i'd like to suggest turning the problem on its head. Your Windows account is already protected with a password. The Win32 API provides a mechanism where you can have Windows encrypt data with your Windows password.
This means that your data is as secure as your Windows password; and you don't need to memorize a second password.
The Windows function CredWrite and CredRead allow storing and saving of credentials; of which i just happen to have a handy wrapper function already for storing credentials:
function CredWriteGenericCredentials(const Target, Username, Password: WideString): Boolean;
var
PersistType: DWORD;
Credentials: CREDENTIALW;
hr: DWORD;
s: string;
begin
if not CredGetMaxPersistType(CRED_TYPE_GENERIC, {var}PersistType) then
begin
Result := False;
Exit;
end;
ZeroMemory(#Credentials, SizeOf(Credentials));
Credentials.TargetName := PWideChar(Target); //cannot be longer than CRED_MAX_GENERIC_TARGET_NAME_LENGTH (32767) characters. Recommended format "Company_Target"
Credentials.Type_ := CRED_TYPE_GENERIC;
Credentials.UserName := PWideChar(Username);
Credentials.Persist := PersistType; //CRED_PERSIST_ENTERPRISE; //local machine and roaming
Credentials.CredentialBlob := PByte(Password);
Credentials.CredentialBlobSize := 2*(Length(Password)); //By convention no trailing null. Cannot be longer than CRED_MAX_CREDENTIAL_BLOB_SIZE (512) bytes
Credentials.UserName := PWideChar(Username);
Result := CredWriteW(Credentials, 0);
if not Result then
begin
hr := GetLastError;
case hr of
CredUI.ERROR_NO_SUCH_LOGON_SESSION: s := 'The logon session does not exist or there is no credential set associated with this logon session. Network logon sessions do not have an associated credential set. (ERROR_NO_SUCH_LOGON_SESSION)';
CredUI.ERROR_INVALID_PARAMETER: s := 'Certain fields cannot be changed in an existing credential. This error is returned if a field does not match the value in a protected field of the existing credential. (ERROR_INVALID_PARAMETER)';
CredUI.ERROR_INVALID_FLAGS: s := 'A value that is not valid was specified for the Flags parameter. (ERROR_INVALID_FLAGS)';
ERROR_BAD_USERNAME: s := 'The UserName member of the passed in Credential structure is not valid. For a description of valid user name syntax, see the definition of that member. (ERROR_BAD_USERNAME)';
ERROR_NOT_FOUND: s := 'CRED_PRESERVE_CREDENTIAL_BLOB was specified and there is no existing credential by the same TargetName and Type. (ERROR_NOT_FOUND)';
// SCARD_E_NO_READERS_AVAILABLE: raise Exception.Create('The CRED_TYPE_CERTIFICATE credential being written requires the smart card reader to be available. (SCARD_E_NO_READERS_AVAILABLE)');
// SCARD_E_NO_SMARTCARD: raise Exception.Create('A CRED_TYPE_CERTIFICATE credential being written requires the smart card to be inserted. (SCARD_E_NO_SMARTCARD)');
// SCARD_W_REMOVED_CARD: raise Exception.Create('A CRED_TYPE_CERTIFICATE credential being written requires the smart card to be inserted. (SCARD_W_REMOVED_CARD)');
// SCARD_W_WRONG_CHV: raise Exception.Create('The wrong PIN was supplied for the CRED_TYPE_CERTIFICATE credential being written. (SCARD_W_WRONG_CHV)');
else
s := SysErrorMessage(hr)+' (0x'+IntToHex(hr, 8)+')';
end;
OutputDebugString(PChar(s));
end;
end;
And a wrapper function to read credentials:
function CredReadGenericCredentials(const Target: WideString; var Username, Password: WideString): Boolean;
var
Credential: PCREDENTIALW;
begin
Credential := nil;
if CredReadW(Target, CRED_TYPE_GENERIC, 0, Credential) then
begin
try
username := Credential.UserName;
password := WideCharToWideString(PWideChar(Credential.CredentialBlob), Credential.CredentialBlobSize); //By convention blobs that contain strings do not have a trailing NULL.
finally
CredFree(Credential);
end;
Result := True;
end
else
Result := False;
end;
It should be noted that CredRead and CredWrite are themselves functions that turn around and use CryptProtectData and CryptUnprotectData.
These functions let you take some arbitrary blob, and encrypt it with the user account's password1, and then hand you back the encrypted blob. You can then store that blob wherever you like (e.g. registry or file).
Later you can have the blob decrypted, and can only be decrypted by the user who originally encrypted it.
This lets you have your dream of forcing you to deal with another password, but uses Windows to protect it.
"MyPassword04" --> CryptProtectData() --> "TXlQYXNzd29yZDA0"
You can store your encrypted password anywhere you like. Then later:
"TXlQYXNzd29yZDA0" --> CryptUnprotectData() --> "MyPassword04"
The suggestion i'm making is the ability to abandon passwords; leveraging the security of your own account.
Just a suggestion; you're free to consider, and reject, it.
Update
Additional helper functions.
Convert a PWideChar to a WideString (if there's a built-in (Delphi 5) function for it, i've never found it):
function WideCharToWideString(Source: PWideChar; SourceLen: Integer): WideString;
begin
if (SourceLen <= 0) then
begin
Result := '';
Exit;
end;
SetLength(Result, SourceLen div 2);
Move(Source^, Result[1], SourceLen);
end;
There are different "scopes" that you'r allowed to store credentails in:
CRED_PERSIST_NONE: No credential can be stored. This value will be returned if the credential type is not supported or has been disabled by policy.
CRED_PERSIST_SESSION: Only a session-specific credential can be stored.
CRED_PERSIST_LOCAL_MACHINE: Session-specific and computer-specific credentials can be stored.
Windows XP: This credential cannot be stored for sessions in which the profile is not loaded.
CRED_PERSIST_ENTERPRISE: Any credential can be stored.
Windows XP: This credential cannot be stored for sessions in which the profile is not loaded.
This function returns the highest supported persistence type for a given credential type (e.g. "generic" credentails). It's needed when you call CredWrite that you don't try to persist it in a location that isn't supported (i.e. in the domain when there is no domain):
type
TCredGetSessionTypes = function(MaximumPersistCount: DWORD; MaximumPersist: LPDWORD): BOOL; stdcall;
function CredGetMaxPersistType(CredType: DWORD; var MaxCredPersistType: DWORD): Boolean;
const
CRED_TYPE_MAXIMUM = 5;
var
_CredGetSessionTypes: TCredGetSessionTypes;
MaximumPersist: array[0..CRED_TYPE_MAXIMUM-1] of DWORD;
begin
_CredGetSessionTypes := GetProcedureAddress(advapi32, 'CredGetSessionTypes');
if Assigned(_CredGetSessionTypes) then
begin
Result := _CredGetSessionTypes(CRED_TYPE_MAXIMUM, PDWORD(#MaximumPersist[0]));
if Result then
MaxCredPersistType := MaximumPersist[CredType]
else
MaxCredPersistType := 0;
end
else
begin
SetLastError(ERROR_INVALID_FUNCTION);
Result := False;
MaxCredPersistType := 0;
end;
end;
Note: Any code is released into the public domain. No attribution required.
Encode your password file with the main password. Don't store that password anywhere; simply query it before decrypting the password file. If someone enters a wrong password, the password file will be scrambled.
What you could probably do is use a one-way hash for all passwords, without the need for a master password at all.
The nice thing with a hash is that it may be readable by everyone, they aren't any the smarter, since the only way to break a hashed password is a brute-force attack. Which is all the more time consuming that the hash is "large".
Of course, this won't hold if passwords stored are easily discoverable by a dictionary attack, but then is your master password secure?
Check out how password safe http://passwordsafe.sourceforge.net/ solves the problem.
Assemble the list-of-passwords-to-be-encrypted.
Generate two random 128 bit numbers using a secure random generator. Use the first as HMAC key for later authentication and integrity checks. Use the second as AES-CBC key for encrypting the list-of-passwords-to-be-encrypted. Append the HMAC output to the end of the encrypted list.
Generate a third random number. Use this as salt together with the password for deriving a key encryption key using a PBKDF. Use the key encryption key for encrypting the two random keys in step 2.
Optionally, generate a password verifier by hashing your password a sufficiently large number of times.
The final file should have the following layout, formatting omitted
[salt][password verifier][encrypted encryption key][encrypted hmac key][encrypted password list][hmac value]
You may be interested in our new SmartUtils Password SDK: http://sutils.com/index.php/smartutils-password-sdk It allows to store passwords with related info like URLs, usernames etc. in AES-256 encrypted database file. A master password may be encrypted using DPAPI in one line of code.

Licensing with OnGuard

Recently, I looked into using OnGuard as a way to help honest people abide by their principles. I agree with the views expressed by many here that no licensing system is going to protect you from someone who wants to use your software and does not want to pay for it. On the other hand, I would like to avoid making it too easy for someone else to create valid keys for my program.
After studying the manual and examples, I added the following to my main form's code:
const
TheKey: TKey = ($4A,$62,$F3,$2B,$9C,$D2,$84,$BF,$CB,$04,$0A,$C3,$3D,$11,$47,$1A);
function TfrmMain1.MakeCode(sName, sNumber: String; dtExpiration: TDate): String;
var Key: TKey;
Code: TCode;
sCode: String;
begin
Key := TheKey;
InitRegCode(Key, sName + ' - ' + sNumber, dtExpiration, Code);
sCode := BufferToHex(Code, SizeOf(Code));
Insert('-', sCode, 13);
Insert('-', sCode, 09);
Insert('-', sCode, 05);
Result := sCode
end;
function TfrmMain1.TestCode(sName, sNumber, sTestCode: String; dtExpiration: TDate): Boolean;
var Key: TKey;
Code: TCode;
sCode: String;
begin
sCode := MakeCode(sName, sNumber, dtExpiration);
Result := SameText(sCode, sTestCode);
end;
This brings up some questions:
Does seem like the correct way to use this? I would rather not add their components to my form.
Since the OnGuard source is available, couldn't a hacker reverse engineer the Key I will choose and produce valid release codes? Should I therefore add some additional obfuscation to the code or might I just weaken the system.
The Key is set as a constant here. Won't it show up in the code as contiguous bytes and be easy to copy?
My program will require (at least) annual updates and my plan is to license it with an annual subscription. Would it be stronger to add the year as a constant in my program and test dated user entries against that year in a few places.
There are 4 questions here that are closely related and pretty specific. It seemed like it would be more awkward to ask those in four separate entries and have to add references for context but I will be glad to do that if that would be preferable. Thank you for your help.
Jack
constructor TLincenceManager.Create;
begin
FSpecialCode := TOgSpecialCode.Create(nil);
FSpecialCode.OnGetModifier := OgNetCodeGetModifier;
FSpecialCode.OnChecked := OgNetCodeChecked;
FSpecialCode.OnGetCode := OgNetCodeGetCode;
FSpecialCode.OnGetKey := OgNetCodeGetKey;
FSpecialCode.AutoCheck := False;
end;
function TLincenceManager.InitializeLicenceCode: Boolean;
begin
Result := FSpecialCode.CheckCode(True) = ogValidCode;
end;
procedure TLincenceManager.OgNetCodeChecked(Sender: TObject; Status: TCodeStatus);
begin
case Status of
ogValidCode : FMaxUsers := FSpecialCode.GetValue;
ogInvalidCode : FMaxUsers := 0;
ogPastEndDate : FMaxUsers := 0;
ogDayCountUsed : FMaxUsers := 0;
ogRunCountUsed : FMaxUsers := 0;
ogNetCountUsed : FMaxUsers := 0;
ogCodeExpired : FMaxUsers := 0;
else
FMaxUsers := 0;
end;
end;
procedure TLincenceManager.OgNetCodeGetCode(Sender: TObject; var Code: TCode);
var
FileName: string;
SerialData: string;
LicenceData: TStringList;
begin
FileName := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
FileName := FileName + cLicenseFileName;
SerialData := '';
LicenceData := TStringList.Create;
try
if FileExists(FileName) then
begin
LicenceData.LoadFromFile(FileName);
SerialData := LicenceData.Values['Serial'];
end;
{convert to proper form}
HexToBuffer(SerialData, Code, SizeOf(Code));
finally
LicenceData.Free;
end;
end;
procedure TLincenceManager.OgNetCodeGetKey(Sender: TObject; var Key: TKey);
const
CKey : TKey = ($4A,$62,$F3,$2B,$9C,$D2,$84,$BF,$CB,$04,$0A,$C3,$3D,$11,$47,$1A);
begin
Key := CKey;
end;
procedure TLincenceManager.OgNetCodeGetModifier(Sender: TObject; var Value: Integer);
begin
Value := GenerateMachineModifierPrim;
end;
I have posted the way I do it. I do not use "visual" components. The way I do it is the way to go, in your case you just apply the date modifier (i have machine modifier)
No in theory no. The key with which you generate you licence is of essence. If you have the key you can crack the licences. But with only the code you cant. This is just like encryption algorithms. You can know how the algorithm works but if you don't have the key you cant crack it. Look at XTEA. It is very simple yet hard to crack.
Yes the key can be extracted from binary if one knows what is doing. You could use some sort of obfuscation here. But I would not bother. For most people such protection is enough, so if you are not making the next MS Office I would not bother. People are way to paranaoid about their products. Sell it first and think about this later. Oh and since it is not a string it is a little harder to find anyway.
Just look at the time trial demos that come with onGuard to know how to do the time limited licence. However be aware that just simple manipulation of the computer clock will be enough to fool it. In my opinion best trial software is such, that is lack some vital functionality (save button...). Good time trials are very hard to make in my opinion.
I use the lower level OnGuard APIs rather than the classes as suggested by Runner. Either will work fine, the classes end up calling the lower level APIs anyway. Here are my wrapper utility functions for these lower level API methods.
{ Used by you to generate the unlock code you send to your customer, do not include in the customer software }
function GenerateReleaseCode(const inAppKey : string; inExpiryDate : TDateTime; inRegCode : string) : string;
(* inAppKey is the byte sequence key you already have
inRegCode can be anything, typically customers name
Returns the release code for the customer to type into the software *)
var
releaseCode : TCode;
key : TKey;
begin
HexToBuffer(inAppKey, key, SizeOf(TKey));
InitRegCode(key, inRegCode, inExpiryDate, releaseCode);
Result := BufferToHex(unlockCode, SizeOf(releaseCode));
end;
{ Used in your program to authenticate if the release code is valid - does not check for expiry }
function AuthenticateReleaseCode(const inReleaseCodeHexString : string; const inAppKey : TKey) : Boolean;
var
releaseCode : TCode;
begin
HexToBuffer(inReleaseCodeHexString, releaseCode, SizeOf(releaseCode));
Result := IsRegCodeValid(inAppKey, releaseCode);
end;
{ Used in your program to test if the license has expired }
function UnlockCodeExpiryDate(const inReleaseCodeHexString : string; const inAppKey : TKey) : TDateTime;
var
releaseCode : TCode;
begin
HexToBuffer(inReleaseCodeHexString, releaseCode, SizeOf(releaseCode));
Result := GetExpirationDate(inAppKey, releaseCode);
end;
I do use OnGuard extensively but only for enterprise software where piracy isn't such an issue. If you're selling consumer software and your worried about piracy I'd recommend a stronger solution such as a commercial copy protection library that encrypts the exe.
Even then you can slow the crackers down, but you can't stop them.
3) You should "scatter" the key around and maybe having part of it computed someway. The simpler to identify the key, of course the simpler to bypass protection. But even a complex key is useless if a simple JMP instruction in the proper place will bypass the whole protection check. Those checks should be also more than one, and again, scattered around.
4) Be careful with such kind of licenses - usually users don't like them unless the annual fee means also some perceived value (i.e. an antivirus gives you update signatures, or a GPS application updated maps). Just forcing users to pay annually may look good for you, but not for users, even if you add new features they may not regard as useful. If the application stop working is even worse. That's one of the issue that killed many Unix applications when Windows application with the same features but without yearly fees became available. I know that many companies are thinking to return to that model - if it will be successful is yet to be seen.

Resources