BadPaddingException в шифровании Android - PullRequest
5 голосов
/ 02 февраля 2011

Я делаю приложение для Android и хочу зашифровать строку перед отправкой в ​​базу данных, и шифрование правильное.Проблема возникает при расшифровке строки, потому что я получаю исключение BadPaddingException, и я понятия не имею, где проблема.Вот код:

public final static String HEX = "36A52C8FB7DF9A3F";

public static String encrypt(String seed, String cleartext) throws Exception 
{
    byte[] rawKey = getRawKey(seed.getBytes());
    byte[] result = encrypt(rawKey, cleartext.getBytes());
    return toHex(result);
}

public static String decrypt(String seed, String encrypted) throws Exception 
{
    byte[] rawKey = getRawKey(seed.getBytes());
    byte[] enc = toByte(encrypted);
    byte[] result = decrypt(rawKey, enc);
    return new String(result);
}

public static String toHex(String txt) {
    return toHex(txt.getBytes());
}

public static String fromHex(String hex) {
    return new String(toByte(hex));
}

public static byte[] toByte(String hexString) {
    int len = hexString.length()/2;
    byte[] result = new byte[len];
    for (int i = 0; i < len; i++)
        result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
    return result;
}

public static String toHex(byte[] buf) {
    if (buf == null)
        return "";
    StringBuffer result = new StringBuffer(2*buf.length);
    for (int i = 0; i < buf.length; i++) {
        appendHex(result, buf[i]);
    }
    return result.toString();
}

private static byte[] getRawKey(byte[] seed) throws Exception {
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    sr.setSeed(seed);
    kgen.init(128, sr); // 192 and 256 bits may not be available
    SecretKey skey = kgen.generateKey();
    byte[] raw = skey.getEncoded();
    return raw;
}

private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(clear);
    return encrypted;
}

private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] decrypted = cipher.doFinal(encrypted);
    return decrypted;
}

private static void appendHex(StringBuffer sb, byte b) {
    sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
}

Я шифрую и дешифрую этим кодом:

String encrypted = encrypt(HEX, "some text");
String decrypted = decrypt(HEX, encrypted);

Кто-нибудь может мне помочь, пожалуйста?

Спасибо большое !!

РЕДАКТИРОВАТЬ: Проблема не решена, но у меня есть немного больше информации.Прежде всего, я шифрую в проекте Java и дешифрую в проекте Android.Я пытался расшифровать в том же проекте Java, и нет никаких проблем, но если я пытаюсь расшифровать в Android, это не сработает.Проблема в методе "getRawKey" (посмотрите комментарий kgen.generateKey ()):

JAVA:

private static byte[] getRawKey(byte[] seed) throws Exception {
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    sr.setSeed(seed); //Seed: [51, 54, 65, 53, 50, 67, 56, 70, 66, 55, 68, 70, 57, 65, 51, 70]
    kgen.init(128, sr); // 192 and 256 bits may not be available
    SecretKey skey = kgen.generateKey(); //skey.key = [-97, -52, 45, -95, -64, -58, 16, -20, 124, -50, -104, 58, 23, -75, 88, 94]
    byte[] raw = skey.getEncoded();
    return raw;
}

ANDROID:

private static byte[] getRawKey(byte[] seed) throws Exception {
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    sr.setSeed(seed); //Seed: [51, 54, 65, 53, 50, 67, 56, 70, 66, 55, 68, 70, 57, 65, 51, 70]
    kgen.init(128, sr); // 192 and 256 bits may not be available
    SecretKey skey = kgen.generateKey(); //skey.key = [-114, 32, 16, -52, -81, 125, -88, 88, -76, 20, -117, -11, 33, -61, 32, -91]
    byte[] raw = skey.getEncoded();
    return raw;
}

Я не эксперт по криптам, но как может быть так, что с одним и тем же семенем я получу другой ключ ??

Ответы [ 4 ]

3 голосов
/ 01 июля 2011

У меня были некоторые проблемы. Решение:

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

public class StringEncrypter {

Cipher ecipher;
Cipher dcipher;

StringEncrypter(String password) {

    // 8-bytes Salt
    byte[] salt = {
        (byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
        (byte)0x56, (byte)0x34, (byte)0xE3, (byte)0x03
    };

    // Iteration count
    int iterationCount = 19;

    try {

        KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount);
        SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);

        ecipher = Cipher.getInstance(key.getAlgorithm());
        dcipher = Cipher.getInstance(key.getAlgorithm());

        // Prepare the parameters to the cipthers
        AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

        ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
        dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

    } catch (InvalidAlgorithmParameterException e) {
        System.out.println("EXCEPTION: InvalidAlgorithmParameterException");
    } catch (InvalidKeySpecException e) {
        System.out.println("EXCEPTION: InvalidKeySpecException");
    } catch (NoSuchPaddingException e) {
        System.out.println("EXCEPTION: NoSuchPaddingException");
    } catch (NoSuchAlgorithmException e) {
        System.out.println("EXCEPTION: NoSuchAlgorithmException");
    } catch (InvalidKeyException e) {
        System.out.println("EXCEPTION: InvalidKeyException");
    }
}


