Cosa succede quando avete finito un software e volete distribuirlo, previa vostra autorizzazione? Questo How To vi viene incontro e vi spiega come io ho affrontato il problema.
La soluzione a tale problema è composta di due parti, un codice php posizionato nel sito dal quale distribuisco il software e dal quale faccio fare l'attivazione del prodotto.
Quella che segue è la classe PHP che mi genera il mio codice di attivazione nel formato: XXXX-XXXX-XXXX-XXXX
[code]
/*
Classe realizzata da Sammarco Francesco
Es:
$OggettoSN = new Serial_Number;
$CodSer = $OggettoSN->PKV_MakeKey($IdCod);
*/
class Serial_Number
{
// Costruttore
function __construct()
{
}
// Distruttore
public function __destruct()
{
//non faccio niente
}
private function Shr($a, $b)
{
return ($a/(2^$b));
}
private function PKV_GetKeyByte($Seed,$a,$b,$c)
{
$a = $a % 25;
$b = $b % 3;
if( $a % 2 == 0 )
{
$result = ( $Seed >> $a ) ^ ( ( $Seed >> $b ) | $c );
} else
{
$result = ( $Seed >> $a ) ^ ( ( $Seed >> $b ) & $c );
}
return $result & 0x000000FF;
}
private function IntToHex($Variabile,$Num)
{
$result=dechex($Variabile);
$k=$Num-strlen($result);
for($i=0;$i<$k;$i++)
{
$result="0" . $result;
}
return strtoupper($result);
}
public function PKV_MakeKey($Seed)
{
$KeyBytes[0]=$this->PKV_GetKeyByte($Seed, 24, 3, 200);
$KeyBytes[1]=$this->PKV_GetKeyByte($Seed, 10, 0, 56);
$KeyBytes[2]=$this->PKV_GetKeyByte($Seed, 1, 2, 91);
$KeyBytes[3]=$this->PKV_GetKeyByte($Seed, 7, 1, 100);
$result=$this->IntToHex($Seed,8);
for ($i=0;$i<=3;$i++)
{
$result=$result . $this->IntToHex($KeyBytes[$i],2);
}
$valorecs=$this->PKV_GetChecksum($result);
$result=$result . $valorecs;
$app="";
$j=1;
for ($i=0;$i<strlen($result);$i++)
{
if (($j == 4)&&($i<(strlen($result)-1)))
{
$app=$app . $result[$i] . "-";
$j=1;
}else
{
$app=$app . $result[$i];
$j++;
}
}
return $app;
}
private function PKV_GetChecksum($s)
{
$left = 0x0056;
$right = 0x00AF;
if(strlen($s) > 0 )
{
for( $i = 0 ; $i < strlen( $s ) ; $i++ )
{
$right = $right + Ord($s[$i]);
if($right > 0x00FF )
{
$right=$right-0x00FF;
}
$left=$left+$right;
if($left > 0x00FF )
{
$left=$left-0x00FF;
}
}
}
$sum = ( $left << 8 ) + $right;
$result = $this->IntToHex($sum, 4 );
return $result;
}
}
?>
[/code]
Usare tale classe è molto molto semplice basta usare il codice (sempre in php):
$OggettoSN = new Serial_Number;
$CodSer = $OggettoSN->PKV_MakeKey($IdCod);
Dove $IdCod equivale all'inizializzatore da cui mi ricavo il mio codice di attivazione e $CodSer è il nostro tanto agognato codice di serie.
Per rendere la nostra classe unica e diversa da questo codice di esempio basta cambiare i parametri numerici nella funzione PKV_GetKeyByte
$KeyBytes[0]=$this->PKV_GetKeyByte($Seed, 24, 3, 200);
$KeyBytes[1]=$this->PKV_GetKeyByte($Seed, 10, 0, 56);
$KeyBytes[2]=$this->PKV_GetKeyByte($Seed, 1, 2, 91);
$KeyBytes[3]=$this->PKV_GetKeyByte($Seed, 7, 1, 100);
Ora questa parte scritta in PHP crea un codice che comunico (tramite mail automatica) all'interessato.
Ora toccherà al programma (scritto in Lazarus) elaborare questa informazione, e io ho scritto una libreria per fare questo:
[code]
unit MySerialNumber;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
const
KEY_GOOD = 0;
KEY_INVALID = 1;
KEY_BLACKLISTED = 2;
KEY_PHONY = 3;
type
SerialNumber=object
private
public
function PKV_GetKeyByte(const Seed : Int64; a, b, c : Byte) : Byte;
function PKV_CheckKey(const S : String) : Integer;
function PKV_CheckKeyChecksum(const Key : String) : Boolean;
function PKV_MakeKey(const Seed : Int64) : String;
function PKV_GetChecksum(const s : String) : String;
end;
var
BL : array[0..0] of String = ('11111111');
implementation
{ serial number }
function StartsStr(str: string; inizio: string): boolean;
var
Esci: boolean;
ret: boolean;
i: integer;
len1: integer;
len2: integer;
begin
len1:=length(str);
len2:=length(inizio);
ret:=false;
if len1>=len2 then
begin
i:=0;
ret:=TRUE;
if len1=0 then
Esci:=TRUE
else
Esci:=FALSE;
while (Esci=False) do
begin
if str[i]<>inizio[i] then
begin
Esci:=TRUE;
ret:=FALSE;
end;
Inc(i);
if i>len1 then
Esci:=TRUE;
end;
end;
StartsStr:=ret;
end;
function SerialNumber.PKV_GetKeyByte(const Seed : Int64; a, b, c : Byte) : Byte;
var
risultato: byte;
begin
a := a mod 25;
b := b mod 3;
if a mod 2 = 0 then
risultato := ((Seed shr a) and $000000FF) xor ((Seed shr b) or c)
else
risultato := ((Seed shr a) and $000000FF) xor ((Seed shr b) and c);
PKV_GetKeyByte:=risultato;
end;
function SerialNumber.PKV_GetChecksum(const s : String) : String;
var
left, right, sum : Word;
i : Integer;
app: string;
begin
left := $0056;
right := $00AF;
if Length(s) > 0 then
for i := 1 to Length(s) do
begin
right := right + Byte(s[i]);
//app:= app + 'r: ' + s[i] + '(' + IntToStr(Byte(s[i])) + ')';
//right := right + Byte(s[i]);
if right > $00FF then
Dec(right, $00FF);
Inc(left, right);
if left > $00FF then
Dec(left, $00FF);
end;
sum := (left shl 8) + right;
result := IntToHex(sum, 4);
end;
function SerialNumber.PKV_MakeKey(const Seed : Int64) : String;
var
KeyBytes : array[0..3] of Byte;
i : Integer;
app: string;
begin
// Fill KeyBytes with values derived from Seed.
// The parameters used here must be extactly the same
// as the ones used in the PKV_CheckKey function.
// A real key system should use more than four bytes.
KeyBytes[0] := PKV_GetKeyByte(Seed, 24, 3, 200);
KeyBytes[1] := PKV_GetKeyByte(Seed, 10, 0, 56);
KeyBytes[2] := PKV_GetKeyByte(Seed, 1, 2, 91);
KeyBytes[3] := PKV_GetKeyByte(Seed, 7, 1, 100);
// the key string begins with a hexidecimal string of the seed
result := IntToHex(Seed, 8);
// then is followed by hexidecimal strings of each byte in the key
for i := 0 to 3 do
begin
result := result + IntToHex(KeyBytes[i], 2);
end;
// add checksum to key string
result := result + PKV_GetChecksum(result);
// Add some hyphens to make it easier to type
i := Length(result) - 3;
while (i > 1) do
begin
Insert('-', result, i);
Dec(i, 4);
end;
end;
function SerialNumber.PKV_CheckKeyChecksum(const Key : String) : Boolean;
var
s, c : String;
begin
result := False;
// remove cosmetic hypens and normalize case
s := UpperCase(StringReplace(Key, '-', '', [rfReplaceAll]));
if Length(s) <> 20 then
exit; // Our keys are always 20 characters long
// last four characters are the checksum
c := Copy(s, 17, 4);
SetLength(s, 16);
// compare the supplied checksum against the real checksum for
// the key string.
result := c = PKV_GetChecksum(s);
end;
function SerialNumber.PKV_CheckKey(const S : String) : Integer;
var
Key, kb : String;
Seed : Int64;
i : Integer;
b : Byte;
begin
result := KEY_INVALID;
if not PKV_CheckKeyChecksum(S) then
exit; // bad checksum or wrong number of characters
// remove cosmetic hypens and normalize case
Key := UpperCase(StringReplace(S, '-', '', [rfReplaceAll]));
// test against blacklist
if Length(BL) > 0 then
for i := Low(BL) to High(BL) do
if StartsStr(BL[i], Key) then
begin
result := KEY_BLACKLISTED;
exit;
end;
// At this point, the key is either valid or forged,
// because a forged key can have a valid checksum.
// We now test the "bytes" of the key to determine if it is
// actually valid.
// When building your release application, use conditional defines
// or comment out most of the byte checks! This is the heart
// of the partial key verification system. By not compiling in
// each check, there is no way for someone to build a keygen that
// will produce valid keys. If an invalid keygen is released, you
// simply change which byte checks are compiled in, and any serial
// number built with the fake keygen no longer works.
// Note that the parameters used for PKV_GetKeyByte calls MUST
// MATCH the values that PKV_MakeKey uses to make the key in the
// first place!
result := KEY_PHONY;
// extract the Seed from the supplied key string
if not TryStrToInt64('$' + Copy(Key, 1, 8), Seed) then
exit;
{$IFDEF KEY00}
kb := Copy(Key, 9, 2);
b := PKV_GetKeyByte(Seed, 24, 3, 200);
if kb <> IntToHex(b, 2) then
exit;
{$ENDIF}
{$IFDEF KEY01}
kb := Copy(Key, 11, 2);
b := PKV_GetKeyByte(Seed, 10, 0, 56);
if kb <> IntToHex(b, 2) then
exit;
{$ENDIF}
{$IFDEF KEY02}
kb := Copy(Key, 13, 2);
b := PKV_GetKeyByte(Seed, 1, 2, 91);
if kb <> IntToHex(b, 2) then
exit;
{$ENDIF}
{$IFDEF KEY03}
kb := Copy(Key, 15, 2);
b := PKV_GetKeyByte(Seed, 7, 1, 100);
if kb <> IntToHex(b, 2) then
exit;
{$ENDIF}
// If we get this far, then it means the key is either good, or was made
// with a keygen derived from "this" release.
result := KEY_GOOD;
end;
end.
[/code]
Quello che è importante è che i valori numerici della funzione PKV_GetKeyByte siano valorizzati in maniera identica con quelli del codice PHP
KeyBytes[0] := PKV_GetKeyByte(Seed, 24, 3, 200);
KeyBytes[1] := PKV_GetKeyByte(Seed, 10, 0, 56);
KeyBytes[2] := PKV_GetKeyByte(Seed, 1, 2, 91);
KeyBytes[3] := PKV_GetKeyByte(Seed, 7, 1, 100);
Per poter sfruttare questa libreria bastano poche linee di codice in free pascal
if length(Codice)=24 then
begin
Valore:=MioOggetto.PKV_CheckKey(Codice);
if Valore=0 then
begin
{CODICE DI ATTIVAZIONE VALIDO}
end;
end;
Dove la variabile Codice è valorizzata con il codice che precedentemente avevamo comunicato al nostro cliente.
Bene questa è la soluzione che avevo usato io in uno dei miei progetti. Se avete altre soluzioni non esitate a comunicarmele e vedrò di pubblicarle. Avverto che pubblicherò solo soluzioni che possono essere multipiattaforma.