当前位置: 首页 > 知识库问答 >
问题:

在Android中使用AES方法解密文件

孟和玉
2023-03-14

我在php中用AES加密法加密了文件,代码如下。

$ALGORITHM = 'AES-128-CBC';
$IV = '12dasdq3g5b2434b';
$password = '123';
openssl_encrypt($contenuto, $ALGORITHM, $password, 0, $IV);

现在我正在尝试在Android中解密它,但我总是面临InvalidKey异常:密钥长度不是128/192/256位错误。这是Android代码:

String initializationVector = "12dasdq3g5b2434b";
String password = "123";
FileInputStream fis = new FileInputStream(cryptFilepath);
FileOutputStream fos = new FileOutputStream(outputFilePath);
byte[] key = (password).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key,16);
SecretKeySpec sks = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, sks, new IvParameterSpec(initializationVector.getBytes()));
CipherInputStream cis = new CipherInputStream(fis, cipher);
int b;
byte[] d = new byte[16];
while((b = cis.read(d)) != -1) {
fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();

有人能建议我怎么做吗?任何帮助都将不胜感激。

共有3个答案

双元魁
2023-03-14

在迈克尔的回答的帮助下,我写下了成功解密文件的代码。

在应用级别build.gradle文件中添加以下依赖项:

implementation 'commons-io:commons-io:2.7'
implementation 'commons-codec:commons-codec:1.13'

Java代码:

public static void decrypt(String path, String outPath) throws Exception {
    String password = "123";
    String initializationVector = "12dasdq3g5b2434b";
    byte[] key = new byte[16]; // 16 bytes for aes cbc 128
    System.arraycopy(password.getBytes(StandardCharsets.UTF_8), 0, key, 0, password.getBytes(StandardCharsets.UTF_8).length);
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVector.getBytes(StandardCharsets.UTF_8));

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);

    byte[] input_file;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        input_file = Files.readAllBytes(Paths.get(path));
    } else {
        input_file = org.apache.commons.io.FileUtils.readFileToByteArray(new File(path));
    }

    byte[] decodedBytes;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        decodedBytes = Base64.getDecoder().decode(input_file);
    } else {
        decodedBytes = org.apache.commons.codec.binary.Base64.decodeBase64(input_file);
    }

    byte[] decryptedtext = cipher.doFinal(decodedBytes);
    FileOutputStream fos = new FileOutputStream(outPath);
    fos.write(decryptedtext);
    fos.flush();
    fos.close();
}

我希望这会有所帮助。

令狐宏浚
2023-03-14

以下完整的工作示例展示了如何处理密码问题和进行Base64解码,这些示例只处理字符串而不是文件。请记住@Topaco所说的openssl以Base64编码输出,需要转换为字节格式,然后文件才能与CipherInputStream一起用于解密!

第三点(不是真正的错误)是,在Java/Android端,您不需要为字符串到字节数组的转换设置字符集,只需添加StandardCharset即可。UTF_8和你对这一点很满意。

当心没有适当的异常处理!

这是我的PHP代码示例:

<?php
// https://stackoverflow.com/questions/63113746/decrypt-file-using-aes-method-in-android
$ALGORITHM = 'AES-128-CBC';
$IV = '12dasdq3g5b2434b';
$password = '123';
$plaintext = "my content to encrypt";
echo 'plaintext: ' . $plaintext . PHP_EOL;
$ciphertext = openssl_encrypt($plaintext, $ALGORITHM, $password, 0, $IV);
echo 'ciphertext: ' . $ciphertext . PHP_EOL;
$decryptedtext = openssl_decrypt($ciphertext, $ALGORITHM, $password, 0, $IV);
echo 'decryptedtext: ' . $decryptedtext . PHP_EOL;
?>

PHP端的输出:

plaintext: my content to encrypt
ciphertext: DElx3eON2WX0MCj2GS8MnD+kn5NOu1i5IOTcrpKegG4=
decryptedtext: my content to encrypt

样本Java代码:

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class DecryptInJava {
    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        System.out.println("https://stackoverflow.com/questions/63113746/decrypt-file-using-aes-method-in-android");
        String password = "123";
        String initializationVector = "12dasdq3g5b2434b";
        String ciphertext = "DElx3eON2WX0MCj2GS8MnD+kn5NOu1i5IOTcrpKegG4="; // password 123 in openssl
        // openssl encodes the output in base64 encoding, so first we have to decode it
        byte[] ciphertextByte = Base64.getDecoder().decode(ciphertext);
        // creating a key filled with 16 'x0'
        byte[] key = new byte[16]; // 16 bytes for aes cbc 128
        // copying the password to the key, leaving the x0 at the end
        System.arraycopy(password.getBytes(StandardCharsets.UTF_8), 0, key, 0, password.getBytes(StandardCharsets.UTF_8).length);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVector.getBytes(StandardCharsets.UTF_8));
        // don't use just AES because that defaults to AES/ECB...
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] decryptedtext = cipher.doFinal(ciphertextByte);
        System.out.println("decryptedtext: " + new String(decryptedtext));
    }
}

