package de.idyl.winzipaes.impl; import java.util.Calendar; import java.util.Date; import java.util.zip.ZipEntry; /** * information about one zip entry that is written to an encrypted zip archive * or read from one * * @author olaf@merkert.de */ public class ExtZipEntry extends ZipEntry { private CentralDirectoryEntry centralDirectoryEntry; /** empty instance with only a name */ public ExtZipEntry(String name) { super(name); } /** copy all "non-compression" attributes */ public ExtZipEntry(ExtZipEntry entry) { super(entry.getName()); setCompressedSize(entry.getCompressedSize()); setSize(entry.getSize()); setComment(entry.getComment()); setTime(entry.getTime()); setMethod(entry.getMethod()); } public ExtZipEntry(String name,CentralDirectoryEntry centralDirectoryEntry) { super(name); this.centralDirectoryEntry = centralDirectoryEntry; } public void initEncryptedEntry() { setCrc(0); // CRC-32 / for encrypted files it's 0 as AES/MAC checks integritiy this.flag |= 1; // bit0 - encrypted // flag |= 8; // bit3 - use data descriptor this.primaryCompressionMethod = 0x63; byte[] extraBytes = new byte[11]; extraBytes = new byte[11]; // extra data header ID for AES encryption is 0x9901 extraBytes[0] = 0x01; extraBytes[1] = (byte)0x99; // data size (currently 7, but subject to possible increase in the // future) extraBytes[2] = 0x07; // data size extraBytes[3] = 0x00; // data size // Integer version number specific to the zip vendor extraBytes[4] = 0x02; // version number extraBytes[5] = 0x00; // version number // 2-character vendor ID extraBytes[6] = 0x41; // vendor id extraBytes[7] = 0x45; // vendor id // AES encryption strength - 1=128, 2=192, 3=256 extraBytes[8] = 0x03; // actual compression method - 0x0000==stored (no compression) - 2 bytes extraBytes[9] = (byte) (getMethod() & 0xff); extraBytes[10] = (byte) ((getMethod() & 0xff00) >> 8); setExtra(extraBytes); } protected int flag; public int getFlag() { return this.flag; } public boolean isAesEncrypted() { return isEncrypted() && centralDirectoryEntry!=null && centralDirectoryEntry.isAesEncrypted(); } public boolean isEncrypted() { return (flag & 1) > 0; } protected int offset; public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } // 0x63 for encryption protected int primaryCompressionMethod; public int getPrimaryCompressionMethod() { return primaryCompressionMethod; } public void setPrimaryCompressionMethod(int primaryCompressionMethod) { this.primaryCompressionMethod = primaryCompressionMethod; } /** * Encrypted files: Note that the value in the "compressed size" fields of * the local file header and the central directory entry is the total size * of all the items listed above. In other words, it is the total size of * the salt value, password verification value, encrypted data, and * authentication code. * * @return data size only */ public long getEncryptedDataSize() { // authentication (10), salt (16), verification (2) return getCompressedSize() - 10 - 16 - 2; } public CentralDirectoryEntry getCentralDirectoryEntry() { return centralDirectoryEntry; } @Override public void setSize(long size) { if( size<0 ) { size = (size & 0xffffffffL); } super.setSize(size); } // -------------------------------------------------------------------------- /** * ZipEntry (my superclass) uses dosTime internally. On getTime() you get a * java time based long value. This method provides the DOS value that is * stored in the zip file. */ public long getDosTime() { return javaToDosTime(getTime()); } public static long javaToDosTime(long javaTime) { Date d = new Date(javaTime); Calendar ca = Calendar.getInstance(); ca.setTime(d); int year = ca.get(Calendar.YEAR); if (year < 1980) { return (1 << 21) | (1 << 16); } return (year - 1980) << 25 | (ca.get(Calendar.MONTH) + 1) << 21 | ca.get(Calendar.DAY_OF_MONTH) << 16 | ca.get(Calendar.HOUR_OF_DAY) << 11 | ca.get(Calendar.MINUTE) << 5 | ca.get(Calendar.SECOND) >> 1; } public static long dosToJavaTime(long dosTime) { Calendar ca = Calendar.getInstance(); ca.set(Calendar.YEAR, (int) ((dosTime >> 25) & 0x7f) + 1980); ca.set(Calendar.MONTH, (int) ((dosTime >> 21) & 0x0f) - 1); ca.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f); ca.set(Calendar.HOUR_OF_DAY, (int) (dosTime >> 11) & 0x1f); ca.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f); ca.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e); return ca.getTime().getTime(); } }