使用AES进行文件加密解密

时间:2020-07-01 15:01:01

标签: java encryption aes

public long copyStreamsLong(InputStream in, OutputStream out, long sizeLimit) throws IOException {

        long byteCount = 0;
        IOException error = null;
        long totalBytesRead = 0;
        try {
            String key = "C4F9EA21977047D6"; // user value (16/24/32 bytes)
            // byte[] keyBytes = DatatypeConverter.parseHexBinary(aesKey);
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
            System.out.println(secretKey.toString());
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] buffer = new byte[CustomLimitedStreamCopier.BYTE_BUFFER_SIZE];
            // in.read(buffer);
            int bytesRead = -1;
            while ((bytesRead = in.read(buffer)) != -1) {
                // We are able to abort the copy immediately upon limit violation.
                totalBytesRead += bytesRead;
                if (sizeLimit > 0 && totalBytesRead > sizeLimit) {
                    StringBuilder msg = new StringBuilder();
                    msg.append("Content size violation, limit = ").append(sizeLimit);
                    throw new ContentLimitViolationException(msg.toString());
                }
                byte[] obuf = cipher.update(buffer, 0, bytesRead);
                if (obuf != null) {
                    out.write(obuf);
                }
                    byteCount += bytesRead;
            }
            byte[] obuf = cipher.doFinal();
            if (obuf != null) {
                out.write(obuf);
            }
                    out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                error = e;
                CustomLimitedStreamCopier.logger.error("Failed to close input stream: " + this, e);
            }
            try {
                out.close();
            } catch (IOException e) {
                error = e;
                CustomLimitedStreamCopier.logger.error("Failed to close output stream: " + this, e);
            }
        }
        if (error != null)
            throw error;
        return byteCount;
    }
     public InputStream getContentInputStream() throws ContentIOException {
        ReadableByteChannel channel = getReadableChannel();
        InputStream is = Channels.newInputStream(channel);
        try {    
            final String ALGORITHM = "AES";
            final String TRANSFORMATION = "AES";
            String key = "C4F9EA21977047D6";
            Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] buffer = ByteStreams.toByteArray(is);
            System.out.println("in read" + buffer.length);
            is.read(buffer);
            byte[] outputBytes = cipher.doFinal(buffer);            
            is = new ByteArrayInputStream(outputBytes);
}

当我尝试加密输入流时,我将获得增加的字节数组,此后,当我那时在解密时解密该输入流时,它将使用原始字节大小。因此,当我尝试打开该文件时,它将给出错误,该结束标记过早,因此我希望加密后文件大小相同,并且此方法是java类的一部分。

该问题的解决方案是什么?

1 个答案:

答案 0 :(得分:1)

您使用TRANSFORMATION =“ AES ”初始化密码,这意味着您的Java实现将选择默认的AES模式,通常为“ AES / ECB / PKCS5Padding >”。在 ECB模式不安全并且不应再用于长于16字节的明文/流的情况下,填充将添加额外的字节,最大块长为16。

因此,在运行我的小程序时,您会看到在实现中,inputLen为“ 10”(表示10字节长的纯文本/流)将导致outputSize为“ 16”。

为避免这种情况,您确实需要另一种AES模式,并且输出的长度与加密侧的输入的长度相同。您可以为此使用 AES CTR模式-您只需要一个附加参数(初始化向量,长16个字节)。重要的是,您绝对不要使用同一iv进行超过1次加密,因此应该将其作为随机值生成。为了解密,消息的接收者(“解密者”)需要知道在加密方面使用了什么初始化向量。

cipher algorithm: AES                  inputLen  10 outputSize  16
cipher algorithm: AES/CTR/NOPADDING    inputLen  10 outputSize  10

编辑(安全警告):正如James K. Polk总统所提醒的那样:“ CTR模式使得修改攻击者选择的单个字节变得很简单,因此需要结合使用验证标签。”。这取决于如果需要身份验证将要加密的数据类型(例如,如果您正在加密音乐文件,则任何修改都将导致“ piep”或“ krk”,财务数据的更改将导致灾难性的后果)。

代码:

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;

public class Main {
    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
        System.out.println("");
        int inputLen = 10;
        final String ALGORITHM = "AES";

        // aes ecb mode
        final String TRANSFORMATION = "AES";
        //final String TRANSFORMATION = "AES/ECB/PKCS5PADDING";
        String key = "C4F9EA21977047D6";
        Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        System.out.format("cipher algorithm: %-20s inputLen %3d outputSize %3d%n", cipher.getAlgorithm(), inputLen, cipher.getOutputSize(inputLen));

        // aes ctr mode
        String TRANSFORMATION2 = "AES/CTR/NOPADDING";
        // you need an unique (random) iv for each encryption
        SecureRandom secureRandom = new SecureRandom();
        byte[] iv = new byte[16];
        secureRandom.nextBytes(iv);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        Cipher cipher2 = Cipher.getInstance(TRANSFORMATION2);
        cipher2.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        System.out.format("cipher algorithm: %-20s inputLen %3d outputSize %3d%n", cipher2.getAlgorithm(), inputLen, cipher2.getOutputSize(inputLen));
    }
}

可以在以下位置找到AES CTR模式的运行中实现:https://stackoverflow.com/a/62662276/8166854

相关问题