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