3DES CB Шифрование Динамический Ключ

Парень сказал мне зашифровать данные с помощью динамического ключа, сгенерированного по следующему алгоритму

timestamp  = '080717032817'    
static_key =  A270AE59FF7782A2EDFE1A781BAB888D0B749D265865C288
k1 = first 5 bytes of the static key
k2 = first 3 bytes of the integer value of the timestamp
k3 = last  5 bytes of the static key
k4 = last  5 bytes of the timestamp
dynamic_key = k1k2k3k4

Он сказал, что данные должны быть дополнены:

pad to reach a multiple of the blocksize (8byte for 3DES CBC),

или же

 pad with 8 null bytes if the length of the data is already multiple of 8.

В его примере с iv='0123456789ABCDEF' он получает:

<DATA>       <TIMESTAMP>    <BASE64 CRYPTED DATA>
3408682266,080717032817,hkUIYwssDQCq0GLx/5MiZg==

Чтобы реализовать алгоритм, я написал этот класс функций

private function __encrypt($data,$parameters,$time,$convert = false)
    {

        $staticKey  =  
        $mode       = MCRYPT_MODE_CBC;
        $ivi        = '0123456789ABCDEF';
        $cipher     = MCRYPT_3DES;
        $this->log[] = "Encrypt params: mode => {$mode}, cipher => {$cipher}, iv =>{$ivi}";
        $dynamicKey =  
        $iv         = pack('H*', $ivi);
        $this->log[] = 'Initial Vector '. var_dump($iv);         
        $data = $this->__padder($data,mcrypt_get_block_size($cipher, $mode),$convert);

        $this->log[] = ('Data After padding: ' . $data .", length (bytes):" . strlen($data)/2);

        try {
             $output = mcrypt_encrypt($cipher, $dynamicKey, $data, $mode,$iv);

        } catch (Exception $ex) {
             debug($ex->getMessage());
            throw new Exception($ex->getMessage());

        }

        return $output;
    }

/**
     * Generate a dynamic key based on a timestamp and a static key
     * @param type $static_key
     */
    private function __generateDynamicKey($static_key = '', $time)
    {

        $dateObj = DateTime::createFromFormat("ymdHis", $time);


        $k[1]    = substr($static_key,0,  10);

        $k[2]    = substr($time,0,6);debug($k[2]);

        $k[3]    = substr($static_key,strlen($static_key) - 10,  10);        

        $k[4]    = substr($time,strlen($time)-6,6);debug($k[4]); //last 3 bytes
        $this->log[] = ("Dynamic key =>".join("",$k). ', length in bytes=>'. strlen(pack('H*', join("",$k))));

        return  pack('H*', join("",$k));

    }
/**
     * 
     * @param type $data
     * @param type $blockSize
     * @param type $convert
     * @return string
     */
    private function __padder($data,$blockSize = 8,$convert = false)
    {  

        if ($convert)
            $data = Generic::strToHex($data); // hex representation of the data

        $this->log[] = 'Block size of cipher: ' .$blockSize;
        $this->log[] = ("Hex value before padding=>".($data) );
        //Chek if the data is padded to 16 bytes        
        $dataBytes   = strlen($data) / 2 ; //  1 byte = 2 Hex digits
        $this->log[] = "Data num. of bytes " . $dataBytes;
        $rest      = $dataBytes % $blockSize;    // The num of bytes is a multiple of blockSize ?
        $nearest   = ceil($dataBytes/$blockSize ) * $blockSize;
        $output    = $data;
        if ($rest != 0)
        {
            $delta       = ($nearest - $dataBytes); // in bytes
            $deltaValue  = Generic::zeropad($delta, 2);            
            $this->log[] = ('padding value '.$deltaValue);

        }
        else
        {  
            $this->log[] = ('Add 8 bytes of padding!');
            $delta       = 8;
            $deltaValue  = '00';

        }

        $output = $data . str_repeat($deltaValue, $delta);
        $this->log[] = ('Hex  value after padding '. $output . ', length in bytes =>' . strlen($output)/2);

        return $output;
    }

public function test($clearUserCode)
{
 $userCode = $this->__encrypt($clearUserCode,$provider,$time,true); //UserCode is given as string, mut be converted in hex
        $this->log[] = ('UserCode Clear : ' . $clearUserCode . ', in hex: ' . Generic::strToHex($clearUserCode));
        $this->log[] = ('UserCode Crypt : ' . bin2hex($userCode));
        $this->log[] = ('UserCode Crypt and base64: ' . base64_encode(($userCode)));

        $this->log[] = ('----------------------End encrypt UserCode part----------------------') ;
 }

И, наконец, где-то

$this->test('3408682266');

которые дают мне другой результат:

UserCode Clear : 3408682266, in hex: 33343038363832323636
UserCode Crypt : 9d7e195a8d85aa7d051362dfae0042c2
UserCode Crypt and base64: nX4ZWo2Fqn0FE2LfrgBCwg==

Любой намек?

1 ответ

После поисков где-то я обнаружил, что 3DES хочет 192-битный ключ, и в некотором роде php"s mcrypt не делает это волшебным образом: вы должны дополнить ключ самостоятельно! Вот две функции, которые работают для примеров вопроса:

 /**
     * Make ciphering (3DES) in pkc7 (padding with 0 or a filling value)
     * key must bey 24 bytes (192 bits)
     * @param type $key
     * @param type $iv
     * @param type $text
     * @return type
     */
    public static function encryptNET3DES($key,$iv,$text)

    {
      $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');

      // Complete the key

      $key_add = 24-strlen($key);
      $key     .= substr($key,0,$key_add);


      // Padding the text 

      $block   = mcrypt_get_block_size("tripledes", "cbc");                
      $len     = strlen($text);           
      $padding = $block - ($len % $block);

      $text    .= str_repeat(chr($padding),$padding);           
      mcrypt_generic_init ($td, $key, $iv);
      $encrypt_text = mcrypt_generic ($td, $text);
      mcrypt_generic_deinit($td);
      mcrypt_module_close($td);
      return $encrypt_text;    

}    
    /**
     * Decrypt 3DES encrypted data with 24-bit key 
     * @param type $key
     * @param type $iv
     * @param type $text
     * @return type
     */
    public static function decryptNET3DES($key,$iv,$text)

    {

    $td = mcrypt_module_open (MCRYPT_3DES, "", MCRYPT_MODE_CBC, "");
    // Complete the key
    $key_add  = 24-strlen($key);
    $key      .= substr($key,0,$key_add);
    mcrypt_generic_init ($td, $key, $iv);
    $decrypt_text = mdecrypt_generic ($td, $text);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    //remove the padding text

    $block   = mcrypt_get_block_size("tripledes", "cbc");
    $packing = ord($decrypt_text{strlen($decrypt_text) - 1}); 
    if($packing && ($packing < $block))
    // Get rid of padded data
    {        
          for($P = strlen($decrypt_text) - 1; $P >= strlen($decrypt_text) - $packing; $P--)          {
               if(ord($decrypt_text{$P}) != $packing)
               {
                   $packing = 0;
               }
         }
    }

   $decrypt_text = substr($decrypt_text,0,strlen($decrypt_text) - $packing);

   return $decrypt_text;

}
Другие вопросы по тегам