我需要在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代码会生成一个文件,但它不可读。请检查我的代码是在做正确的代码还是包含任何问题。请帮我解决一下
答案 0 :(得分:1)
模式和填充不匹配。您在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。它不一定是秘密,但它必须是不可预测的。您可以将其与密文一起发送,例如将其放在密文前面。
通过在其上运行HMAC来验证您的密文(encrypt-then-MAC)。在尝试解密之前,您需要检查接收器端的MAC以查看它是否在途中被操纵。