安卓AES 256位加密数据

11

我看了很多例子,通过谷歌搜索和在Stack Overflow上浏览了很多示例...但是我需要帮助。 我有一个安卓应用程序,我正在将用户名和密码存储在设备上,并且我需要使用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;
        }
    }
}

我正在尝试基于密码来实现认证,这样用户在首次使用用户名和密码与服务器进行通信时会创建一个账户,并创建一个 PIN 作为存储在数据库中的凭据的密钥。

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

我知道已经有很多关于此问题的提问,但我希望有人可以明确地说出 "这是安全的" 或者 "这不是安全的,需要修改"。

谢谢!


编辑:

这是我目前所拥有的代码,并且它似乎正在运行......

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

我使用了以下代码进行测试:

 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());

所以我不需要担心初始化向量吗?或者特别硬编码加密算法?

再次感谢!


谢谢你的帮助,伙计.. :) - Noman
你能帮我解决这个问题吗?http://stackoverflow.com/questions/34061675/convert-ios-encryption-to-android :( - MetaSnarf
1个回答

8

编辑:以下代码是正确的,但您所拥有的代码基本上是从密码派生的IV,因此您不必单独存储它。

您的代码是否按预期工作?对于实际加密/解密,您需要使用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);

这取决于你使用它的安全性。盐值的目的是使暴力破解密码更加困难:如果它是随机的,攻击者就不能使用预先生成的密码表(密码->密钥)。如果你不太担心这种攻击,可以将其保持不变。如果你决定让它随机,只需将其与加密数据一起存储。初始化向量也是如此。


我假设在存储加密的用户名和密码之前,我会对盐值(salt)和初始化向量(iv)进行加密。那么,我应该如何解密呢?因为我没有盐值和初始化向量来设置解密器,所以可能我理解有误? - joshkendrick
我应该尝试生成一个随机盐,以增加安全性,对吗? - joshkendrick
请注意,您通常可以将块大小用作iv长度,Cipher.getBlockSize()即可达到效果... - Maarten Bodewes
@Josh 是的,你需要盐,并且最好使它随机。顺便说一下,你可以通过使用加密算法 AlgorithmParameters 来轻松保存盐和迭代次数:AlgorithmParameters algParams = ecipher.getParameters(); byte[] encodedAlgParams = algParams.getEncoded(); - Nikolay Elenkov
以下是关于使用哪些算法(以及不要使用哪些算法)的讨论,附有示例代码:http://nelenkov.blogspot.jp/2012/04/using-password-based-encryption-on.html - Nikolay Elenkov
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接