php - 在 PHP 中使用 AES-128 加密文件并在 Android 中解密

标签 php android encryption aes

我需要在 PHP 中使用 AES-128 加密文件并在 Android 中解密。

我正在使用以下代码。我已使用 PHP 代码成功加密和解密它,但我需要使用我的应用程序中的 Android 对其进行解密。

PHP代码:

$key= "asdfghjklzxccvbn";   
$in_filename = $_FILES["fileToUpload"]["tmp_name"];
$aes_filename =$target_dir."encry_".$_FILES["fileToUpload"]["name"];
$decry_filename =$target_dir."decry_".$_FILES["fileToUpload"]["name"];

//encrypt file
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = '1234567890123456';

$fin = fopen($in_filename, "rb");
$fcrypt = fopen($aes_filename, 'wb');
fwrite($fcrypt, $iv);
$opts = array('iv'=>$iv, 'key'=>$key, 'mode'=>'cbc');
stream_filter_append($fcrypt, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE,     $opts);
while (!feof($fin))
{
    fwrite($fcrypt, fread($fin, 8192));
}
fclose($fcrypt);
fclose($fin);

我用于解密加密文件的 Android 代码:

 // encripted file stored in android device for decrypt
 String uri= Environment.getExternalStorageDirectory().toString();
 uri=uri+"/encry_file.mp4";
 File file = new File(uri.toString());
 FileInputStream fis = new FileInputStream(file);
 spec =getIV();

 FileOutputStream fos = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/decrypted.mp4");
 SecretKeySpec sks = new SecretKeySpec("asdfghjklzxccvbn".getBytes(),
          "AES");
 Cipher cipher = Cipher.getInstance("AES");
 cipher.init(Cipher.DECRYPT_MODE, sks, spec);
 CipherInputStream cis = new CipherInputStream(fis, cipher);

 int b;
 byte[] d = new byte[8192];
 while ((b = cis.read(d)) != -1) {
    fos.write(d, 0, b);
 }
 fos.flush();
 fos.close();
 cis.close();

获取IV函数

public AlgorithmParameterSpec getIV() {
    byte[] iv = { 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6 };
    IvParameterSpec ivParameterSpec;
    ivParameterSpec = new IvParameterSpec(iv);

    return ivParameterSpec;
}

android 代码将生成一个文件,但它不可读。请检查我的代码是否在执行正确的代码,或者它是否包含任何问题。请帮我解决一下

最佳答案

模式和填充不匹配。您在 PHP 中使用 AES/CBC/ZeroPadding(Java 表示法),但在 Java 中您使用的是 Cipher.getInstance("AES") (可能)默认为 Cipher.getInstance (“AES/ECB/PKCS5Padding”)。始终使用完全限定的密码描述:

Cipher cipher = Cipher.getInstance("AES/CBC/ZeroPadding", "BC");

(这并不能解决问题。)

您没有使用相同的 IV。字符'1'和字节1不是一回事,因为一个'1'实际上是字节49。

byte[] iv = { 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54 };

由于 BouncyCaSTLes/SpongyCaSTLes ZeroPadding 与 mcrypt 的零填充不完全相同,您应该使用 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 为了解析解密的最后 16 个字节并删除尾随的 0x00 字节。

在您的情况下,这是一种执行此操作的方法:

int b;
byte[] d = new byte[8192];
byte[] p = null;
int holdOff;
while ((b = cis.read(d)) != -1) {
    holdOff = Math.max(b - cipher.getBlockSize(), 0);
    if (p != null) {
        fos.write(p, 0, p.length);
        Arrays.fill(p, 0);
    }
    if (p == null) {
        p = new byte[cipher.getBlockSize()];
    }
    System.arraycopy(d, holdOff, p, 0, p.length);

    fos.write(d, 0, holdOff);
}

// here p contains the end of the plaintext followed by padding bytes
// remove padding:
int i = cipher.getBlockSize() - 1;
while(i >= 0 && p[i] == 0) {
    i--;
}
// write remaining bytes
fos.write(Arrays.copyOf(p, i+1), 0, i+1);

fos.flush();
fos.close();
cis.close();

这个想法是你推迟将最后 16 个字节写入文件并单独处理它们,因为解密文件的最后 16 个字节可能包含你需要删除的 0x00 字节。


其他注意事项:

  • 始终为每次加密随机生成 IV。它不必是 secret 的,但它必须是不可预测的。您可以将它与密文一起发送,例如将它放在密文前面。

  • 通过在其上运行 HMAC(加密然后 MAC)来验证您的密文。在您尝试解密之前,您需要检查接收方的 MAC 以查看它是否在途中被操纵。

关于php - 在 PHP 中使用 AES-128 加密文件并在 Android 中解密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33082139/

相关文章:

python - 如何使用 PyPDF2 解密 PDF?

php - Symfony 2 服务容器为空

php - 如何将 NULL 作为变量从 PHP 发送到 SQL?

android - 在测试期间显示触摸

java - 尝试在空对象引用上调用虚拟方法 'void com.facebook.login.widget.LoginButton.setReadPermissions(java.lang.String[])'

mysql - 解密 mysql 数据库中的某些字段

php - 在 php7 中发布 ajax 数据的问题

php - Codeigniter $this->zip->download() 不起作用

c# - 我是否需要取消订阅 Click 事件以防止内存泄漏?

Android Studio - 生成发布 apk "Error finalising cipher"