为了账号安全,请及时绑定邮箱和手机立即绑定

ZIP 文件的 AES 加密不正确的第二个 16 字节块

ZIP 文件的 AES 加密不正确的第二个 16 字节块

慕斯709654 2023-06-04 17:52:30
我正在使用 Java 的实现来实现Zip AESCipher加密。这是我的加密代码:public final class AesEncoder implements Encoder {    private final Cipher cipher;    private final Mac mac;    private final byte[] salt;    private final byte[] derivedPasswordVerifier;    // AesStrength is an Enum with AES strength constants like salt or mac length    public static AesEncoder create(AesStrength strength, char[] password) throws Exception {        KeySpec spec = new PBEKeySpec(password, salt, 1000, strength.getSize());        SecretKey secretKey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec);        byte[] iv = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secretKey.getEncoded(), "AES"), new IvParameterSpec(iv));        Mac mac = Mac.getInstance("HmacSHA1");        mac.init(new SecretKeySpec(macKey, "HmacSHA1"));        return new AesEncoder(cipher, mac, salt, derivedPasswordVerifier);    }    private static byte[] generateSalt(AesStrength strength) {        SecureRandom random = new SecureRandom();        byte[] buf = new byte[strength.getSaltLength()];        random.nextBytes(buf);        return buf;    }}我想加密以下文本:abcdefghijklmnopqrstuvwxyz没有细节,我encrypt()用两个长度为 16 字节的块调用了方法两次:abcdefghijklmnop和qrstuvwxyz。我有使用 AES 算法加密的正确 ZIP 文件。我在任何存档器中打开此 ZIP 文件,例如WinZip或WinRar并打开加密文件。结果我有以下文字:abcdefghijklmnopÄÝB`CÙ∼Wi¯如您所见,第一个块已正确加密,但第二个块未正确加密。我调查了这个问题。我找到了一个名为zip4j 的工作解决方案,并发现了两个不同之处:第一:这个库有自定义 AES 实现AESEngine;我使用jdk实现;第二:这个库从第一个字节开始增加初始化向量。第一个块iv = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };第二块iv = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };但是jdk 中使用的com.sun.crypto.provider.CounterMode从末尾递增向量:第一个块iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };第二块iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };PS如果我设置初始向量iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
查看完整描述

1 回答

?
哆啦的时光机

TA贡献1779条经验 获得超6个赞

我已经解决了这个问题。问题是 java 的 AES 实现与 ZIP 标准有点不同。


我已经使用 java类在我的zip4jvmCypher库中实现了 AES 加密:


class AesEncoder {

    public static AesEncoder create(AesStrength strength, char[] password) {

        try {

            byte[] salt = strength.generateSalt();

            byte[] key = AesEngine.createKey(password, salt, strength);


            Cipher cipher = AesEngine.createCipher(strength.createSecretKeyForCipher(key));

            Mac mac = AesEngine.createMac(strength.createSecretKeyForMac(key));

            byte[] passwordChecksum = strength.createPasswordChecksum(key);


            return new AesEncoder(cipher, mac, salt, passwordChecksum);

        } catch(Exception e) {

            throw new Zip4jvmException(e);

        }

    }

}


@RequiredArgsConstructor(access = AccessLevel.PACKAGE)

enum AesStrength {

    S128(128),

    S192(192),

    S256(256);


    private final int size;


    public final int saltLength() {

        return size / 16;

    }


    private int macLength() {

        return size / 8;

    }


    private int keyLength() {

        return size / 8;

    }


    public SecretKeySpec createSecretKeyForCipher(byte[] key) {

        return new SecretKeySpec(key, 0, keyLength(), "AES");

    }


    public SecretKeySpec createSecretKeyForMac(byte[] key) {

        return new SecretKeySpec(key, keyLength(), macLength(), "HmacSHA1");

    }


    public byte[] createPasswordChecksum(byte[] key) {

        final int offs = keyLength() + macLength();

        return new byte[] { key[offs], key[offs + 1] };

    }


    public byte[] generateSalt() {

        SecureRandom random = new SecureRandom();

        byte[] buf = new byte[saltLength()];

        random.nextBytes(buf);

        return buf;

    }


}


class AesEngine {

    private static final int ITERATION_COUNT = 1000;


    public static byte[] createKey(char[] password, byte[] salt, AesStrength strength) throws NoSuchAlgorithmException, InvalidKeySpecException {

        int keyLength = strength.getSize() * 2 + 16;

        KeySpec keySpec = new PBEKeySpec(password, salt, ITERATION_COUNT, keyLength);

        return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(keySpec).getEncoded();

    }


    public static Cipher createCipher(SecretKeySpec secretKeySpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {

        Cipher cipher = Cipher.getInstance("AES");

        // use custom AES implementation, so no worry for DECRYPT_MODE

        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

        return cipher;

    }


    public static Mac createMac(SecretKeySpec secretKeySpec) throws NoSuchAlgorithmException, InvalidKeyException {

        Mac mac = Mac.getInstance("HmacSHA1");

        mac.init(secretKeySpec);

        return mac;

    }


}


查看完整回答
反对 回复 2023-06-04
  • 1 回答
  • 0 关注
  • 188 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号