/**
 * Takes a single String as an argument and returns an Encrypted version
 * of that String.
 * @param str String to be encrypted
 * @return <code>String</code> Encrypted version of the provided String
 */
public byte[] encrypt(String str) {
    try {
        // Encode the string into bytes using utf-8
        byte[] utf8 = str.getBytes("UTF8");

        // Encrypt
        byte[] enc = ecipher.doFinal(utf8);

        // Encode bytes to base64 to get a string
        //return new sun.misc.BASE64Encoder().encode(enc);
        return enc;

    } catch (BadPaddingException e) {
    } catch (IllegalBlockSizeException e) {
    } catch (UnsupportedEncodingException e) {
    }
    return null;
}


/**
 * Takes a encrypted String as an argument, decrypts and returns the
 * decrypted String.
 * @param str Encrypted String to be decrypted
 * @return <code>String</code> Decrypted version of the provided String
 */
public String decrypt(byte[] dec) {

    try {

        // Decode base64 to get bytes
        //byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
        //byte[] dec = Base64Coder.decode(str);

        // Decrypt
        byte[] utf8 = dcipher.doFinal(dec);

        // Decode using utf-8
        return new String(utf8, "UTF8");

    } catch (BadPaddingException e) {
    } catch (IllegalBlockSizeException e) {
    } catch (UnsupportedEncodingException e) {
    }
    return null;
}

}

Я нашел это решение здесь . Это работает отлично. Но мне действительно интересно знать, в чем была проблема с AES.

2 голосов
/ 04 июля 2013

Для тех, кто хочет использовать кодирование String и Base64 для использования с базой данных, вы можете использовать эти (слегка переписанные) функции от Майка Кескинова (и xalien на Anddev.org).

public String encrypt(String str) 
{
    try {

        // Encode the string into bytes using utf-8
        byte[] utf8 = str.getBytes("UTF8");

        // Encrypt
        byte[] enc = ecipher.doFinal(utf8);

        // Encode bytes to base64 to get a string 
        return new String(Base64.encode(enc,0));

    } catch (BadPaddingException e) {
    } catch (IllegalBlockSizeException e) {
    } catch (UnsupportedEncodingException e) {       
    }
    return null;
}

public String decrypt(String str) 
{
    try {

        // Decode base64 to get bytes           
        byte[] dec = Base64.decode(str, 0);

        // Decrypt
        byte[] utf8 = dcipher.doFinal(dec);

        // Decode using utf-8
        return new String(utf8, "UTF8");

    } catch (BadPaddingException e) {
    } catch (IllegalBlockSizeException e) {
    } catch (UnsupportedEncodingException e) {       
    }
    return null;
}

Этот код также использует встроенные библиотеки Android Base64, поэтому нет необходимости импортировать какие-либо дополнительные внешние библиотеки.

Сначала я не осознавал, как на самом деле использовать этот класс из другого класса (мое невежество). Вот простой пример:

StringEncrypter se = new StringEncrypter("mypass");
String decryptedString = se.decrypt(encryptedString);

Еще одна вещь на Base64. Я выяснил, каким сложным образом кодирование Base64 добавляет символ перевода строки '/ n' (0x0a) всякий раз, когда длина строки превышает 76 символов. Это сильно портит результаты дешифрования. Чтобы удалить эти символы новой строки, вы можете добавить что-то вроде:

b64_enc_str = se.encrypt(plaintext_str);
str = b64_enc_str.replace("/n","");

Надеюсь, это кому-нибудь поможет.

1 голос
/ 02 февраля 2011

В общем, я думаю, что BadPaddingException может быть вызвано следующими причинами:

i> Неправильный размер данных для алгоритма шифрования.

ii> Используются разные ключидля шифрования и дешифрования данных.

0 голосов
/ 10 сентября 2015

getRawKey предполагает, что экземпляр SecureRandom является четко определенным, детерминированным генератором псевдослучайных чисел.

Прежде всего, он не определен;метод "SHA1PRNG" точно не определен.Это может - и изменилось - даже для самого поставщика SUN.

Во-вторых, тот факт, что заполнение экземпляра SecureRandom непосредственно после построения делает его детерминированным, зависит от поставщика SUN.Другими словами, другие провайдеры могут добавить начальное число в пул энтропии.Этот пул энтропии, возможно, уже был заполнен значениями, полученными от операционной системы.Это относится ко многим версиям Android.Чтобы объяснить это с точки зрения непрофессионала: экземпляр Android SecureRandom является полностью случайным, даже если он был посеян сразу после создания .Это означает, что метод getRawKey в таких системах всегда генерирует новый, совершенно случайный ключ.

Так в чем же решение?Решением является либо сохранение ключа в KeyStore, либо генерация ключа из ключевой фразы.В этом случае вы можете использовать функцию PBKDF2 (функция получения ключа на основе пароля # 2), которая уже присутствует в java SE и Android:

SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
// note, the third argument should be set to a value as high as possible
// 10K is about the minimum nowadays
KeySpec ks = new PBEKeySpec(password, salt, 1024, 128);
SecretKey s = f.generateSecret(ks);
Key k = new SecretKeySpec(s.getEncoded(),"AES");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...