Java端的输出:

decryptedtext: my content to encrypt
江英卓
2023-03-14

问题中发布的原始代码使用流来读取、解密和写入相应的文件。这样就可以处理大于可用内存的文件

然而,最初发布的代码缺少Base64解码,这是必要的,因为PHP代码的密文是Base64编码的。

使用Apache Commons编解码器的< code>Base64InputStream类可以轻松实现Base64解码,该类在< code>FileInputStream和< code>CipherInputStream之间运行,因此易于集成:

import org.apache.commons.codec.binary.Base64InputStream;

...

public static void decrypt(String ciphertextFilepath, String decryptedFilePath) throws Exception {
    
    String password = "123";
    String initializationVector = "12dasdq3g5b2434b";

    byte[] key = new byte[16];
    byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
    System.arraycopy(passwordBytes, 0, key, 0, passwordBytes.length);
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVector.getBytes(StandardCharsets.UTF_8));
    
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
    
    try (FileInputStream fis = new FileInputStream(ciphertextFilepath);
         Base64InputStream b64is = new Base64InputStream(fis);
         CipherInputStream cis = new CipherInputStream(b64is, cipher);
         FileOutputStream fos = new FileOutputStream(decryptedFilePath)) {
            
        int read;
        byte[] buffer = new byte[16]; // 16 bytes for testing, in practice use a suitable size (depending on your RAM size), e.g. 64 Mi
        while((read = cis.read(buffer)) != -1) {
            fos.write(buffer, 0, read);
        }
    }
}

其他修复的错误/优化点是(另请参阅其他答案/评论):

  • 使用类似于PHP代码的CBC模式
  • 使用PHP代码中的密钥
  • 使用的编码的显式规范

编辑:考虑IV,参见@Michael Fehr的评论。

通常为每次加密生成新的随机IV。IV不是秘密的,通常放在密文之前,结果是Base64编码。接受者可以将两部分分开,因为静脉注射的大小是已知的(对应于块大小)。此构造还可以与Base64InputStream类结合使用,其中IV必须在Base64InputStream实例化和

...
try (FileInputStream fis = new FileInputStream(ciphertextFilepath);
     Base64InputStream b64is = new Base64InputStream(fis)){
        
    byte[] iv = b64is.readNBytes(16); // 16 bytes for AES
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
    
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            
    try (CipherInputStream cis = new CipherInputStream(b64is, cipher);
         FileOutputStream fos = new FileOutputStream(decryptedFilePath)) {
        ...

如果在加密过程中IV和密文分别进行Base64编码,然后连接,用分隔符分隔(参见@Michael Fehr的评论),则IV的确定必须在FileInpuStreamBase64InpuStream实例化之间完成(分隔符也必须刷新)。

 类似资料:
  • 但这总是给我以下的例外- 我的键盘生成逻辑- 我的加密逻辑- Base64 Util方法-

  • 问题内容: 因此,我正在为自己开发一个个人项目,并且正在尝试加密手机上的文件。这些文件可以是任何文件,例如文档,照片等。现在,我正在尝试使其正常运行。每当我运行加密时,它似乎都可以正常工作并加密文件。当我运行解密时,有时它可以工作,而其他时候则不起作用。当它失败时,我通常会收到“在确定密码时出错,填充块已损坏”错误。我也没有使用不同的测试文件,所以它不像某些文件可以工作,而其他文件则不能。我每次都

  • 我试图在Android和PHP端使用AES加密/解密数据,并累犯空答案。 首先,我在Android中生成了对称密钥: 在服务器端,我试图解密数据。我可以解密(从RSA)秘密的AES密钥,并得到它的字符串表示。在客户端(Android)和服务器端(PHP)上是一样的。但是如何使用这个字符串AES密钥来解密数据呢?我尝试了这个(PHP): PHP中的结果: 怎么啦?

  • 我正在Android中进行AES加密和解密,我使用下面的Android代码片段发布请求。 申请职位 我成功地从上述请求中获得了Base64加密响应字符串,但当我尝试使用以下代码段解密响应字符串时,它会返回无法读取的字符串,如字符和方框。 解密 解密的输出 ��]ة*�]��O Z Q2_ 响应应为JSON格式,但实际输出如上所述。 请使用Base 64共享用于使用AES 256位安全密钥解密数据的

  • 问题内容: 有没有一个很好的示例,说明如何在Android上使用AES 加密和解密图像及其他文件? 问题答案: 并像这样调用它们: 这应该可行,我现在在项目中使用类似的代码。