java - Android 中加密文本时解密返回空字符串

标签 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 来存储加密文本,并使用共享首选项来解密文本并将其提供给 TextView。但在 getPassword() 中,我获取 SharedPreference 值并尝试解密 SharedPrefs 中是否有任何值。我将 SharedPrefs 放入字符串(password)并尝试解密它,但我无法!我得到一个空字符串!

最佳答案

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

由于您没有生成任何 IV,因此系统将为您生成它。错误的 IV 仅影响第一个 block (AES 的前 16 个字节)。如果你的明文比一个 block 短,那么这将导致完全不同的解密,然后填充无法被删除,概率约为 255/256。

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

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];
}

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

private static final byte[] IV = new byte[16];
...
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV));
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV));

关于java - Android 中加密文本时解密返回空字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37932240/

相关文章:

java - 如何将 Oracle Responsys 与 Java 应用程序结合使用

java - OnClick ButterKnife,没有任何反应

android - 尝试将多个复选框添加到 Android Studio 上的数组中

android - 代号一 - ToastBar when "No connectivity detected"

file - Python将文本文件读取为二进制文件?

java - 如何在 Android Studio 中获取错误列表窗口?

java - Intellij - 项目结构设置(spring 方面配置) - 它们存储在哪里?

android - Android Studio 更新后 ProGuard 无法正常工作

android - 在 Android 上加密图像的对称加密与非对称加密

ssl - TLS、 header 和代理 : how to answer the client?