Android AES (с Keystore) создает другой зашифрованный текст с одним и тем же открытым текстом

Я пытаюсь сделать так, чтобы хранилище ключей помогло мне сгенерировать ключ для шифрования AES и использовать его для шифрования простого текста, который я ввел. Итак, вот мои коды. Я вызываю метод createKey() только один раз в методе onCreate() другого действия, а затем много раз вызываю метод printCipherText() с одним и тем же keyAlias ​​и одним и тем же открытым текстом. Странная вещь: каждый раз, когда я вызываю метод printCipherText(), я получаю другой результат. Я использую один и тот же псевдоним ключа и один и тот же открытый текст, но почему я каждый раз получаю разный зашифрованный текст?

public class KeyCreatorClass {

    KeyStore keyStore;
    KeyGenerator keyGenerator;
    Cipher cipher;

    public void createKey(String keyAlias) { //I call this method only once in the onCreate() method of another activity, with keyAlias "A"
        try {
            keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            keyStore.load(null);
            keyGenerator.init(
                    new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                        .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                        .setUserAuthenticationRequired(false)
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                        .setRandomizedEncryptionRequired(false)
                        .build());
            keyGenerator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String printCipherText(String keyAlias, String plainText){ //I call this method many times with the same keyAlias "A" and same plaintext in the same activity
        try {
            keyStore.load(null);
            SecretKey key = (SecretKey) keyStore.getKey(keyAlias, null);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return byteToHex(cipher.doFinal(plainText.getBytes()));
        }catch(Exception e){
            e.printStackTrace();
        }
        return "BUG";
    }

    private String byteToHex(byte[] byteArray){
        StringBuilder buf = new StringBuilder();
        for (byte b : byteArray)
            buf.append(String.format("%02X", b));
        String hexStr = buf.toString();
        return hexStr;
    }
} 

person Chris Han    schedule 24.04.2016    source источник
comment
Во всех этих случаях вы получаете исходный текст обратно при расшифровке?   -  person MikeC    schedule 24.04.2016
comment
Спасибо, я думаю, что @Artjom B. только что решил мою проблему   -  person Chris Han    schedule 25.04.2016


Ответы (1)


Вы используете режим CBC, который использует вектор инициализации (IV). Поскольку вы не указываете IV в своем коде, он будет генерироваться случайным образом при каждом вызове кода. Это важное свойство, чтобы не позволить наблюдателю шифротекстов определить, есть ли сообщение, которое вы отправили снова. Это необходимо для достижения семантической безопасности.

Поскольку IV генерируется случайным образом, вам нужен тот же IV, который использовался при шифровании и при дешифровании. IV не обязательно должен быть секретным, но он должен быть непредсказуемым (как это и есть). Один из распространенных способов — записать его перед зашифрованным текстом и прочитать обратно во время расшифровки. Он всегда имеет одинаковую длину, которая является размером блока. Этот размер составляет 16 байт для AES.

person Artjom B.    schedule 24.04.2016
comment
Большое спасибо! Кстати, можете ли вы привести пример обычного способа использования IV? - person Chris Han; 24.04.2016
comment
В вашем случае простой return byteToHex(cipher.getIV()) + byteToHex(cipher.doFinal(plainText.getBytes())); должен работать наоборот, используя подстроку во время расшифровки. Если вы хотите, чтобы ваше шифрование работало для больших данных, вам следует использовать потоки. - person Artjom B.; 24.04.2016