AES Инициализация Вектор рандомизации - PullRequest
0 голосов
/ 06 мая 2018

Я пытаюсь повторно использовать реализацию AES с вектором инициализации. Пока что я только реализую ту часть, где данные шифруются в приложении для Android и расшифровываются на php-сервере. Тем не менее, алгоритм имеет большую лазейку: постоянный вектор инициализации, который я недавно обнаружил, является серьезным недостатком безопасности. К сожалению, я уже реализовал это в каждой активности моего приложения и во всех сценариях на стороне сервера. Я хотел знать, есть ли способ изменить этот код так, чтобы вектор инициализации был рандомизирован, и каким-то образом отправить этот вектор на сервер (или наоборот), чтобы каждый раз, когда сообщение шифровалось, шаблон постоянно менялся. Вот мои коды для Android и PHP:

Android:

package com.fyp.merchantapp;

// This file and its contents have been taken from http://www.androidsnippets.com/encrypt-decrypt-between-android-and-php.html 
//Ownership has been acknowledged

import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class MCrypt {
static char[] HEX_CHARS = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};

private String iv = "MyNameIsHamza100";//(IV)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;

private String SecretKey = "MyNameIsBilal100";//(SECRETKEY)

public MCrypt()
{
    ivspec = new IvParameterSpec(iv.getBytes());

    keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public byte[] encrypt(String text) throws Exception
{
    if(text == null || text.length() == 0)
        throw new Exception("Empty string");

    byte[] encrypted = null;

    try {
        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

        encrypted = cipher.doFinal(padString(text).getBytes());
    } catch (Exception e)
    {
        throw new Exception("[encrypt] " + e.getMessage());
    }

    return encrypted;
}

public byte[] decrypt(String code) throws Exception
{
    if(code == null || code.length() == 0)
        throw new Exception("Empty string");

    byte[] decrypted = null;

    try {
        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

        decrypted = cipher.doFinal(hexToBytes(code));
        //Remove trailing zeroes
        if( decrypted.length > 0)
        {
            int trim = 0;
            for( int i = decrypted.length - 1; i >= 0; i-- ) if( decrypted[i] == 0 ) trim++;

            if( trim > 0 )
            {
                byte[] newArray = new byte[decrypted.length - trim];
                System.arraycopy(decrypted, 0, newArray, 0, decrypted.length - trim);
                decrypted = newArray;
            }
        }
    } catch (Exception e)
    {
        throw new Exception("[decrypt] " + e.getMessage());
    }
    return decrypted;
}


public static String bytesToHex(byte[] buf)
{
    char[] chars = new char[2 * buf.length];
    for (int i = 0; i < buf.length; ++i)
    {
        chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
        chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
    }
    return new String(chars);
}


public static byte[] hexToBytes(String str) {
    if (str==null) {
        return null;
    } else if (str.length() < 2) {
        return null;
    } else {
        int len = str.length() / 2;
        byte[] buffer = new byte[len];
        for (int i=0; i<len; i++) {
            buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
        }
        return buffer;
    }
}



private static String padString(String source)
{
    char paddingChar = 0;
    int size = 16;
    int x = source.length() % size;
    int padLength = size - x;

    for (int i = 0; i < padLength; i++)
    {
        source += paddingChar;
    }

    return source;
}
}

PHP:

<?php
class MCrypt
{
    private $iv = 'MyNameIsHamza100'; #Same as in JAVA
    private $key = 'MyNameIsBilal100'; #Same as in JAVA
    function __construct()
    {
    }
    /**
     * @param string $str
     * @param bool $isBinary whether to encrypt as binary or not. Default is: false
     * @return string Encrypted data
     */
    function encrypt($str, $isBinary = false)
    {
        $iv = $this->iv;
        $str = $isBinary ? $str : utf8_decode($str);
        $td = mcrypt_module_open('rijndael-128', ' ', 'cbc', $iv);
        mcrypt_generic_init($td, $this->key, $iv);
        $encrypted = mcrypt_generic($td, $str);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        return $isBinary ? $encrypted : bin2hex($encrypted);
    }
    /**
     * @param string $code
     * @param bool $isBinary whether to decrypt as binary or not. Default is: false
     * @return string Decrypted data
     */
    function decrypt($code, $isBinary = false)
    {
        $code = $isBinary ? $code : $this->hex2bin($code);
        $iv = $this->iv;
        $td = mcrypt_module_open('rijndael-128', ' ', 'cbc', $iv);
        mcrypt_generic_init($td, $this->key, $iv);
        $decrypted = mdecrypt_generic($td, $code);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        return $isBinary ? trim($decrypted) : utf8_encode(trim($decrypted));
    }
    protected function hex2bin($hexdata)
    {
        $bindata = '';
        for ($i = 0; $i < strlen($hexdata); $i += 2) {
            $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
        }
        return $bindata;
    }
}
?>

1 Ответ

0 голосов
/ 06 мая 2018

Чтобы прямо ответить на ваш вопрос: вы можете просто сгенерировать случайный IV и добавить его к зашифрованному тексту . Вам нужно сделать это перед кодированием зашифрованного текста в шестнадцатеричные числа. Затем во время дешифрования сначала декодируйте, затем «удаляйте» байты IV, инициализируйте IV и, наконец, расшифруйте зашифрованный текст, чтобы получить открытый текст.

Обратите внимание, что IV всегда будет 16 байтов для AES в режиме CBC, поэтому нет прямой необходимости включать длину IV где-либо. Я использовал кавычки вокруг «удалить», так как оба IvParameterSpec как Cipher.doFinal принимают буферы со смещением и длиной; нет необходимости копировать байты в разные массивы.


Примечания:

  • ключи не должны быть строками; поиск PBKDF, например PBKDF2, для получения ключа из пароля или парольной фразы;
  • CBC, как правило, уязвим к атакам оракулов; однако, придерживаясь нулевого отступа PHP, вы могли избежать случайных атак;
  • CBC не обеспечивает защиту целостности, поэтому обратите внимание, что злоумышленники могут изменить шифротекст без сбоя расшифровки;
  • если базовый код, использующий текст, генерирует ошибки, то вы можете быть уязвимы к атакам оракула с открытым текстом (атаки оракула с дополнением являются лишь частью большей группы оракулов с открытым текстом);
  • ваш Java-код не сбалансирован; режим шифрования и дешифрования должен либо выполнять шестнадцатеричное кодирование / декодирование, либо не должен;
  • обработка исключений, конечно, не очень хорошая (хотя это может быть только для примера);
  • String#getBytes() будет использовать UTF-8 на Android, но он может использовать Windows-1252 на Java SE на Windows, поэтому это может привести к генерации неправильного ключа, если вы не будете осторожны - всегда определяйте набор символов для использования .

Чтобы использовать общий секретный ключ для связи, попробуйте TLS в режиме предварительного общего секретного ключа, определенного одним из наборов шифров PSK_.

...