1 package java.util.zip; 2 3 import java.util.Random; 4 5 6 /** 7 * This class implements a Traditional Zip Encryption / Decryption 8 * engine according to <a href="https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT">the ZIP file format specification</a> to encrypt 9 * / decrypt a data after it has been compressed. 10 * 11 * @since 1.9 12 */ 13 public class TraditionalZipCryption implements ZipCryption { 14 15 private static long[] crc32Table; 16 17 private long[] keys; 18 19 private String password; 20 21 /** 22 * Encryption header size 23 */ 24 public static int ENCRYPTION_HEADER_SIZE = 12; 25 26 private static long[] INITIAL_KEY = {305419896L, 591751049L, 878082192L}; 27 28 private static int CRC_TABLE_SIZE = 256; 29 30 static { 31 crc32Table = new long[CRC_TABLE_SIZE]; 32 33 /* 34 * Calculate CRC-32 table 35 * make_crc_table() 36 * https://tools.ietf.org/html/rfc1952#section-8 37 */ 38 for (int n = 0; n < CRC_TABLE_SIZE; n++) { 39 long c = n; 40 41 for (int k = 0; k < 8; k++) { 42 c = ((c & 1) == 1) ? 0xedb88320L ^ (c >>> 1) : (c >>> 1); 43 c &= 0xffffffffL; 44 } 45 46 crc32Table[n] = c; 47 } 48 49 } 50 51 /** 52 * Constructor of TraditionalZipCryption. 53 * @param password ZIP password 54 */ 55 public TraditionalZipCryption(String password) { 56 this.password = password; 57 this.keys = new long[ENCRYPTION_HEADER_SIZE]; 58 reset(); 59 } 60 61 /** 62 * update_crc() 63 * https://tools.ietf.org/html/rfc1952#section-8 64 */ 65 private long crc32(long crc, int buf) { 66 return 67 (crc32Table[(int)(crc ^ buf) & 0xff] ^ (crc >>> 8)) & 0xffffffffL; 68 } 69 70 /** 71 * update_keys() 72 * 6.1.5 Initializing the encryption keys 73 * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT 74 */ 75 private void updateKeys(int c){ 76 keys[0] = crc32(keys[0], c); 77 keys[1] += keys[0] & 0xffL; 78 keys[1] = ((keys[1] * 134775813L) + 1L) & 0xffffffffL; 79 keys[2] = crc32(keys[2], (int)(keys[1] >>> 24)); 80 } 81 82 /** 83 * decrypt_byte() 84 * 6.1.6 Decrypting the encryption header 85 * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT 86 * 87 * @return Decrypt byte 88 */ 89 private int decryptByte() { 90 int temp = (int)(keys[2] & 0xffffL) | 2; 91 return ((temp * (temp ^ 1)) >>> 8) & 0xff; 92 } 93 94 private int encode(int unsignedByteData) { 95 int decByte = decryptByte(); 96 updateKeys(unsignedByteData); 97 return decByte ^ unsignedByteData; 98 } 99 100 private int decode(int unsignedByteData) { 101 int decByte = (decryptByte() ^ unsignedByteData) & 0xff; 102 updateKeys(decByte); 103 return decByte; 104 } 105 106 /** 107 * Calculate encription header 108 * 109 * @param e ZIP entry 110 * @return ZIP encryption header 111 */ 112 @Override 113 public byte[] getEncryptionHeader(ZipEntry e) { 114 /* 115 * 6.1.6 Decrypting the encryption header 116 * 117 * After the header is decrypted, the last 1 or 2 bytes in Buffer 118 * should be the high-order word/byte of the CRC for the file being 119 * decrypted, stored in Intel low-byte/high-byte order. Versions of 120 * PKZIP prior to 2.0 used a 2 byte CRC check; a 1 byte CRC check is 121 * used on versions after 2.0. This can be used to test if the password 122 * supplied is correct or not. 123 */ 124 Random rand = new Random(); 125 byte[] encryptionHeader = new byte[ENCRYPTION_HEADER_SIZE]; 126 rand.nextBytes(encryptionHeader); 127 128 /* This code comes from testkey() at crypt.c in unzip 6.0 */ 129 encryptionHeader[ENCRYPTION_HEADER_SIZE - 1] = 130 (e.crc == -1) ? (byte)((e.xdostime >>> 8) & 0xffL) 131 : (byte)((e.crc >>> 24) & 0xffL); 132 encryptBytes(encryptionHeader); 133 return encryptionHeader; 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override 140 public byte[] encryptBytes(byte[] data, int offset, int length) { 141 142 for (int idx = offset; idx < length; idx++) { 143 data[idx] = (byte)(encode(Byte.toUnsignedInt(data[idx])) & 0xff); 144 } 145 146 return data; 147 } 148 149 /** 150 * {@inheritDoc} 151 */ 152 @Override 153 public byte[] encryptBytes(byte[] data) { 154 return encryptBytes(data, 0, data.length); 155 } 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override 161 public void reset() { 162 keys[0] = INITIAL_KEY[0]; 163 keys[1] = INITIAL_KEY[1]; 164 keys[2] = INITIAL_KEY[2]; 165 166 for (byte b : password.getBytes()) { 167 updateKeys(Byte.toUnsignedInt(b)); 168 } 169 170 } 171 172 /** 173 * {@inheritDoc} 174 */ 175 @Override 176 public int getEncryptionHeaderSize() { 177 return ENCRYPTION_HEADER_SIZE; 178 } 179 180 /** 181 * {@inheritDoc} 182 */ 183 @Override 184 public boolean isValid(ZipEntry e, byte[] encryptionHeader) { 185 /* This code comes from testkey() at crypt.c in unzip 6.0 */ 186 byte checkDigit = encryptionHeader[ENCRYPTION_HEADER_SIZE - 1]; 187 188 return (e.flag & 8) == 8 189 ? (checkDigit == (byte)((e.xdostime >>> 8) & 0xffL)) 190 : (checkDigit == (byte)((e.crc >>> 24) & 0xffL)); 191 } 192 193 /** 194 * {@inheritDoc} 195 */ 196 @Override 197 public byte[] decryptBytes(byte[] data, int offset, int length) { 198 199 for (int idx = offset; idx < length; idx++) { 200 data[idx] = (byte)(decode(Byte.toUnsignedInt(data[idx])) & 0xff); 201 } 202 203 return data; 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 public byte[] decryptBytes(byte[] data) { 211 return decryptBytes(data, 0, data.length); 212 } 213 214 }