W poprzednim wpisie pisałem o potędze polimorfizmu w programowaniu obiektowym. W tym postaram się podać bardziej przydatny przykład z życia na podstawie szyfrowania tekstu.
Do stworzenia mechanizmu potrzebuje interfejsu Icrypt, który zawiera przydatne metody dla każdego algorytmu szyfrowania np zaszyfruj, odszyfruj itp.
interface ICrypt { public function encrypt($plainText); public function decrypt(); public function setPlaintext($text); public function getCipher(); }
Nastepnie potrzebuje klasy abstrakcyjnej, która będzie zawierała implementacje niektórych metod oraz będzie implementowała interfejs Icrypt
abstract class Crypt implements ICrypt { protected $cipher; protected $plain; protected $k; public function setPlaintext($text) { $this->plain = $text; } public function getCipher() { return $this->cipher; } public function toString() { return $this->decrypt(); } }
Mając interfejs i klase abstrakcyjna mogę stworzyć konkretne klasy szyfrujące, jedna z nich to prosty algorytm cezara czyli przesuwanie o K znaków w alfabecie, natomiast druga to zaawansowany algorytm używany w wojsku o nazwie Rijndael, który jest używany jako standard AES(Advanced Encryption Standard).
class Caesar extends Crypt { static $ALPHABET_LENGTH = 254; public function Caesar() { $this->k = rand(1,self::$ALPHABET_LENGTH); } public function encrypt($plainText) { $this->plain = $plainText; if($this->plain && !empty($this->plain)) { $length = strlen($this->plain); $this->cipher = ""; for($i = 0; $i < $length ; ++$i) { $this->cipher[$i] = (ord($this->plain[$i]) + $this->k) % self::$ALPHABET_LENGTH; } } } public function decrypt() { $length = count($this->cipher); $plain = ""; for($i = 0; $i < $length ; ++$i) { $plain .= chr($this->cipher[$i] - $this->k % self::$ALPHABET_LENGTH); } return $plain; } public function getCipher() { $str = ""; print_r($this->cipher); foreach($this->cipher as $l) $str += (string)$this->cipher; return $str; } } class Rijndael extends Crypt { private $enc; private $mode; private $iv; public function Rijndael() { $this->enc = MCRYPT_RIJNDAEL_128; $this->mode = MCRYPT_MODE_CBC; $this->k = pack('H*', "00112233445566778899aabbccddee"); // 16 bytes } public function encrypt($plainText) { $this->iv = mcrypt_create_iv(mcrypt_get_iv_size($this->enc, $this->mode), MCRYPT_DEV_URANDOM); $this->cipher = mcrypt_encrypt($this->enc, $this->k, $plainText, $this->mode, $this->iv); } public function decrypt() { return strtok(mcrypt_decrypt($this->enc, $this->k, $this->cipher, $this->mode, $this->iv), "\0"); } public function getCipher() { return bin2hex($this->cipher); } }
Mając te dwie klasy można przetestować szyfrowanie:
$ceasar = new Caesar(); $ceasar->encrypt('abcdefgh'); echo "Cezar dekrypt:".$ceasar->decrypt()."\n"; //demo Advanced Encryption Standard(AES) $aes = new Rijndael(); $aes->encrypt('abcdefgh'); echo 'aes szyfrogram:'. $aes->getCipher()."\n"; echo "Aes dekrypt:".$aes->decrypt()."\n"; //resultat: //Cezar dekrypt:abcdefgh //aes szyfrogram:80f6dda813ed89af3f1d4b3bbaaee426 //Aes dekrypt:abcdefgh
Teraz przejdzmy do sedna, stworzymy funkcję, która nie wiedząc jakiego algorytmu używa po prostu zaszyfruje nam tekst dostając obiekt. Funkcja ta wie, że ten algorytm będzie posiadał metody szyfruj i deszyfruj ang encrypt, decrypt i to jej wystarcza.
function encrypt_me($string, ICrypt $encryptor) { $encryptor->encrypt($string); //encrypts string using ICrypt object echo $encryptor->decrypt($string); //decrypts string echo "\n"; }
i test tej funkcji:
encrypt_me("top secret data", $ceasar); encrypt_me("top secret data", $aes);
Całość kodu można zobaczyć i przetestować tutaj. Jeśli ktoś chce używać kodu w komercyjnych aplikacjach powinien zapewnić losowe generowanie klucza, gdyż w klasie, którą napisałem klucz jest stały i tak nie powinno być. Polecam też użyć innego mechanizmu wiązania bloków w zależności od potrzeb. Nie polecam używać EBC gdyż jest to najłatwiejszy sposób do złamania przez hakerów i nie wykorzystuje mechanizmu wektora inicacji (IV).
Zostaw komentarz