W jednym z projektów w C# realizowałem rejestrację i walidację licencji, która musiała być szyfrowana i odszyfrowywana za pomocą klucza w dowolnej liczbie bitów. Problem z pozoru prosty nie był taki do końca. Po zrealizowaniu projektu klient poprosił mnie aby algorytm, który generuje klucz licencji był generowany za pomocą PHP a nie C# jak było na początku. No cóż.. zakasałem rękawy i do roboty :)

Poniżej przedstawię klasę XorEncrypter, która szyfruje elementy wejściowe i umożliwia wygenerowanie zaszyfrowanego ciągu dowolnej wielkości kluczem. W kolejnym poście przedstawię klasę działającą w sposób odwrotny.

<?php

/**
 * @author Mateusz Manaj
 * @company Firma Softgraf
 * @see http://www.mateuszmanaj.pl/post/9,szyfrowanie-danych---xorencrypter.html
 */

class XorEncrypter
{
    private $_b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk.mnopqrstuvwxyz-123456789+/=";

    public function Encrypt($key, $data)
    {
        $keypos = 0;
        $binarydata = "";

        $key = base64_encode($key);
        $data = base64_encode($data);

        for($i=0; $i<strlen($data); $i++)
        {
            $xor = (ord($data[$i]) ^ ord($key[$keypos])) + strlen($key);
            if(++$keypos >= strlen($key))
            {
                $keypos = 0;
            }

            $binarydata .= $this->DecToBin($xor, 8);
        }

        $m = 0;
        $cipher = "";

        for($i=0; $i<strlen($binarydata); $i+=4)
        {
            $v = bindec(substr($binarydata, $i, 4));
            $cipher .= $this->GetB64FromN(((int)$v * 4) + $m);
            if(++$m > 3)
            {
                $m = 0;
            }
        }

        return $cipher;
    }

    private function DecToBin($val, $len)
    {
        $bin = decbin($val);
        $div = $len - strlen($bin);

        if($div <= 0) return $bin;

        $nBin = "";

        for($i=1; $i<=$div; $i++)
		{
			$nBin .= "0";
		}

        $nBin .= $bin;

        return $nBin;
    }

    private function GetB64FromN($n)
    {
        if($n > strlen($this->_b64)) return "=";
        return $this->_b64[$n];
    }
}

$xor = new XorEncrypter();
$key = "78b7fce3e50d14e2e3452a285daa871a";
$code = $xor->Encrypt($key, "Mateusz Manaj#Softgraf#ul.Pogodna 123#Katowice");
echo "Your key is:".$key."
"; echo "Your license code is:".$code; ?>

Zmienna członkowska $_b64 przechowuje wszystkie dostępne znaki szyfru.
Metoda DecToBin – własna implementacja translacji liczby dziesiętnej na binarną, na podstawie funkcji PHP decbin.
Metoda GetB64FromN zwraca element o numerze podanym w parametrze; np. $n = 3 zatem zwrócony zostanie 3 element z ciągu w zmiennej $_b64.

<?php
$xor = new XorEncrypter();
$key = "78b7fce3e50d14e2e3452a285daa871a";
$code = $xor->Encrypt($key, "Mateusz Manaj#Softgraf#ul.Pogodna 123#Katowice");
echo "Your key is:".$key."
"; echo "Your license code is:".$code; ?>

Zmienna $key przechowuje dowolnej długości klucz szyfrujący. W tym przypadku posługuję się 32-bitowym hashem.
W metodzie Encrypt podajemy klucz – $key oraz dowolny ciąg znakowy, który chcemy zaszyfrować – $data.

W rezultacie powinniśmy otrzymać coś takiego:

Your key is:78b7fce3e50d14e2e3452a285daa871a
Your license code is:QZWnUpiXQBmbMhOzYtanMhS7QNK/oNK/UhanQhaTUtivMtebQBqPQtO3I9anMNOHUhSDkNaXYdmLI9eHQNOzkxiHMNarU.OzI9efQBWLI9OjQhSbUZmLUpK3QNOHkte3

Pozdrawiam :)