You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中如何解密从Azure Storage下载的ByteArrayOutputStream流

正确解密实现方案

核心逻辑说明

你上传时将原始文件流包装为CipherInputStream完成加密后上传,Azure Blob中存储的是加密后的字节数据,原有代码直接将加密字节下载到普通ByteArrayOutputStream,未经过解密逻辑处理,因此得到的仍然是加密数据。
正确逻辑是将读取到的加密字节通过和加密参数一致的Cipher工具类处理,得到明文后输出。

修复后完整代码(基于CipherInputStream实现,和上传逻辑对应)

public ByteArrayOutputStream download(String fileName, Long id) {
    ByteArrayOutputStream decryptedOutputStream = new ByteArrayOutputStream();
    try {
        // 初始化解密Cipher,参数和加密时完全对齐
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
        SecretKeySpec key = new SecretKeySpec(encrypkey.getBytes("UTF-8"), "AES");
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(INITIALIZATIO_VECTOR.getBytes("UTF-8")));

        CloudBlobContainer container = getBlobClient().getContainerReference("container" + id.toString());
        CloudBlockBlob blob = container.getBlockBlobReference(fileName);
        
        if (!blob.exists()) {
            logger.info("File does not exists on azure container");
            return decryptedOutputStream;
        }

        // 用try-with-resources自动关闭流,保证缓冲数据完全输出
        try (
            // 拿到Blob存储的加密内容输入流
            InputStream encryptedInput = blob.openInputStream();
            // 包装为解密输入流
            CipherInputStream decryptInput = new CipherInputStream(encryptedInput, cipher);
        ) {
            // 流拷贝得到明文输出
            byte[] buffer = new byte[4096];
            int readLen;
            while ((readLen = decryptInput.read(buffer)) != -1) {
                decryptedOutputStream.write(buffer, 0, readLen);
            }
            decryptedOutputStream.flush();
        }

    } catch (StorageException e) {
        logger.error("StorageException : {}", e.getLocalizedMessage(), e);
    } catch (Exception e) {
        logger.error("Exception : {}", e.getLocalizedMessage(), e);
    }
    return decryptedOutputStream;
}

可选CipherOutputStream实现

如果你更倾向用CipherOutputStream完成解密,替换流处理部分即可:

if (blob.exists()) {
    try (
        CipherOutputStream cipherOutput = new CipherOutputStream(decryptedOutputStream, cipher);
    ) {
        blob.download(cipherOutput);
        cipherOutput.flush();
    }
}

注意事项

  • 必须保证解密用的密钥、IV值的取值和编码和加密时完全一致,否则会出现解密失败
  • 当前使用的AES/CBC/NoPadding要求原始文件长度必须是16字节的整数倍,否则解密后末尾会出现异常字节,业务允许的情况下推荐替换为AES/CBC/PKCS5Padding自动处理填充逻辑
  • 必须保证Cipher包装的流正常关闭/flush,否则会出现末尾部分数据缺失的问题

内容的提问来源于stack exchange,提问作者Sanjay Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 15:45:03