猿问

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

我正在使用 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 字节的块调用了方法两次:abcdefghijklmnopqrstuvwxyz。我有使用 AES 算法加密的正确 ZIP 文件。

我在任何存档器中打开此 ZIP 文件,例如WinZipWinRar并打开加密文件。结果我有以下文字:

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 }


慕斯709654
浏览 189回答 1
1回答

哆啦的时光机

我已经解决了这个问题。问题是 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;    }}
随时随地看视频慕课网APP

相关分类

Java
我要回答