EVP_DecryptFinal_ex:解密不良,可能是Android中的密钥生成问题

时间:2016-04-17 13:36:33

标签: android encryption passwords encryption-symmetric

因此我遇到了从基于字符串的密码生成密钥的问题。加密步骤正常,解密步骤一直有效,直到底部列出的错误,使解密的文件损坏。我使用以下函数来执行加密/解密:

public static boolean decryptFileFromUri(Context context, Uri file, String keyphrase) {
    try {
        File f = new File(getRealPathFromURI(context, file));
        FileInputStream fis = new FileInputStream(f);

        File ef = new File(f.toString().replace(".epf", ""));
        FileOutputStream fos = new FileOutputStream(ef);

        Log.d("HIDEMYPICS","Decrypting: " + f.toString());

        byte[] rawKey = getRawKey(keyphrase.getBytes("UTF8"));
        /*KeyGenerator kgen = KeyGenerator.getInstance("AES");
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        sr.setSeed(rawKey);
        kgen.init(128, sr); // 192 and 256 bits may not be available
        SecretKey skey = kgen.generateKey();
        byte[] key = skey.getEncoded();*/
        SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        // Wrap the output stream
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);
        // Write bytes
        int b;
        byte[] d = new byte[8];
        while ((b = fis.read(d)) != -1) {
            cos.write(d, 0, b);
        }
        // Flush and close streams.
        cos.flush();
        cos.close();
        fis.close();

        Log.d("HIDEMYPICS","Decrypted to: " + ef.toString());
        return true;
    } catch (IOException e){
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }
    return false;
}

public static boolean encryptFileFromUri(Context context, Uri file, String keyphrase) {
    try {
        File f = new File(getRealPathFromURI(context, file));
        FileInputStream fis = new FileInputStream(f);

        File ef = new File(f.toString() + ".epf");
        FileOutputStream fos = new FileOutputStream(ef);

        Log.d("HIDEMYPICS","Encrypting: " + f.toString());

        byte[] rawKey = getRawKey(keyphrase.getBytes("UTF8"));
        /*KeyGenerator kgen = KeyGenerator.getInstance("AES");
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        sr.setSeed(rawKey);
        kgen.init(128, sr); // 192 and 256 bits may not be available
        SecretKey skey = kgen.generateKey();
        byte[] key = skey.getEncoded();*/
        SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        // Wrap the output stream
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);
        // Write bytes
        int b;
        byte[] d = new byte[8];
        while ((b = fis.read(d)) != -1) {
            cos.write(d, 0, b);
        }
        // Flush and close streams.
        cos.flush();
        cos.close();
        fis.close();
        Log.d("HIDEMYPICS","Encrypted to: " + ef.toString());
        return true;
    } catch (IOException e){
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }
    return false;
}

这里是生成原始密钥的函数:

private static byte[] getRawKey(byte[] seed) throws NoSuchAlgorithmException {
    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();

    String result = "";
    for(int index = 0; index < raw.length; index++) {
        result += Integer.toHexString(raw[index]);
        // maybe you have to convert your byte to int before this can be done
        // (cannot check reight now)
    }

    Log.d("HIDEMYPICS","Passphrase: " + new String(seed).toString() + " Key: " + result );
    return raw;
}

&#34;测试&#34;的输出对于密钥的上述十六进制转储,输入字符串如下:

加密:

    04-17 09:01:25.088 18341-18341/com.dcheeseman.hidemypics D/HIDEMYPICS: Encrypting: /storage/emulated/0/Download/bailout_5128280_GIFSoup.com-1.gif
04-17 09:01:25.088 18341-18341/com.dcheeseman.hidemypics D/HIDEMYPICS: Passphrase: test Key: ffffff85affffffe21023ffffffb7ffffffe8ffffffc8214031fffffffa5b29ffffff9affffff80

解密:

04-17 09:01:43.808 18341-18341/com.dcheeseman.hidemypics D/HIDEMYPICS: Decrypting: /storage/emulated/0/Download/bailout_5128280_GIFSoup.com-1.gif.epf
04-17 09:01:43.808 18341-18341/com.dcheeseman.hidemypics D/HIDEMYPICS: Passphrase: test Key: ffffff8331ffffffe2ffffff87ffffffe242dffffffa61cffffffc7ffffffb4ffffffa1d74ffffff9affffff9b

