Android AES 256 位加密数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10198462/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:50:33  来源:igfitidea点击:

Android AES 256-bit Encrypt data

androidencryptionaes

提问by joshkendrick

So I've seen a lot of examples, and done a lot of googling, and looked at examples on Stack Overflow... and I need help. I've got an Android application and I'm storing username and passwords on the device, and I need to encrypt them AES 256. From looking at examples, this is what I have so far:

所以我看过很多例子,做了很多谷歌搜索,并查看了 Stack Overflow 上的例子......我需要帮助。我有一个 Android 应用程序,我在设备上存储用户名和密码,我需要对它们进行 AES 256 加密。从查看示例来看,这是我到目前为止所拥有的:

public class Security {
    Cipher ecipher;
    Cipher dcipher;

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

    // Iteration count
    int iterationCount = 19;

    public Security (String passPhrase) {
        try {
            // Create the key
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance(
                "PBEWithSHAAndAES").generateSecret(keySpec);
            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());

            // Prepare the parameter to the ciphers
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            // Create the ciphers
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
        } catch (Exception e) { 
            e.printStackTrace(); 
        }
    }

    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 Base64.encodeToString(enc, Base64.DEFAULT);
        } catch (Exception e) { 
            e.printStackTrace();
            return null;
        }
    }

    public String decrypt(String str) {
        try {
            // Decode base64 to get bytes
            byte[] dec = Base64.decode(str, Base64.DEFAULT);

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

            // Decode using utf-8
            return new String(utf8, "UTF8");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

I'm trying to make it password based, so a user will create an account the first time using the username and password needed to communicate back to the server, and create a PIN that will be used as the key for these credentials stored in the database.

我正在尝试使其基于密码,因此用户将在第一次使用与服务器通信所需的用户名和密码创建一个帐户,并创建一个 PIN 用作存储在数据库。

What I'm mainly concerned about is does this look secure? I know a fixed salt is bad, how do I fix that?

我主要关心的是这看起来安全吗?我知道固定盐不好,我该如何解决?

I know there's been like a billion questions about this, but I want someone to just come out and say "THIS IS SECURE" or "THIS IS NOT SECURE, CHANGE THIS"

我知道关于这个问题有十亿个,但我希望有人站出来说“这是安全的”或“这不是安全的,改变这个”

Thanks!

谢谢!



EDIT:

编辑:

So this is the code I have so far, and it seems to be working...

所以这是我到目前为止的代码,它似乎正在工作......

public class Security {

    Cipher ecipher;
    Cipher dcipher;
    byte[] salt = new byte[8];
    int iterationCount = 200;

    public Security(String passPhrase) {
        try {
            // generate a random salt
            SecureRandom random = new SecureRandom();
            random.nextBytes(salt);

            // Create the key
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance(
                "PBEWithSHA256And256BitAES-CBC-BC").generateSecret(keySpec);
            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());

            // Prepare the parameter to the ciphers
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            // Create the ciphers
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    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 Base64.encodeToString(enc, Base64.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public String decrypt(String str) {
        try {
            // Decode base64 to get bytes
            byte[] dec = Base64.decode(str, Base64.DEFAULT);

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

            // Decode using utf-8
            return new String(utf8, "UTF8");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public int getIterationCount() {
        return iterationCount;
    }

    public String getSalt() {
        return Base64.encodeToString(salt, Base64.DEFAULT);
    }
}

I used this code to test it:

我用这段代码来测试它:

 Security s = new Security(pinBox.getText().toString());
            String encrypted = s.encrypt(passwordBox.getText().toString());
            String decrypted = s.decrypt(encrypted);
            builder.setMessage("pin: " + pinBox.getText().toString() + "\n" +
                    "password: " + passwordBox.getText().toString() + "\n" +
                    "encrypted: " + encrypted + "\n" +
                    "decrypted: " + decrypted + "\n" +
                    "salt: " + s.getSalt());

So I don't need to worry about an initialization vector? Or specifically hardcode a Cipher algorithm?

所以我不需要担心初始化向量?或者专门硬编码密码算法?

Thanks again!

再次感谢!

采纳答案by Nikolay Elenkov

EDIT: While the code below is correct, what you have is doing basically the same thing, with the IV derived from the password, so you don't have to store it separately.

编辑:虽然下面的代码是正确的,但你所做的基本上是一样的,IV 派生自密码,所以你不必单独存储它。

Does your code work as expected? For the actual encryption/decryption you would want to use AES, most probably in CBC mode. Then you would need an IV, so it becomes something like this:

您的代码是否按预期工作?对于实际的加密/解密,您可能希望使用 AES,很可能在 CBC 模式下。然后你需要一个 IV,所以它变成了这样:

ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] enc = ecipher.doFinal(utf8);

Whether it is secure depends on what you are using this for. The purpose of the salt is to make it harder to brute force the passphrase: if it's random the attacker cannot use pre-generated passphrase tables (passphrase->key). If you are not too worried about this sort of attack, you might leave it fixed. If you decide to make it random, just store it with the encrypted data. Same with the IV.

它是否安全取决于您使用它的目的。salt 的目的是使破解密码更难:如果它是随机的,攻击者就不能使用预先生成的密码表(密码->密钥)。如果您不太担心这种攻击,您可以将其固定。如果您决定使其随机,只需将其与加密数据一起存储。与IV相同。