16

实际上,我也从互联网和stackoverflow中搜索了很多内容,

最初我在加密和解密中没有使用填充,

但最后我从这里得到了解决方案

https://stackoverflow.com/a/10775577/1115788

我用填充更新了我的代码作为 AES/CBC/PKCS5Padding 并且同样的错误即将到来,最后一个块没有被解密......

我最近两天一直在做这个,但没有找到解决方案

我的加密器代码:

package mani.droid.browsedropbox;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypter {

    Cipher encipher;
    Cipher decipher;
CipherInputStream cis;
CipherOutputStream cos;
FileInputStream fis;
byte[] ivbytes = new byte[]{(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e',                                                                                          (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',     (byte)'n', (byte)'o', (byte)'p'};
    IvParameterSpec iv = new IvParameterSpec(ivbytes);

public boolean enCrypt(String key, InputStream is, OutputStream os)
{
    try {
        byte[] encoded = new BigInteger(key, 16).toByteArray();
        SecretKey seckey = new SecretKeySpec(encoded, "AES");
        encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        encipher.init(Cipher.ENCRYPT_MODE, seckey, iv);
        cis = new CipherInputStream(is, encipher);
        copyByte(cis, os);
        return true;
    } 
    catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return false;
}

public boolean deCrypt(String key, InputStream is, OutputStream os)
{
    try {
        byte[] encoded = new BigInteger(key, 16).toByteArray();
        SecretKey seckey = new SecretKeySpec(encoded, "AES");
        encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        encipher.init(Cipher.DECRYPT_MODE, seckey, iv);
        cos = new CipherOutputStream(os, encipher);
        copyByte(is, cos);
        //cos.close();
        return true;
    } 
    catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return false;
}

public void copyByte(InputStream is, OutputStream os) throws IOException
{
    byte[] buf = new byte[8192];
    int numbytes;
    while((numbytes = is.read(buf)) != -1)
    {
        os.write(buf, 0, numbytes);
        os.flush();
    }
    os.close();
    is.close();
}
}
4

6 回答 6

16

我遇到了完全相同的问题。接受的解决方案有效,因为您使用了不需要填充的密码模式,但这不是解决加密相关问题的方法。

根据 CipherOutputStream文档,您必须调用 close() 方法才能正确完成加密(即添加填充块)。

此方法调用封装密码对象的 doFinal 方法,这会导致处理封装密码缓冲的任何字节。通过调用此输出流的 flush 方法写出结果。

此方法将封装的密码对象重置为其初始状态并调用底层输出流的关闭方法。

如果您想在调用 CipherOutputStream.close() 方法后仍保持 OutputStream 处于打开状态,您可以将 OutputStream 包装到不关闭它的流中。例如:

public class NotClosingOutputStream extends OutputStream {
  private final OutputStream os;

  public NotClosingOutputStream(OutputStream os) {
    this.os = os;
  }

  @Override
  public void write(int b) throws IOException {
    os.write(b);
  }

  @Override
  public void close() throws IOException {
    // not closing the stream.
  }

  @Override
  public void flush() throws IOException {
    os.flush();
  }

  @Override
  public void write(byte[] buffer, int offset, int count) throws IOException {
    os.write(buffer, offset, count);
  }

  @Override
  public void write(byte[] buffer) throws IOException {
    os.write(buffer);
  }
}

然后你可以使用:

...
cos = new CipherOutputStream(new NotClosingOutputStream(os), encipher);
copyByte(is, cos);
cos.close();
...

请注意,os流不会关闭,您需要在适当的时候自行关闭。

于 2014-03-20T23:24:14.677 回答
7

最后我得到了我自己的问题的答案,经过反复试验实际上在这里冲突是我设置了填充encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

并将 IV 设置为一些值.....,

最后我得到了答案,只是替换了算法

从:

AES/CBC/PKCS7Padding

到:

AES/CFB8/无填充

并且它的工作就像魅力......,所以我建议其他在这个问题上苦苦挣扎的人的答案,如果你解决了你的问题,请在这里为其他人提及......

于 2012-10-10T11:20:12.213 回答
0

我也看到 CipherInputStream 因填充问题而失败。这种行为因 JVM 的不同版本而异。例如 7u55 32 位我的代码运行良好,7u55 64 位相同的代码失败了......而且我还看到后来的 32 位 JVM 出现故障。解决方法是使用字节数组方法并避免使用 CipherInputStream。

于 2014-10-29T06:53:28.423 回答
0

不确定这是否与 OP 的问题有关,但这可能会对某人有所帮助。

无论您更改什么,当您反复得到java.io.IOException: last block incomplete in decryption 该文件时,请检查您是否仍在使用之前运行的文件。如果您的读/写测试代码附加到该文件,您将始终得到该异常——除非您删除了您写入的损坏文件。

于 2015-03-05T14:04:30.410 回答
0

可以使用带有填充的 CipherInputStream,切换到 NoPadding 是一种解决方法,但不是解决方案。

当 CipherInputStream 到达流的末尾时应用填充。重要的一点是您必须调用 CipherInputStream 的 read() 方法至少两次才能获取所有数据。

以下示例演示了读取带有填充的 CipherInputStream:

public static void test() throws Exception {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    SecureRandom rnd = new SecureRandom();
    byte[] keyData = new byte[16];
    byte[] iv = new byte[16];
    rnd.nextBytes(keyData);
    rnd.nextBytes(iv);
    SecretKeySpec key = new SecretKeySpec(keyData, "AES");

    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    CipherOutputStream out = new CipherOutputStream(buffer, cipher);

    byte[] plain = "Test1234567890_ABCDEFG".getBytes();
    out.write(plain);
    out.flush();
    out.close();
    byte[] encrypted = buffer.toByteArray();
    System.out.println("Plaintext length: " + plain.length);
    System.out.println("Padding length  : " + (cipher.getBlockSize() - (plain.length % cipher.getBlockSize())));
    System.out.println("Cipher length   : " + encrypted.length);

    cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
    CipherInputStream in = new CipherInputStream(new ByteArrayInputStream(encrypted), cipher);
    buffer = new ByteArrayOutputStream();
    byte[] b = new byte[100];
    int read;
    while ((read = in.read(b)) >= 0) {
        buffer.write(b, 0, read);
    }
    in.close();

    // prints Test1234567890_ABCDEFG
    System.out.println(new String(buffer.toByteArray()));
}
于 2015-12-01T21:00:29.957 回答
0

对于那些正在努力使用图像文件进行 aes 加密/解密的人,这是我的示例,它就像一个魅力。

public static String decrypt(String textToDecrypt)  {
    byte[] dataDecrypted = null;

    try {
        Cipher cipher = getCipher();
        SecretKey key = getKey();
        IvParameterSpec iv = getIV();
        if(cipher == null) {
            return null;
        }
        cipher.init(Cipher.DECRYPT_MODE, key, iv);

        byte[] dataToDecrypt = Base64.decode(textToDecrypt, Base64.NO_WRAP);
        ByteArrayInputStream bais = new ByteArrayInputStream(dataToDecrypt);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // Wrap the output stream
        CipherOutputStream cos = new CipherOutputStream(baos, cipher);

        // Read bytes
        int count = 0;
        byte[] buffer = new byte[DEFAULT_BYTE_READ_WRITE_BLOCK_BUFFER_SIZE];
        while ((count = bais.read(buffer)) != -1) {
            cos.write(buffer, 0, count);
        }
        cos.close();    // manually do close for the last bit

        dataDecrypted = baos.toByteArray();

        // Close streams.
        baos.close();
        bais.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return (dataDecrypted == null ? "" : Base64.encodeToString(dataDecrypted, Base64.NO_WRAP));
}

public static String encrypt(String textToEncrypt) {
    byte[] dataEncrypted = null;

    try {
        Cipher cipher = getCipher();
        SecretKey key = getKey();
        IvParameterSpec iv = getIV();
        if (cipher == null) {
            return null;
        }
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);

        byte[] dataToEncrypt = Base64.decode(textToEncrypt, Base64.NO_WRAP);
        ByteArrayInputStream bais = new ByteArrayInputStream(dataToEncrypt);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // Wrap the output stream
        CipherOutputStream cos = new CipherOutputStream(baos, cipher);

        // Read bytes
        int count = 0;
        byte[] buffer = new byte[DEFAULT_BYTE_READ_WRITE_BLOCK_BUFFER_SIZE];
        while ((count = bais.read(buffer)) != -1) {
            cos.write(buffer, 0, count);
        }
        cos.close();    // manually do close for the last bit

        dataEncrypted = baos.toByteArray();

        // Close streams.
        baos.close();
        bais.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return (dataEncrypted == null ? "" : Base64.encodeToString(dataEncrypted, Base64.NO_WRAP));
}
于 2020-12-08T08:25:54.900 回答