3

我目前正在 Android 上使用 AES 256 实现对称加密/解密,灵感来自这篇文章: Java 256bit AES Encryption。我实现的目的是我想加密数据库中的数据。

对于密钥生成,我使用以下构造函数,该构造函数采用 char[] 密码:

public Cryptography(char[] password) throws NoSuchAlgorithmException,
        InvalidKeySpecException, NoSuchPaddingException {

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
    KeySpec spec = new PBEKeySpec(password, salt, 1024, 256);
    secretKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
    cipher = Cipher.getInstance(AES/CBC/PKCS5Padding);
}

因此,当我在 Android 中启动我的 Activity 时,我初始化了我的 Cryptography 类的一个新实例,因此得到了一个生成的密钥。salt 是一个 16 字节的固定随机字节[]。所以这意味着我总是得到相同的密钥。后面的原因。

现在,在一个 Activity 中获得对象后,我可以使用以下加密和解密方法,始终使用相同的密钥:

public byte[] encrypt(String cleartext) throws InvalidKeyException,
        IllegalBlockSizeException, BadPaddingException,
        UnsupportedEncodingException, InvalidParameterSpecException {

    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    byte[] encText = cipher.doFinal(cleartext.getBytes(CHARSET_NAME));
    byte[] iv = cipher.getParameters()
            .getParameterSpec(IvParameterSpec.class).getIV();

    byte[] enc = new byte[IV_SIZE + encText.length];

    for (int i = 0; i < enc.length; i++) {
        if (i < IV_SIZE)
            enc[i] = iv[i];
        else if (i < enc.length)
            enc[i] = encText[i - IV_SIZE];
    }

    return enc;
}

public String decrypt(byte[] encryptedText) throws InvalidKeyException,
        InvalidAlgorithmParameterException, UnsupportedEncodingException,
        IllegalBlockSizeException, BadPaddingException {

    byte[] iv = new byte[IV_SIZE];
    byte[] dec = new byte[encryptedText.length - IV_SIZE];

    for (int i = 0; i < encryptedText.length; i++) {
        if (i < IV_SIZE)
            iv[i] = encryptedText[i];
        else if (i < encryptedText.length)
            dec[i - IV_SIZE] = encryptedText[i];
    }

    cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));

    return new String(cipher.doFinal(dec), CHARSET_NAME);
}

如您所见,每次加密消息时,我都会保存一个新的 IV 和密文。

总之:我为数据库表中的每个字段使用一个加密密钥、一个随机盐和一个新的 IV。

首先,每当我加密数据库表中的一个字段并将所需的盐和 IV 与密文一起保存时,或者至少为一个表行保存时,我想生成一个带有新盐和新 IV 的新密钥。但我之所以这样做,是因为在 Android 设备上生成密钥需要很长时间。我在模拟器上进行了测试,但生成密钥大约需要两秒钟。这就是为什么我在启动 Activity 时只生成一个密钥的原因。

所以最后我的问题是:用我的方法,只使用一个密钥,但每条消息都使用新的随机 IV 是否足够安全?目前,我没有看到另一种方法可以通过使其与性能保持平衡来使其尽可能安全。

我希望我写的内容足够清楚,有人可以给我一些建议。

亲切的问候

希德贝格

4

1 回答 1

1

I believe the question is not relevant for you (xoidberg), but it might be relevant for some other people.

From what I understand - you use the salt to create a (securely random) key from password. If every user has a random (different) salt - it is ok. Otherwise it might be problematic.

I believe that this is what you did, so it seems (to me) to be ok.

I just want to mention that usually you want to use salts when you save hash function of some values (usually password). Hash functions like MD5 or the SHAs do not have a key, and you must add randomness for this purpose. This is why you need the salt, and this is why in this case you usually need random salt for each value (if you just save passwords hashes with the same salt, one can detect the most common hashes and learn that the password of the users with the most common hash is 123456). In your case - every user needs a unique salt.

About the IV - you really need a random one each time (so it's ok).

于 2013-12-22T11:05:06.373 回答