--- /dev/null 2015-12-16 21:44:10.542337700 +0900 +++ new/src/java.base/share/classes/java/util/zip/TraditionalZipCryption.java 2015-12-16 21:59:38.602215573 +0900 @@ -0,0 +1,212 @@ +package java.util.zip; + +import java.util.Random; + + +/** + * This class implements a Traditional Zip Encryption / Decryption + * engine according to the ZIP file format specification to encrypt + * / decrypt a data after it has been compressed. + */ +public class TraditionalZipCryption implements ZipCryption { + + private static long[] crc32Table; + + private long[] keys; + + private String password; + + /** + * Encryption header size + */ + public static int ENCRYPTION_HEADER_SIZE = 12; + + private static long[] INITIAL_KEY = {305419896L, 591751049L, 878082192L}; + + private static int CRC_TABLE_SIZE = 256; + + static { + crc32Table = new long[CRC_TABLE_SIZE]; + + /* + * Calculate CRC-32 table + * make_crc_table() + * https://tools.ietf.org/html/rfc1952#section-8 + */ + for (int n = 0; n < CRC_TABLE_SIZE; n++) { + long c = n; + + for (int k = 0; k < 8; k++) { + c = ((c & 1) == 1) ? 0xedb88320L ^ (c >>> 1) : (c >>> 1); + c &= 0xffffffffL; + } + + crc32Table[n] = c; + } + + } + + /** + * Constructor of TraditionalZipCryption. + * @param password ZIP password + */ + public TraditionalZipCryption(String password) { + this.password = password; + this.keys = new long[ENCRYPTION_HEADER_SIZE]; + reset(); + } + + /** + * update_crc() + * https://tools.ietf.org/html/rfc1952#section-8 + */ + private long crc32(long crc, int buf) { + return + (crc32Table[(int)(crc ^ buf) & 0xff] ^ (crc >>> 8)) & 0xffffffffL; + } + + /** + * update_keys() + * 6.1.5 Initializing the encryption keys + * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + */ + private void updateKeys(int c){ + keys[0] = crc32(keys[0], c); + keys[1] += keys[0] & 0xffL; + keys[1] = ((keys[1] * 134775813L) + 1L) & 0xffffffffL; + keys[2] = crc32(keys[2], (int)(keys[1] >>> 24)); + } + + /** + * decrypt_byte() + * 6.1.6 Decrypting the encryption header + * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + * + * @return Decrypt byte + */ + private int decryptByte() { + int temp = (int)(keys[2] & 0xffffL) | 2; + return ((temp * (temp ^ 1)) >>> 8) & 0xff; + } + + private int encode(int unsignedByteData) { + int decByte = decryptByte(); + updateKeys(unsignedByteData); + return decByte ^ unsignedByteData; + } + + private int decode(int unsignedByteData) { + int decByte = (decryptByte() ^ unsignedByteData) & 0xff; + updateKeys(decByte); + return decByte; + } + + /** + * Calculate encription header + * + * @param e ZIP entry + * @return ZIP encryption header + */ + @Override + public byte[] getEncryptionHeader(ZipEntry e) { + /* + * 6.1.6 Decrypting the encryption header + * + * After the header is decrypted, the last 1 or 2 bytes in Buffer + * should be the high-order word/byte of the CRC for the file being + * decrypted, stored in Intel low-byte/high-byte order. Versions of + * PKZIP prior to 2.0 used a 2 byte CRC check; a 1 byte CRC check is + * used on versions after 2.0. This can be used to test if the password + * supplied is correct or not. + */ + Random rand = new Random(); + byte[] encryptionHeader = new byte[ENCRYPTION_HEADER_SIZE]; + rand.nextBytes(encryptionHeader); + + /* This code comes from testkey() at crypt.c in unzip 6.0 */ + encryptionHeader[ENCRYPTION_HEADER_SIZE - 1] = + (e.crc == -1) ? (byte)((e.xdostime >>> 8) & 0xffL) + : (byte)((e.crc >>> 24) & 0xffL); + encryptBytes(encryptionHeader); + return encryptionHeader; + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] encryptBytes(byte[] data, int offset, int length) { + + for (int idx = offset; idx < length; idx++) { + data[idx] = (byte)(encode(Byte.toUnsignedInt(data[idx])) & 0xff); + } + + return data; + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] encryptBytes(byte[] data) { + return encryptBytes(data, 0, data.length); + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + keys[0] = INITIAL_KEY[0]; + keys[1] = INITIAL_KEY[1]; + keys[2] = INITIAL_KEY[2]; + + for (byte b : password.getBytes()) { + updateKeys(Byte.toUnsignedInt(b)); + } + + } + + /** + * {@inheritDoc} + */ + @Override + public int getEncryptionHeaderSize() { + return ENCRYPTION_HEADER_SIZE; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(ZipEntry e, byte[] encryptionHeader) { + /* This code comes from testkey() at crypt.c in unzip 6.0 */ + byte checkDigit = encryptionHeader[ENCRYPTION_HEADER_SIZE - 1]; + + return (e.flag & 8) == 8 + ? (checkDigit == (byte)((e.xdostime >>> 8) & 0xffL)) + : (checkDigit == (byte)((e.crc >>> 24) & 0xffL)); + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] decryptBytes(byte[] data, int offset, int length) { + + for (int idx = offset; idx < length; idx++) { + data[idx] = (byte)(decode(Byte.toUnsignedInt(data[idx])) & 0xff); + } + + return data; + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] decryptBytes(byte[] data) { + return decryptBytes(data, 0, data.length); + } + +}