在加密Android中的文本时,解密会返回一个空字符串

时间:2016-06-20 21:40:09

标签: java android encryption android-security

我正在尝试安全保存少量文本字段。为此,我正在尝试加密和解密内容。这是代码:

public class SecureStorage {

    public String getPassword() {
        if(!isRooted()) {
            String password = pref.getPassword("");
            System.out.println("pass getPass: " + password);
            return password.isEmpty() ? password : new String(decrypt(Base64.decode(password, Base64.DEFAULT)));

        } else
            return "";
    }

    public void setPassword(String passwordStr) {
        if(!isRooted()) {
            byte[] password = encrypt(passwordStr.getBytes());
            pref.setPassword(password == null ? "" : Base64.encodeToString(password, Base64.DEFAULT));
        }
    }

    private SecretKey generateKey() {
        // Generate a 256-bit key
        final int outputKeyLength = 256;
        try {
            SecureRandom secureRandom = new SecureRandom();
            // Do *not* seed secureRandom! Automatically seeded from system entropy.
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(outputKeyLength, secureRandom);
            return keyGenerator.generateKey();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    private byte[] getRawKey(byte[] key) throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto");
        secureRandom.setSeed(key);
        keyGenerator.init(128, secureRandom); // 192 and 256 bits may not be available
        SecretKey secretKey = keyGenerator.generateKey();
        byte[] rawKey = secretKey.getEncoded();
        return rawKey;
    }

    /** The method that encrypts the string.
     @param toEncrypt The string to be encrypted.
     @return The encrypted string in bytes. */
    //****************************************
    private byte[] encrypt(byte[] toEncrypt) {
        byte[] encryptedByte = new String().getBytes();
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            encryptedByte = cipher.doFinal(toEncrypt);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptedByte;
    }

    //**************************************
    /** The method that decrypts the string.
     @param encryptedByte The string to be encrypted.
     @return The decrypted string in bytes. */
    //****************************************
    private byte[] decrypt(byte[] encryptedByte) {
        byte[] decryptedByte = new String().getBytes();
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            decryptedByte = cipher.doFinal(encryptedByte);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return decryptedByte;
    }
}

我可以加密文本。 我正在使用SharedPreferences存储加密文本并获取sharedprefs来解密文本并将其提供给TextView。但是在getPassword()我得到SharedPreference值并尝试解密,如果SharedPrefs中有任何值。我将SharedPrefs变成一个字符串(password)并试图解密它,但我无法做到!我得到一个空字符串!

1 个答案:

答案 0 :(得分:1)

CBC模式需要初始化矢量(IV)才能运行。 IV用于随机化密文并防止攻击者确定先前的明文是否具有与当前明文相同的前缀。

由于您未生成任何IV,因此将为您生成。错误的IV仅影响第一个块(AES的前16个字节)。如果你的明文短于一个块,那么这将导致完全不同的解密,然后填充不能以大约255/256的概率被移除。

IV不应该是秘密的。通常将它添加到密文并在解密之前将其切掉。

setVerticalTextPosition()

这个问题可能是密文比预期的要大(IV的另外16个字节)。如果您可以确保攻击者在确定先前的明文具有相同的前缀时没有获得任何有用的信息,那么您可以使用静态IV。但请注意,这通常不是一个好主意,只有在确实需要那个空间时才应该这样做。

public byte[] encrypt(byte[] toEncrypt) throws Exception {
    try {
        SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        byte[] iv = cipher.getIV();
        byte[] ct = cipher.doFinal(toEncrypt);

        byte[] result = new byte[ct.length + iv.length];
        System.arraycopy(iv, 0, result, 0, iv.length);
        System.arraycopy(ct, 0, result, iv.length, ct.length);
        return result;
    } catch(...) {...}
    return new byte[0];
}

public byte[] decrypt(byte[] encryptedByte) throws Exception {
    try {
        SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        byte[] iv = new byte[cipher.getBlockSize()];
        byte[] ct = new byte[encryptedByte.length - cipher.getBlockSize()];
        System.arraycopy(encryptedByte, 0, iv, 0, cipher.getBlockSize());
        System.arraycopy(encryptedByte, cipher.getBlockSize(), ct, 0, ct.length);

        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
        return cipher.doFinal(ct);
    } catch (...) {...}
    return new byte[0];
}