javax.crypto 在不同版本的 Android 操作系统中的工作方式不同?

标签 java android encryption javax.crypto

我正在使用此代码 fragment 来加密/解密我应用数据库中的数据:

http://www.androidsnippets.com/encryptdecrypt-strings

看来 javax.crypto.KeyGenerator.generateKey() 操作在 Android 2.3.3 操作系统中的工作方式与其他(以前的?)版本不同。自然地,当我的用户将他们的设备从 2.2 升级到 2.3.3 并且应用程序开始抛出解密数据库的错误时,这对我的用户来说是一个主要问题。

这是一个已知问题吗?我是否错误地使用了加密库?有人对如何解决这个问题有任何建议,以便能够在 2.3.3 中解密在 2.2 中加密的数据吗?

我构建了一个通过加密函数提供值的测试应用程序。当我在 2.2 AVD 上运行它时,我得到一个结果。当我在 2.3.3 AVD 上运行它时,我得到了不同的结果。

    import java.security.SecureRandom;

    import javax.crypto.Cipher;
    import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;

    public class main extends Activity {
        TextView tvOutput;
        static String out;
        String TEST_STRING = "abcdefghijklmnopqrstuvwxyz";
        String PASSKEY = "ThePasswordIsPassord";

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            tvOutput = (TextView) findViewById(R.id.tvOutput);
        }

        @Override
        public void onResume() {
            super.onResume();
            out = "";
            runTest();
            tvOutput.setText(out);
        }

        private void runTest() {
            out = "Test string: " + TEST_STRING + "\n";
            out += "Passkey: " + PASSKEY + "\n";
            try {
                out += "Encrypted: " + encrypt(PASSKEY, TEST_STRING) + "\n";
            } catch (Exception e) {
                out += "Error: " + e.getMessage() + "\n";
                e.printStackTrace();
            }

        }

        public static String encrypt(String seed, String cleartext)
        throws Exception {
            byte[] rawKey = getRawKey(seed.getBytes());
            byte[] result = encrypt(rawKey, cleartext.getBytes());
            return toHex(result) + "\n" + "Raw Key: " + String.valueOf(rawKey)
                    + "\n";
        }

        private static byte[] getRawKey(byte[] seed) throws Exception {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
            sr.setSeed(seed);
            kgen.init(128, sr); // 192 and 256 bits may not be available
            SecretKey skey = kgen.generateKey();
            byte[] raw = skey.getEncoded();
            return raw;
        }

        private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
                byte[] encrypted = cipher.doFinal(clear);
            return encrypted;
        }

        public static String toHex(String txt) {
            return toHex(txt.getBytes());
        }

        public static String fromHex(String hex) {
            return new String(toByte(hex));
        }

        public static byte[] toByte(String hexString) {
            int len = hexString.length() / 2;
            byte[] result = new byte[len];
            for (int i = 0; i < len; i++)
                result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
                        16).byteValue();
            return result;
        }

        public static String toHex(byte[] buf) {
            if (buf == null)
                return "";
            StringBuffer result = new StringBuffer(2 * buf.length);
            for (int i = 0; i < buf.length; i++) {
                appendHex(result, buf[i]);
            }
            return result.toString();
        }

         private final static String HEX = "0123456789ABCDEF";

        private static void appendHex(StringBuffer sb, byte b) {
            sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
        }
    }

我的 main.xml 布局如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <TextView android:layout_width="fill_parent"
            android:layout_height="wrap_content" android:id="@+id/tvOutput" />
    </LinearLayout>

由于我是新用户,我无法发布链接或图片,但是如果您想查看结果,可以解密以下两张图片的网址:

我从 2.2 中得到了什么:

wct.vg/wt/droid/2.2.png

..从 2.3.3 开始:

wct.vg/wt/droid/2.3.3.png

最佳答案

您滥用了伪随机数生成器并将其作为 key 派生函数的种子 - 这确实是一种非常糟糕的风格。伪随机数生成器“SHA1PRNG”不是像 AES 这样的标准 - 因此您永远不知道您得到的是什么实现。 另见 Is there a SHA1PRNG standard

这让我难怪你会得到不同的结果。根据给定的种子获得确定性结果不是您可以从伪随机数函数中获得的属性。

如果您想从密码中导出加密 key ,请使用 Key Derivation Function像 PKCS #5/PBKDF2。 PBKDF2 的一个实现是包含在 Bouncy CaSTLe 中的 AFAIR。

关于javax.crypto 在不同版本的 Android 操作系统中的工作方式不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6316225/

相关文章:

java - Int 字段的空值

android - 阿克拉后脚本

c# - 使用密码 C# 保护 pdf 文件

java - Tomcat : 404 The requested resource is not available

Java正则表达式匹配所有字符,除了

java - 在 ConcurrentHashMap.computeIfAbsent 和 ConcurrentHashMap.computeIfPresent 中执行 `mappingFunction`

安卓ViewPager : Child Button prevents scroll

java - 有没有办法在 Java Android 应用程序或 Obj-C iOS 应用程序中使用 Xamarin 代码?

javascript - 匹配在 Ruby on Rails 和 JavaScript 中加密的签名 header

javascript - 解密由 crypto.pbkdf2 对象创建的密码