解密和加密都用&#34; test&#34;作为密码而且该函数返回2个不同的密钥,这就是为什么我认为我得到了解密错误,其完整跟踪列在下面:

    04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err: java.io.IOException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at javax.crypto.CipherOutputStream.close(CipherOutputStream.java:136)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at com.dcheeseman.hidemypics.AESUtils.decryptFileFromUri(AESUtils.java:102)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at com.dcheeseman.hidemypics.HideMyPics.onActivityResult(HideMyPics.java:35)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.app.Activity.dispatchActivityResult(Activity.java:6808)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.app.ActivityThread.deliverResults(ActivityThread.java:4698)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.app.ActivityThread.handleSendResult(ActivityThread.java:4745)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.app.ActivityThread.access$1500(ActivityThread.java:197)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1730)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.os.Looper.loop(Looper.java:145)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6872)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
04-17 09:33:24.158 823-823/com.dcheeseman.hidemypics W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)

提前感谢您对此问题的任何帮助!

2 个答案:

答案 0 :(得分:0)

您声明:&#34; 2个不同的键&#34;,键必须相同。

通常,您预先共享密钥或密码,如果您使用密码,则使用PBKDF2等功能从中获取密钥。如果使用密码派生,则必须在加密和解密时使用相同的确定性功能。使用的功能必须从每侧的密码生成相同的密钥。由于您使用的是随机生成器,因此派生不是确定性的,这不起作用。

答案 1 :(得分:0)

接受Zaph的建议并交换到PBKDF2,现在有一致的密钥生成。这是任何有趣的人的代码。

public static SecretKey generateKey(Context c, char[] passphraseOrPin) throws NoSuchAlgorithmException, InvalidKeySpecException {
    // Number of PBKDF2 hardening rounds to use. Larger values increase
    // computation time. You should select a value that causes computation
    // to take >100ms.
    byte[] salt = Settings.Secure.getString(c.getContentResolver(),
            Settings.Secure.ANDROID_ID).getBytes();

    final int iterations = 1000;

    final int outputKeyLength = 128;

    SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
    SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
    Log.d("HIDEMYPICS","Secret Key: " + toHex(secretKey.getEncoded()) );
    return secretKey;
}

public static boolean decryptFileFromUri(Context context, Uri file, String keyphrase) {
    try {
        File f = new File(getRealPathFromURI(context, file));
        FileInputStream fis = new FileInputStream(f);

        File ef = new File(f.toString().replace(".epf", ""));
        FileOutputStream fos = new FileOutputStream(ef);

        Log.d("HIDEMYPICS","Decrypting: " + f.toString());

        SecretKey key = generateKey(context, keyphrase.toCharArray());
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        // Wrap the output stream
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);
        // Write bytes
        int b;
        byte[] d = new byte[8];
        while ((b = fis.read(d)) != -1) {
            cos.write(d, 0, b);
        }
        // Flush and close streams.
        cos.flush();
        cos.close();
        fis.close();

        Log.d("HIDEMYPICS","Decrypted to: " + ef.toString());
        return true;
    } catch (IOException e){
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    }
    return false;
}

public static boolean encryptFileFromUri(Context context, Uri file, String keyphrase) {
    try {
        File f = new File(getRealPathFromURI(context, file));
        FileInputStream fis = new FileInputStream(f);

        File ef = new File(f.toString() + ".epf");
        FileOutputStream fos = new FileOutputStream(ef);

        Log.d("HIDEMYPICS","Encrypting: " + f.toString());

        SecretKey key = generateKey(context, keyphrase.toCharArray());
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // Wrap the output stream
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);
        // Write bytes
        int b;
        byte[] d = new byte[8];
        while ((b = fis.read(d)) != -1) {
            cos.write(d, 0, b);
        }
        // Flush and close streams.
        cos.flush();
        cos.close();
        fis.close();
        Log.d("HIDEMYPICS","Encrypted to: " + ef.toString());
        return true;
    } catch (IOException e){
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    }
    return false;
}

注意:它仍然无法正常工作,因为它在解密过程中损坏了前几个字节,从而导致文件头损坏。如果您想查看结果,我已在此处开始单独提问:CipherOutputStream corrupting headers in Android