diff --git a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesCentralDirectoryDecoder.java b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesCentralDirectoryDecoder.java index 06e13b60..5d8262a5 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesCentralDirectoryDecoder.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesCentralDirectoryDecoder.java @@ -8,6 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.io.IOException; import javax.crypto.Cipher; /** @@ -25,7 +26,7 @@ public final class AesCentralDirectoryDecoder implements Decoder { public static AesCentralDirectoryDecoder create128(DecryptionHeader decryptionHeader, char[] password, long compressedSize, - ByteOrder byteOrder) { + ByteOrder byteOrder) throws IOException { return create(decryptionHeader, password, AesStrength.S128, compressedSize, byteOrder); } @@ -33,7 +34,7 @@ public static AesCentralDirectoryDecoder create128(DecryptionHeader decryptionHe public static AesCentralDirectoryDecoder create192(DecryptionHeader decryptionHeader, char[] password, long compressedSize, - ByteOrder byteOrder) { + ByteOrder byteOrder) throws IOException { return create(decryptionHeader, password, AesStrength.S192, compressedSize, byteOrder); } @@ -41,7 +42,7 @@ public static AesCentralDirectoryDecoder create192(DecryptionHeader decryptionHe public static AesCentralDirectoryDecoder create256(DecryptionHeader decryptionHeader, char[] password, long compressedSize, - ByteOrder byteOrder) { + ByteOrder byteOrder) throws IOException { return create(decryptionHeader, password, AesStrength.S256, compressedSize, byteOrder); } @@ -49,7 +50,7 @@ private static AesCentralDirectoryDecoder create(DecryptionHeader decryptionHead char[] password, AesStrength strength, long compressedSize, - ByteOrder byteOrder) { + ByteOrder byteOrder) throws IOException { Cipher cipher = AesStrongEngine.createCipher(decryptionHeader, password, strength, byteOrder); AesStrongEngine engine = new AesStrongEngine(cipher); return new AesCentralDirectoryDecoder(engine, compressedSize); diff --git a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesDecoder.java b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesDecoder.java index 650a4681..ea90021f 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesDecoder.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesDecoder.java @@ -50,34 +50,32 @@ public final class AesDecoder implements Decoder { private final long compressedSize; @SuppressWarnings("NewMethodNamingConvention") - public static AesDecoder create128(ZipEntry zipEntry, DataInput in) { + public static AesDecoder create128(ZipEntry zipEntry, DataInput in) throws IOException { return create(zipEntry, AesStrength.S128, in); } @SuppressWarnings("NewMethodNamingConvention") - public static AesDecoder create192(ZipEntry zipEntry, DataInput in) { + public static AesDecoder create192(ZipEntry zipEntry, DataInput in) throws IOException { return create(zipEntry, AesStrength.S192, in); } @SuppressWarnings("NewMethodNamingConvention") - public static AesDecoder create256(ZipEntry zipEntry, DataInput in) { + public static AesDecoder create256(ZipEntry zipEntry, DataInput in) throws IOException { return create(zipEntry, AesStrength.S256, in); } - private static AesDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) { - return Quietly.doQuietly(() -> { - byte[] salt = in.readBytes(strength.getSaltSize()); - byte[] key = AesEngine.createKey(zipEntry.getPassword(), salt, strength); + private static AesDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) throws IOException { + byte[] salt = in.readBytes(strength.getSaltSize()); + byte[] key = AesEngine.createKey(zipEntry.getPassword(), salt, strength); - Cipher cipher = AesEngine.createCipher(strength.createSecretKeyForCipher(key)); - byte[] passwordChecksum = strength.createPasswordChecksum(key); - checkPasswordChecksum(passwordChecksum, zipEntry, in); + Cipher cipher = AesEngine.createCipher(strength.createSecretKeyForCipher(key)); + byte[] passwordChecksum = strength.createPasswordChecksum(key); + checkPasswordChecksum(passwordChecksum, zipEntry, in); - Mac mac = AesEngine.createMac(strength.createSecretKeyForMac(key)); - AesEngine engine = new AesEngine(cipher, mac); - long compressedSize = AesEngine.getDataCompressedSize(zipEntry.getCompressedSize(), strength); - return new AesDecoder(engine, compressedSize); - }); + Mac mac = AesEngine.createMac(strength.createSecretKeyForMac(key)); + AesEngine engine = new AesEngine(cipher, mac); + long compressedSize = AesEngine.getDataCompressedSize(zipEntry.getCompressedSize(), strength); + return new AesDecoder(engine, compressedSize); } // ---------- Decrypt ---------- diff --git a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesEngine.java b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesEngine.java index d17ba3bf..cf44be84 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesEngine.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesEngine.java @@ -27,13 +27,9 @@ import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import javax.crypto.Cipher; import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKeyFactory; import javax.crypto.ShortBufferException; import javax.crypto.spec.PBEKeySpec; @@ -121,25 +117,29 @@ public byte[] getMac() { return mac.doFinal(); } - 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 byte[] createKey(char[] password, byte[] salt, AesStrength strength) { + return Quietly.doQuietly(() -> { + 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/ECB/NoPadding"); - // use custom AES implementation, so no worry for DECRYPT_MODE - cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); - return cipher; + public static Cipher createCipher(SecretKeySpec secretKeySpec) { + return Quietly.doQuietly(() -> { + Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); + // 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; + public static Mac createMac(SecretKeySpec secretKeySpec) { + return Quietly.doQuietly(() -> { + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(secretKeySpec); + return mac; + }); } public static AesStrength getStrength(EncryptionMethod encryptionMethod) { diff --git a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesStrongDecoder.java b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesStrongDecoder.java index 2ab752fc..035eb4dd 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesStrongDecoder.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/crypto/aes/AesStrongDecoder.java @@ -50,28 +50,26 @@ public final class AesStrongDecoder implements Decoder { private long decryptedBytes; @SuppressWarnings("NewMethodNamingConvention") - public static AesStrongDecoder create128(ZipEntry zipEntry, DataInput in) { + public static AesStrongDecoder create128(ZipEntry zipEntry, DataInput in) throws IOException { return create(zipEntry, AesStrength.S128, in); } @SuppressWarnings("NewMethodNamingConvention") - public static AesStrongDecoder create192(ZipEntry zipEntry, DataInput in) { + public static AesStrongDecoder create192(ZipEntry zipEntry, DataInput in) throws IOException { return create(zipEntry, AesStrength.S192, in); } @SuppressWarnings("NewMethodNamingConvention") - public static AesStrongDecoder create256(ZipEntry zipEntry, DataInput in) { + public static AesStrongDecoder create256(ZipEntry zipEntry, DataInput in) throws IOException { return create(zipEntry, AesStrength.S256, in); } - public static AesStrongDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) { - return Quietly.doQuietly(() -> { - in.mark(DECRYPTION_HEADER); - Cipher cipher = createCipher(zipEntry, strength, in); - int decryptionHeaderSize = (int) in.getMarkSize(DECRYPTION_HEADER); - long compressedSize = zipEntry.getCompressedSize() - decryptionHeaderSize; - return new AesStrongDecoder(cipher, compressedSize); - }); + public static AesStrongDecoder create(ZipEntry zipEntry, AesStrength strength, DataInput in) throws IOException { + in.mark(DECRYPTION_HEADER); + Cipher cipher = createCipher(zipEntry, strength, in); + int decryptionHeaderSize = (int) in.getMarkSize(DECRYPTION_HEADER); + long compressedSize = zipEntry.getCompressedSize() - decryptionHeaderSize; + return new AesStrongDecoder(cipher, compressedSize); } private static Cipher createCipher(ZipEntry zipEntry, AesStrength strength, DataInput in) throws IOException { diff --git a/src/main/java/ru/olegcherednik/zip4jvm/crypto/strong/EncryptionAlgorithm.java b/src/main/java/ru/olegcherednik/zip4jvm/crypto/strong/EncryptionAlgorithm.java index 5e7c336d..c99e473d 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/crypto/strong/EncryptionAlgorithm.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/crypto/strong/EncryptionAlgorithm.java @@ -29,6 +29,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.io.IOException; import java.util.Optional; import javax.crypto.Cipher; @@ -76,7 +77,7 @@ public enum EncryptionAlgorithm { public Decoder createEcdDecoder(DecryptionHeader decryptionHeader, char[] password, long compressedSize, - ByteOrder byteOrder) { + ByteOrder byteOrder) throws IOException { return Optional.ofNullable(ecdDecoderFactory) .orElseThrow(() -> new EncryptionNotSupportedException(this)) .create(decryptionHeader, password, compressedSize, byteOrder); @@ -98,7 +99,8 @@ public static EncryptionAlgorithm parseCode(int code) { private interface DecoderFactory { - Decoder create(DecryptionHeader decryptionHeader, char[] password, long compressedSize, ByteOrder byteOrder); + Decoder create(DecryptionHeader decryptionHeader, char[] password, long compressedSize, ByteOrder byteOrder) + throws IOException; } diff --git a/src/main/java/ru/olegcherednik/zip4jvm/io/in/data/EncryptedDataInput.java b/src/main/java/ru/olegcherednik/zip4jvm/io/in/data/EncryptedDataInput.java index d0b66767..ec63feba 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/io/in/data/EncryptedDataInput.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/io/in/data/EncryptedDataInput.java @@ -35,7 +35,7 @@ public class EncryptedDataInput extends BaseDataInput { protected final Decoder decoder; protected long available; - public static EncryptedDataInput create(ZipEntry zipEntry, DataInput in) { + public static EncryptedDataInput create(ZipEntry zipEntry, DataInput in) throws IOException { Decoder decoder = zipEntry.createDecoder(in); int batchSize = Math.max(0, decoder.getBlockSize()); long encryptedSize = decoder == Decoder.NULL ? zipEntry.getCompressedSize() : decoder.getCompressedSize(); diff --git a/src/main/java/ru/olegcherednik/zip4jvm/model/EncryptionMethod.java b/src/main/java/ru/olegcherednik/zip4jvm/model/EncryptionMethod.java index bf1d003c..edf10241 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/model/EncryptionMethod.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/model/EncryptionMethod.java @@ -35,6 +35,7 @@ import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import java.io.IOException; import java.util.Optional; import java.util.function.Function; @@ -73,7 +74,7 @@ public Encoder createEncoder(ZipEntry zipEntry) { .apply(zipEntry); } - public Decoder createDecoder(ZipEntry zipEntry, DataInput in) { + public Decoder createDecoder(ZipEntry zipEntry, DataInput in) throws IOException { return Optional.ofNullable(decoderFactory) .orElseThrow(() -> new EncryptionNotSupportedException(this)) .create(zipEntry, in); @@ -103,7 +104,7 @@ public static EncryptionMethod get(PkwareExtraField extraField, GeneralPurposeFl private interface DecoderFactory { - Decoder create(ZipEntry zipEntry, DataInput in); + Decoder create(ZipEntry zipEntry, DataInput in) throws IOException; } diff --git a/src/main/java/ru/olegcherednik/zip4jvm/model/entry/RegularFileZipEntry.java b/src/main/java/ru/olegcherednik/zip4jvm/model/entry/RegularFileZipEntry.java index 4535665f..10e23eb2 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/model/entry/RegularFileZipEntry.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/model/entry/RegularFileZipEntry.java @@ -32,6 +32,8 @@ import lombok.Getter; import lombok.Setter; +import java.io.IOException; + /** * @author Oleg Cherednik * @since 26.07.2019 @@ -61,7 +63,7 @@ final class RegularFileZipEntry extends ZipEntry { } @Override - public Decoder createDecoder(DataInput in) { + public Decoder createDecoder(DataInput in) throws IOException { return encryptionMethod.createDecoder(this, in); } diff --git a/src/main/java/ru/olegcherednik/zip4jvm/model/entry/ZipEntry.java b/src/main/java/ru/olegcherednik/zip4jvm/model/entry/ZipEntry.java index a92f9782..07ff2a7c 100644 --- a/src/main/java/ru/olegcherednik/zip4jvm/model/entry/ZipEntry.java +++ b/src/main/java/ru/olegcherednik/zip4jvm/model/entry/ZipEntry.java @@ -136,7 +136,7 @@ public ZipFile.Entry createImmutableEntry() { externalFileAttributes); } - public Decoder createDecoder(DataInput in) { + public Decoder createDecoder(DataInput in) throws IOException { return Decoder.NULL; }