【问题标题】:File Encryption Decryption with AES使用 AES 进行文件加密解密
【发布时间】:2020-07-03 20:03:47
【问题描述】:
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 类的一部分。

这个问题的解决方案是什么?

【问题讨论】:

    标签: java encryption aes


    【解决方案1】:

    您使用 TRANSFORMATION = "AES" 初始化密码,这意味着您的 Java 实现将选择默认的 AES 模式,通常是 "AES/ECB/PKCS5Padding”。在 ECB 模式不安全且不应再使用长于 16 字节的纯文本/流这一事实的背后,填充将添加额外的字节,直至(多个)块长度为 16。

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

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

    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

    【讨论】:

    • CTR 模式使得修改攻击者选择的单个字节变得微不足道,因此它需要与身份验证标签相结合。这基本上是 GCM 提供的。
    • @President James K. Polk:感谢您举手并指出使 CTR 使用“独立”不安全的部分。 CTR 模式 - 据我所知 - 唯一一种对纯数据和加密数据产生相同长度的 AES 模式。 GCM 模式为标签添加了一些字节。实际使用的 AES 模式是 ECB 模式,这也很糟糕 :-) 在这种情况下还有什么其他的想法可以提供帮助吗?
    • 从纯粹的信息论的角度来看,如果不使密文比明文长,您将无法获得经过身份验证的加密。如果您必须具有相同的长度,那么您必须在没有身份验证的情况下生活。我相信在这种情况下最好的模式是XTS。它仍然是一种未认证模式但是更改单个密文的一个比特会对解密结果产生更大的影响,并且不那么可控。
    • @President James K. Polk - XTS 似乎是“重炮”,但你是对的,我编辑了答案并添加了安全警告 - 谢谢。您是否有指向有效 XTS 示例的链接?
    • 很好的编辑。我没有任何 XTS 示例,Java 也没有对 XTS 的任何直接支持,您必须从 ECB 模式自己构建它。
    猜你喜欢
    • 2020-06-16
    • 2013-04-30
    • 2014-01-14
    • 2017-08-28
    • 2014-03-17
    • 2021-04-27
    • 2014-09-14
    • 1970-01-01
    • 2011-01-31
    相关资源
    最近更新 更多