Java 用AES加密解密文件 public long copyreamslong(InputStream in、OutputStream out、long sizeLimit)引发IOException{ 长字节数=0; IOException error=null; 长totalBytesRead=0; 试一试{ String key=“C4F9EA21977047D6”;//用户值(16/24/32字节) //byte[]keyBytes=DatatypeConverter.parseHexBinary(aesKey); SecretKeySpec secretKey=新的SecretKeySpec(key.getBytes(),“AES”); System.out.println(secretKey.toString()); Cipher Cipher=Cipher.getInstance(“AES”); cipher.init(cipher.ENCRYPT_模式,secretKey); byte[]buffer=新字节[CustomLimitedStreamCopier.byte_buffer_SIZE]; //in.read(缓冲区); int字节读取=-1; while((bytesRead=in.read(buffer))!=-1){ //一旦违反限制,我们可以立即中止复制。 totalBytesRead+=字节读取; if(sizeLimit>0&&totalBytesRead>sizeLimit){ StringBuilder msg=新的StringBuilder(); msg.append(“内容大小冲突,limit=”).append(sizeLimit); 抛出新的ContentLimitViolationException(msg.toString()); } 字节[]obuf=cipher.update(缓冲区,0,字节读取); 如果(obuf!=null){ out.write(obuf); } 字节计数+=字节读取; } 字节[]obuf=cipher.doFinal(); 如果(obuf!=null){ out.write(obuf); } out.flush(); }捕获(例外e){ e、 printStackTrace(); }最后{ 试一试{ in.close(); }捕获(IOE异常){ 误差=e; CustomLimitedStreamCopier.logger.error(“未能关闭输入流:“+this,e”); } 试一试{ out.close(); }捕获(IOE异常){ 误差=e; CustomLimitedStreamCopier.logger.error(“未能关闭输出流:“+this,e”); } } if(错误!=null) 投掷误差; 返回字节数; } 公共InputStream getContentInputStream()引发ContentIOException{ ReadableByteChannel通道=getReadableChannel(); InputStream is=通道。newInputStream(通道); 试试{ 最终字符串算法=“AES”; 最终字符串转换=“AES”; 字符串键=“C4F9EA21977047D6”; Key secretKey=new SecretKeySpec(Key.getBytes(),算法); Cipher Cipher=Cipher.getInstance(转换); cipher.init(cipher.DECRYPT_模式,secretKey); 字节[]缓冲区=ByTestStreams.toByteArray(is); System.out.println(“in read”+buffer.length); is.read(缓冲区); byte[]outputBytes=cipher.doFinal(缓冲区); is=新的ByteArrayInputStream(outputBytes); }

Java 用AES加密解密文件 public long copyreamslong(InputStream in、OutputStream out、long sizeLimit)引发IOException{ 长字节数=0; IOException error=null; 长totalBytesRead=0; 试一试{ String key=“C4F9EA21977047D6”;//用户值(16/24/32字节) //byte[]keyBytes=DatatypeConverter.parseHexBinary(aesKey); SecretKeySpec secretKey=新的SecretKeySpec(key.getBytes(),“AES”); System.out.println(secretKey.toString()); Cipher Cipher=Cipher.getInstance(“AES”); cipher.init(cipher.ENCRYPT_模式,secretKey); byte[]buffer=新字节[CustomLimitedStreamCopier.byte_buffer_SIZE]; //in.read(缓冲区); int字节读取=-1; while((bytesRead=in.read(buffer))!=-1){ //一旦违反限制,我们可以立即中止复制。 totalBytesRead+=字节读取; if(sizeLimit>0&&totalBytesRead>sizeLimit){ StringBuilder msg=新的StringBuilder(); msg.append(“内容大小冲突,limit=”).append(sizeLimit); 抛出新的ContentLimitViolationException(msg.toString()); } 字节[]obuf=cipher.update(缓冲区,0,字节读取); 如果(obuf!=null){ out.write(obuf); } 字节计数+=字节读取; } 字节[]obuf=cipher.doFinal(); 如果(obuf!=null){ out.write(obuf); } out.flush(); }捕获(例外e){ e、 printStackTrace(); }最后{ 试一试{ in.close(); }捕获(IOE异常){ 误差=e; CustomLimitedStreamCopier.logger.error(“未能关闭输入流:“+this,e”); } 试一试{ out.close(); }捕获(IOE异常){ 误差=e; CustomLimitedStreamCopier.logger.error(“未能关闭输出流:“+this,e”); } } if(错误!=null) 投掷误差; 返回字节数; } 公共InputStream getContentInputStream()引发ContentIOException{ ReadableByteChannel通道=getReadableChannel(); InputStream is=通道。newInputStream(通道); 试试{ 最终字符串算法=“AES”; 最终字符串转换=“AES”; 字符串键=“C4F9EA21977047D6”; Key secretKey=new SecretKeySpec(Key.getBytes(),算法); Cipher Cipher=Cipher.getInstance(转换); cipher.init(cipher.DECRYPT_模式,secretKey); 字节[]缓冲区=ByTestStreams.toByteArray(is); System.out.println(“in read”+buffer.length); is.read(缓冲区); byte[]outputBytes=cipher.doFinal(缓冲区); is=新的ByteArrayInputStream(outputBytes); },java,encryption,aes,Java,Encryption,Aes,当我试图加密输入流时,我将得到增加的字节数组,然后在解密时解密该输入流,它将采用原始字节大小。因此,当我试图打开该文件时,它将给出错误,即过早结束标记,因此我希望加密后的文件大小相同,并且该方法是java类的一部分 这个问题的解决方案是什么?使用转换=“AES”初始化密码,这意味着您的Java实现将选择默认的AES模式,通常是“AES/ECB/PKCS5Padding”。在ECB模式不安全且不应再对长度超过16字节的明文/流使用的事实下,填充将添加额外的字节,最多为(多个)块长度16 因此,运行

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


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

使用转换=“AES”初始化密码,这意味着您的Java实现将选择默认的AES模式,通常是“AES/ECB/PKCS5Padding”。在ECB模式不安全且不应再对长度超过16字节的明文/流使用的事实下,填充将添加额外的字节,最多为(多个)块长度16

因此,运行我的小程序时,您会看到在您的实现中,输入为“10”(表示10字节长度的明文/流)将导致输出大小为“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”,更改财务数据将导致灾难性后果)

代码:


AES CTR模式的一个正在运行的实现可以在这里找到:

CTR模式使得修改攻击者选择的单个字节变得很简单,因此它需要与身份验证标签相结合。这基本上就是GCM提供的。@James K.Polk总统:感谢您举手并指出使用CTR的部分“独立”不安全。据我所知,CTR模式是唯一一种为普通数据和加密数据生成相同长度的AES模式。GCM模式为标记添加了一些字节。实际使用的AES模式是ECB模式,这也很糟糕:-)在这种情况下,还有其他的想法吗?从纯信息论的角度来看,如果密文不比明文长,你就无法获得经过身份验证的加密。如果你必须拥有相同的长度,那么你就必须在没有身份验证的情况下生活。我相信
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));
    }
}