http://git-wip-us.apache.org/repos/asf/commons-compress/blob/a433f625/src/main/java/org/apache/commons/compress/archivers/zip/PKWareExtraHeader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/PKWareExtraHeader.java b/src/main/java/org/apache/commons/compress/archivers/zip/PKWareExtraHeader.java new file mode 100644 index 0000000..fbd1c09 --- /dev/null +++ b/src/main/java/org/apache/commons/compress/archivers/zip/PKWareExtraHeader.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.compress.archivers.zip; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; + +/** + * Base class for all PKWare strong crypto extra headers. + * + * <b>Algorithm IDs</b> - integer identifier of the encryption algorithm from + * the following range + * + * <ul> + * <li>0x6601 - DES</li> + * <li>0x6602 - RC2 (version needed to extract < 5.2)</li> + * <li>0x6603 - 3DES 168</li> + * <li>0x6609 - 3DES 112</li> + * <li>0x660E - AES 128</li> + * <li>0x660F - AES 192</li> + * <li>0x6610 - AES 256</li> + * <li>0x6702 - RC2 (version needed to extract >= 5.2)</li> + * <li>0x6720 - Blowfish</li> + * <li>0x6721 - Twofish</li> + * <li>0x6801 - RC4</li> + * <li>0xFFFF - Unknown algorithm</li> + * </ul> + * + * <b>Hash Algorithms</b> - integer identifier of the hash algorithm from the + * following range + * + * <ul> + * <li>0x0000 - none</li> + * <li>0x0001 - CRC32</li> + * <li>0x8003 - MD5</li> + * <li>0x8004 - SHA1</li> + * <li>0x8007 - RIPEMD160</li> + * <li>0x800C - SHA256</li> + * <li>0x800D - SHA384</li> + * <li>0x800E - SHA512</li> + * </ul> + * + * TODO: define enums for crypto and hash algorithms. + */ +public abstract class PKWareExtraHeader implements ZipExtraField { + + /** + * Encryption algorithm. + */ + public enum EncryptionAlgorithm { + DES(0x6601), + RC2pre52(0x6602), + TripleDES168(0x6603), + TripleDES192(0x6609), + AES128(0x660E), + AES192(0x660F), + AES256(0x6610), + RC2(0x6702), + RC4(0x6801), + UNKNOWN(0xFFFF); + + private final int code; + + private static final Map<Integer, EncryptionAlgorithm> codeToEnum; + + static { + Map<Integer, EncryptionAlgorithm> cte = new HashMap<Integer, EncryptionAlgorithm>(); + for (EncryptionAlgorithm method : values()) { + cte.put(Integer.valueOf(method.getCode()), method); + } + codeToEnum = Collections.unmodifiableMap(cte); + } + + /** + * private constructor for enum style class. + */ + EncryptionAlgorithm(int code) { + this.code = code; + } + + /** + * the algorithm id. + * + * @return the PKWare AlgorithmId + */ + public int getCode() { + return code; + } + + /** + * returns the EncryptionAlgorithm for the given code or null if the + * method is not known. + */ + public static EncryptionAlgorithm getAlgorithmByCode(int code) { + return codeToEnum.get(Integer.valueOf(code)); + } + } + + /** + * Hash Algorithm + */ + public enum HashAlgorithm { + NONE(0), + CRC32(1), + MD5(0x8003), + SHA1(0x8004), + RIPEND160(0x8007), + SHA256(0x800C), + SHA384(0x800D), + SHA512(0x800E); + + private final int code; + + private static final Map<Integer, HashAlgorithm> codeToEnum; + + static { + Map<Integer, HashAlgorithm> cte = new HashMap<Integer, HashAlgorithm>(); + for (HashAlgorithm method : values()) { + cte.put(Integer.valueOf(method.getCode()), method); + } + codeToEnum = Collections.unmodifiableMap(cte); + } + + /** + * private constructor for enum style class. + */ + HashAlgorithm(int code) { + this.code = code; + } + + /** + * the hash algorithm ID. + * + * @return the PKWare hashAlg + */ + public int getCode() { + return code; + } + + /** + * returns the HashAlgorithm for the given code or null if the method is + * not known. + */ + public static HashAlgorithm getAlgorithmByCode(int code) { + return codeToEnum.get(Integer.valueOf(code)); + } + } +}
http://git-wip-us.apache.org/repos/asf/commons-compress/blob/a433f625/src/main/java/org/apache/commons/compress/archivers/zip/X0014_X509Certificates.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/X0014_X509Certificates.java b/src/main/java/org/apache/commons/compress/archivers/zip/X0014_X509Certificates.java new file mode 100644 index 0000000..669700b --- /dev/null +++ b/src/main/java/org/apache/commons/compress/archivers/zip/X0014_X509Certificates.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.compress.archivers.zip; + +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * PKCS#7 Store for X.509 Certificates (0x0014): + * + * This field MUST contain information about each of the certificates files may + * be signed with. When the Central Directory Encryption feature is enabled for + * a ZIP file, this record will appear in the Archive Extra Data Record, + * otherwise it will appear in the first central directory record and will be + * ignored in any other record. + * + * Note: all fields stored in Intel low-byte/high-byte order. + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * (Store) 0x0014 2 bytes Tag for this "extra" block type + * TSize 2 bytes Size of the store data + * TData TSize Data about the store + * </pre> + * + * @NotThreadSafe + */ +public class X0014_X509Certificates extends PKWareExtraHeader implements ZipExtraField { + private static final ZipShort HEADER_ID = new ZipShort(0x0014); + private static final long serialVersionUID = 1L; + + /** + * Get the header id. + * + * @return the header id + */ + public ZipShort getHeaderId() { + return HEADER_ID; + } + + /** + * Extra field data in local file data - without Header-ID or length + * specifier. + */ + private byte[] localData; + + /** + * Set the extra field data in the local file data - without Header-ID or + * length specifier. + * + * @param data + * the field data to use + */ + public void setLocalFileDataData(byte[] data) { + localData = ZipUtil.copy(data); + } + + /** + * Get the length of the local data. + * + * @return the length of the local data + */ + public ZipShort getLocalFileDataLength() { + return new ZipShort(localData != null ? localData.length : 0); + } + + /** + * Get the local data. + * + * @return the local data + */ + public byte[] getLocalFileDataData() { + return ZipUtil.copy(localData); + } + + /** + * Extra field data in central directory - without Header-ID or length + * specifier. + */ + private byte[] centralData; + + /** + * Set the extra field data in central directory. + * + * @param data + * the data to use + */ + public void setCentralDirectoryData(byte[] data) { + centralData = ZipUtil.copy(data); + } + + /** + * Get the central data length. If there is no central data, get the local + * file data length. + * + * @return the central data length + */ + public ZipShort getCentralDirectoryLength() { + if (centralData != null) { + return new ZipShort(centralData.length); + } + return getLocalFileDataLength(); + } + + /** + * Get the central data. + * + * @return the central data if present, else return the local file data + */ + public byte[] getCentralDirectoryData() { + if (centralData != null) { + return ZipUtil.copy(centralData); + } + return getLocalFileDataData(); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromLocalFileData(byte[], int, int) + */ + public void parseFromLocalFileData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setLocalFileDataData(tmp); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) + */ + public void parseFromCentralDirectoryData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setCentralDirectoryData(tmp); + if (localData == null) { + setLocalFileDataData(tmp); + } + } +} http://git-wip-us.apache.org/repos/asf/commons-compress/blob/a433f625/src/main/java/org/apache/commons/compress/archivers/zip/X0015_CertificateIdForFile.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/X0015_CertificateIdForFile.java b/src/main/java/org/apache/commons/compress/archivers/zip/X0015_CertificateIdForFile.java new file mode 100644 index 0000000..44295db --- /dev/null +++ b/src/main/java/org/apache/commons/compress/archivers/zip/X0015_CertificateIdForFile.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.compress.archivers.zip; + +/** + * X.509 Certificate ID and Signature for individual file (0x0015): + * + * This field contains the information about which certificate in the PKCS#7 + * store was used to sign a particular file. It also contains the signature + * data. This field can appear multiple times, but can only appear once per + * certificate. + * + * Note: all fields stored in Intel low-byte/high-byte order. + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * (CID) 0x0015 2 bytes Tag for this "extra" block type + * TSize 2 bytes Size of data that follows + * RCount 4 bytes Number of recipients. (inferred) + * HashAlg 2 bytes Hash algorithm identifier. (inferred) + * TData TSize Signature Data + * </pre> + * + * @NotThreadSafe + */ +public class X0015_CertificateIdForFile extends PKWareExtraHeader implements ZipExtraField { + private static final ZipShort HEADER_ID = new ZipShort(0x0015); + private static final long serialVersionUID = 1L; + + /** + * Get the header id. + * + * @return the header id + */ + public ZipShort getHeaderId() { + return HEADER_ID; + } + + /** + * Extra field data in local file data - without Header-ID or length + * specifier. + */ + private byte[] localData; + + private int rcount; + private HashAlgorithm hashAlg; + + /** + * Get record count. + * @return + */ + public int getRecordCount() { + return rcount; + } + + /** + * Get hash algorithm. + * @return + */ + public HashAlgorithm getHashAlgorithm() { + return hashAlg; + } + + /** + * Set the extra field data in the local file data - without Header-ID or + * length specifier. + * + * @param data + * the field data to use + */ + public void setLocalFileDataData(byte[] data) { + localData = ZipUtil.copy(data); + } + + /** + * Get the length of the local data. + * + * @return the length of the local data + */ + public ZipShort getLocalFileDataLength() { + return new ZipShort(localData != null ? localData.length : 0); + } + + /** + * Get the local data. + * + * @return the local data + */ + public byte[] getLocalFileDataData() { + return ZipUtil.copy(localData); + } + + /** + * Extra field data in central directory - without Header-ID or length + * specifier. + */ + private byte[] centralData; + + /** + * Set the extra field data in central directory. + * + * @param data + * the data to use + */ + public void setCentralDirectoryData(byte[] data) { + centralData = ZipUtil.copy(data); + } + + /** + * Get the central data length. If there is no central data, get the local + * file data length. + * + * @return the central data length + */ + public ZipShort getCentralDirectoryLength() { + if (centralData != null) { + return new ZipShort(centralData.length); + } + return getLocalFileDataLength(); + } + + /** + * Get the central data. + * + * @return the central data if present, else return the local file data + */ + public byte[] getCentralDirectoryData() { + if (centralData != null) { + return ZipUtil.copy(centralData); + } + return getLocalFileDataData(); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromLocalFileData(byte[], int, int) + */ + public void parseFromLocalFileData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setLocalFileDataData(tmp); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) + */ + public void parseFromCentralDirectoryData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setCentralDirectoryData(tmp); + if (localData == null) { + setLocalFileDataData(tmp); + } + + this.rcount = ZipShort.getValue(data, offset); + this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 2)); + } +} http://git-wip-us.apache.org/repos/asf/commons-compress/blob/a433f625/src/main/java/org/apache/commons/compress/archivers/zip/X0016_CertificateIdForCentralDirectory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/X0016_CertificateIdForCentralDirectory.java b/src/main/java/org/apache/commons/compress/archivers/zip/X0016_CertificateIdForCentralDirectory.java new file mode 100644 index 0000000..2696c61 --- /dev/null +++ b/src/main/java/org/apache/commons/compress/archivers/zip/X0016_CertificateIdForCentralDirectory.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.compress.archivers.zip; + +/** + * X.509 Certificate ID and Signature for central directory (0x0016): + * + * This field contains the information about which certificate in the PKCS#7 + * store was used to sign the central directory structure. When the Central + * Directory Encryption feature is enabled for a ZIP file, this record will + * appear in the Archive Extra Data Record, otherwise it will appear in the + * first central directory record. + * + * Note: all fields stored in Intel low-byte/high-byte order. + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * (CDID) 0x0016 2 bytes Tag for this "extra" block type + * TSize 2 bytes Size of data that follows + * RCount 4 bytes Number of recipients. (inferred) + * HashAlg 2 bytes Hash algorithm identifier. (inferred) + * TData TSize Data + * </pre> + * + * @NotThreadSafe + */ +public class X0016_CertificateIdForCentralDirectory extends PKWareExtraHeader implements ZipExtraField { + private static final ZipShort HEADER_ID = new ZipShort(0x0016); + private static final long serialVersionUID = 1L; + + /** + * Get the header id. + * + * @return the header id + */ + public ZipShort getHeaderId() { + return HEADER_ID; + } + + /** + * Extra field data in local file data - without Header-ID or length + * specifier. + */ + private byte[] localData; + + private int rcount; + private HashAlgorithm hashAlg; + + /** + * Get record count. + * @return + */ + public int getRecordCount() { + return rcount; + } + + /** + * Get hash algorithm. + * @return + */ + public HashAlgorithm getHashAlgorithm() { + return hashAlg; + } + + /** + * Set the extra field data in the local file data - without Header-ID or + * length specifier. + * + * @param data + * the field data to use + */ + public void setLocalFileDataData(byte[] data) { + localData = ZipUtil.copy(data); + } + + /** + * Get the length of the local data. + * + * @return the length of the local data + */ + public ZipShort getLocalFileDataLength() { + return new ZipShort(localData != null ? localData.length : 0); + } + + /** + * Get the local data. + * + * @return the local data + */ + public byte[] getLocalFileDataData() { + return ZipUtil.copy(localData); + } + + /** + * Extra field data in central directory - without Header-ID or length + * specifier. + */ + private byte[] centralData; + + /** + * Set the extra field data in central directory. + * + * @param data + * the data to use + */ + public void setCentralDirectoryData(byte[] data) { + centralData = ZipUtil.copy(data); + } + + /** + * Get the central data length. If there is no central data, get the local + * file data length. + * + * @return the central data length + */ + public ZipShort getCentralDirectoryLength() { + if (centralData != null) { + return new ZipShort(centralData.length); + } + return getLocalFileDataLength(); + } + + /** + * Get the central data. + * + * @return the central data if present, else return the local file data + */ + public byte[] getCentralDirectoryData() { + if (centralData != null) { + return ZipUtil.copy(centralData); + } + return getLocalFileDataData(); + } + + /** + * This should never be called for this header type. + * + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromLocalFileData(byte[], int, int) + */ + public void parseFromLocalFileData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setLocalFileDataData(tmp); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) + */ + public void parseFromCentralDirectoryData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setCentralDirectoryData(tmp); + + this.rcount = ZipShort.getValue(data, offset); + this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 2)); + } +} http://git-wip-us.apache.org/repos/asf/commons-compress/blob/a433f625/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java b/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java new file mode 100644 index 0000000..ca9c829 --- /dev/null +++ b/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java @@ -0,0 +1,482 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.compress.archivers.zip; + +/** + * Strong Encryption Header (0x0017) + * + * Certificate-based encryption: + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * 0x0017 2 bytes Tag for this "extra" block type + * TSize 2 bytes Size of data that follows + * Format 2 bytes Format definition for this record + * AlgID 2 bytes Encryption algorithm identifier + * Bitlen 2 bytes Bit length of encryption key (32-448 bits) + * Flags 2 bytes Processing flags + * RCount 4 bytes Number of recipients. + * HashAlg 2 bytes Hash algorithm identifier + * HSize 2 bytes Hash size + * SRList (var) Simple list of recipients hashed public keys + * + * Flags - This defines the processing flags. + * + * <ul> + * <li>0x0007 - reserved for future use + * <li>0x000F - reserved for future use + * <li>0x0100 - Indicates non-OAEP key wrapping was used. If this + * this field is set, the version needed to extract must + * be at least 61. This means OAEP key wrapping is not + * used when generating a Master Session Key using + * ErdData. + * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the + * same algorithm used for encrypting the file contents. + * <li>0x8000 - reserved for future use + * </ul> + * + * RCount - This defines the number intended recipients whose + * public keys were used for encryption. This identifies + * the number of elements in the SRList. + * + * see also: reserved1 + * + * HashAlg - This defines the hash algorithm used to calculate + * the public key hash of each public key used + * for encryption. This field currently supports + * only the following value for SHA-1 + * + * 0x8004 - SHA1 + * + * HSize - This defines the size of a hashed public key. + * + * SRList - This is a variable length list of the hashed + * public keys for each intended recipient. Each + * element in this list is HSize. The total size of + * SRList is determined using RCount * HSize. + * </pre> + * + * Password-based Extra Field 0x0017 in central header only. + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * 0x0017 2 bytes Tag for this "extra" block type + * TSize 2 bytes Size of data that follows + * Format 2 bytes Format definition for this record + * AlgID 2 bytes Encryption algorithm identifier + * Bitlen 2 bytes Bit length of encryption key (32-448 bits) + * Flags 2 bytes Processing flags + * (more?) + * </pre> + * + * <b>Format</b> - the data format identifier for this record. The only value + * allowed at this time is the integer value 2. + * + * Password-based Extra Field 0x0017 preceding compressed file data. + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * 0x0017 2 bytes Tag for this "extra" block type + * IVSize 2 bytes Size of initialization vector (IV) + * IVData IVSize Initialization vector for this file + * Size 4 bytes Size of remaining decryption header data + * Format 2 bytes Format definition for this record + * AlgID 2 bytes Encryption algorithm identifier + * Bitlen 2 bytes Bit length of encryption key (32-448 bits) + * Flags 2 bytes Processing flags + * ErdSize 2 bytes Size of Encrypted Random Data + * ErdData ErdSize Encrypted Random Data + * Reserved1 4 bytes Reserved certificate processing data + * Reserved2 (var) Reserved for certificate processing data + * VSize 2 bytes Size of password validation data + * VData VSize-4 Password validation data + * VCRC32 4 bytes Standard ZIP CRC32 of password validation data + * + * IVData - The size of the IV should match the algorithm block size. + * The IVData can be completely random data. If the size of + * the randomly generated data does not match the block size + * it should be complemented with zero's or truncated as + * necessary. If IVSize is 0,then IV = CRC32 + Uncompressed + * File Size (as a 64 bit little-endian, unsigned integer value). + * + * Format - the data format identifier for this record. The only + * value allowed at this time is the integer value 2. + * + * ErdData - Encrypted random data is used to store random data that + * is used to generate a file session key for encrypting + * each file. SHA1 is used to calculate hash data used to + * derive keys. File session keys are derived from a master + * session key generated from the user-supplied password. + * If the Flags field in the decryption header contains + * the value 0x4000, then the ErdData field must be + * decrypted using 3DES. If the value 0x4000 is not set, + * then the ErdData field must be decrypted using AlgId. + * + * Reserved1 - Reserved for certificate processing, if value is + * zero, then Reserved2 data is absent. See the explanation + * under the Certificate Processing Method for details on + * this data structure. + * + * Reserved2 - If present, the size of the Reserved2 data structure + * is located by skipping the first 4 bytes of this field + * and using the next 2 bytes as the remaining size. See + * the explanation under the Certificate Processing Method + * for details on this data structure. + * + * VSize - This size value will always include the 4 bytes of the + * VCRC32 data and will be greater than 4 bytes. + * + * VData - Random data for password validation. This data is VSize + * in length and VSize must be a multiple of the encryption + * block size. VCRC32 is a checksum value of VData. + * VData and VCRC32 are stored encrypted and start the + * stream of encrypted data for a file. + * </pre> + * + * + * Reserved1 - Certificate Decryption Header Reserved1 Data: + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * RCount 4 bytes Number of recipients. + * </pre> + * + * RCount - This defines the number intended recipients whose public keys were + * used for encryption. This defines the number of elements in the REList field + * defined below. + * + * + * Reserved2 - Certificate Decryption Header Reserved2 Data Structures: + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * HashAlg 2 bytes Hash algorithm identifier + * HSize 2 bytes Hash size + * REList (var) List of recipient data elements + * + * HashAlg - This defines the hash algorithm used to calculate + * the public key hash of each public key used + * for encryption. This field currently supports + * only the following value for SHA-1 + * + * 0x8004 - SHA1 + * + * HSize - This defines the size of a hashed public key + * defined in REHData. + * + * REList - This is a variable length of list of recipient data. + * Each element in this list consists of a Recipient + * Element data structure as follows: + * </pre> + * + * Recipient Element (REList) Data Structure: + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * RESize 2 bytes Size of REHData + REKData + * REHData HSize Hash of recipients public key + * REKData (var) Simple key blob + * + * + * RESize - This defines the size of an individual REList + * element. This value is the combined size of the + * REHData field + REKData field. REHData is defined by + * HSize. REKData is variable and can be calculated + * for each REList element using RESize and HSize. + * + * REHData - Hashed public key for this recipient. + * + * REKData - Simple Key Blob. The format of this data structure + * is identical to that defined in the Microsoft + * CryptoAPI and generated using the CryptExportKey() + * function. The version of the Simple Key Blob + * supported at this time is 0x02 as defined by + * Microsoft. + * + * For more details see https://msdn.microsoft.com/en-us/library/aa920051.aspx + * </pre> + * + * <b>Flags</b> - Processing flags needed for decryption + * + * <ul> + * <li>0x0001 - Password is required to decrypt</li> + * <li>0x0002 - Certificates only</li> + * <li>0x0003 - Password or certificate required to decrypt</li> + * <li>0x0007 - reserved for future use + * <li>0x000F - reserved for future use + * <li>0x0100 - indicates non-OAEP key wrapping was used. If this field is set + * the version needed to extract must be at least 61. This means OAEP key + * wrapping is not used when generating a Master Session Key using ErdData. + * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the same + * algorithm used for encrypting the file contents. + * <li>0x8000 - reserved for future use. + * </ul> + * + * <b>See the section describing the Strong Encryption Specification for + * details. Refer to the section in this document entitled + * "Incorporating PKWARE Proprietary Technology into Your Product" for more + * information.</b> + * + * @NotThreadSafe + */ +public class X0017_StrongEncryptionHeader extends PKWareExtraHeader implements ZipExtraField { + private static final ZipShort HEADER_ID = new ZipShort(0x0017); + private static final long serialVersionUID = 1L; + + /** + * Get the header id. + * + * @return the header id + */ + public ZipShort getHeaderId() { + return HEADER_ID; + } + + /** + * Extra field data in local file data - without Header-ID or length + * specifier. + */ + private byte[] localData; + + private int format; + private EncryptionAlgorithm algId; + private int bitlen; + private int flags; + private long rcount; + private HashAlgorithm hashAlg; + private int hashSize; + + // encryption data + private byte ivData[]; + private byte erdData[]; + + // encryption key + private byte recipientKeyHash[]; + private byte keyBlob[]; + + // password verification data + private byte vData[]; + private byte vCRC32[]; + + /** + * Get record count. + * @return + */ + public long getRecordCount() { + return rcount; + } + + /** + * Get hash algorithm. + * @return + */ + public HashAlgorithm getHashAlgorithm() { + return hashAlg; + } + + /** + * Get encryption algorithm. + * @return + */ + public EncryptionAlgorithm getEncryptionAlgorithm() { + return algId; + } + + /** + * Set the extra field data in the local file data - without Header-ID or + * length specifier. + * + * @param data + * the field data to use + */ + public void setLocalFileDataData(byte[] data) { + localData = ZipUtil.copy(data); + } + + /** + * Get the length of the local data. + * + * @return the length of the local data + */ + public ZipShort getLocalFileDataLength() { + return new ZipShort(localData != null ? localData.length : 0); + } + + /** + * Get the local data. + * + * @return the local data + */ + public byte[] getLocalFileDataData() { + return ZipUtil.copy(localData); + } + + /** + * Extra field data in central directory - without Header-ID or length + * specifier. + */ + private byte[] centralData; + + /** + * Set the extra field data in central directory. + * + * @param data + * the data to use + */ + public void setCentralDirectoryData(byte[] data) { + centralData = ZipUtil.copy(data); + } + + /** + * Get the central data length. If there is no central data, get the local + * file data length. + * + * @return the central data length + */ + public ZipShort getCentralDirectoryLength() { + if (centralData != null) { + return new ZipShort(centralData.length); + } + return getLocalFileDataLength(); + } + + /** + * Get the central data. + * + * @return the central data if present, else return the local file data + */ + public byte[] getCentralDirectoryData() { + if (centralData != null) { + return ZipUtil.copy(centralData); + } + return getLocalFileDataData(); + } + + /** + * Parse central directory format. + * + * @param data + * @param offset + * @param length + */ + public void parseCentralDirectoryFormat(byte[] data, int offset, int length) { + this.format = ZipShort.getValue(data, offset); + this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 2)); + this.bitlen = ZipShort.getValue(data, offset + 4); + this.flags = ZipShort.getValue(data, offset + 6); + this.rcount = ZipLong.getValue(data, offset + 8); + + if (rcount > 0) { + this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 12)); + this.hashSize = ZipShort.getValue(data, offset + 14); + // srlist... hashed public keys + for (int i = 0; i < this.rcount; i++) { + for (int j = 0; j < this.hashSize; j++) { + // ZipUtil.signedByteToUnsignedInt(data[offset + 16 + (i * this.hashSize) + j])); + } + } + } + } + + /** + * Parse file header format. (Password only?) + * + * @param data + * @param offset + * @param length + */ + public void parseFileFormat(byte[] data, int offset, int length) { + int ivSize = ZipShort.getValue(data, offset); + this.ivData = new byte[ivSize]; + System.arraycopy(data, offset + 4, this.ivData, 0, ivSize); + + long size = ZipLong.getValue(data, offset + ivSize + 2); + this.format = ZipShort.getValue(data, offset + ivSize + 6); + this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 8)); + this.bitlen = ZipShort.getValue(data, offset + ivSize + 10); + this.flags = ZipShort.getValue(data, offset + ivSize + 12); + + int erdSize = ZipShort.getValue(data, offset + ivSize + 14); + this.erdData = new byte[erdSize]; + System.arraycopy(data, offset + ivSize + 16, this.erdData, 0, erdSize); + + this.rcount = ZipLong.getValue(data, offset + ivSize + 16 + erdSize); + System.out.println("rcount: " + rcount); + if (rcount == 0) { + int vSize = ZipShort.getValue(data, offset + ivSize + 20 + erdSize); + this.vData = new byte[vSize - 4]; + this.vCRC32 = new byte[4]; + System.arraycopy(data, offset + ivSize + 22 + erdSize , this.vData, 0, vSize - 4); + System.arraycopy(data, offset + ivSize + 22 + erdSize + vSize - 4, vCRC32, 0, 4); + } else { + this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 20 + erdSize)); + this.hashSize = ZipShort.getValue(data, offset + ivSize + 22 + erdSize); + int resize = ZipShort.getValue(data, offset + ivSize + 24 + erdSize); + this.recipientKeyHash = new byte[this.hashSize]; + this.keyBlob = new byte[resize - this.hashSize]; + System.arraycopy(data, offset + ivSize + 24 + erdSize, this.recipientKeyHash, 0, this.hashSize); + System.arraycopy(data, offset + ivSize + 24 + erdSize + this.hashSize, this.keyBlob, 0, resize - this.hashSize); + + int vSize = ZipShort.getValue(data, offset + ivSize + 26 + erdSize + resize); + this.vData = new byte[vSize - 4]; + this.vCRC32 = new byte[4]; + System.arraycopy(data, offset + ivSize + 22 + erdSize + resize, this.vData, 0, vSize - 4); + System.arraycopy(data, offset + ivSize + 22 + erdSize + resize + vSize - 4, vCRC32, 0, 4); + } + + // validate values? + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromLocalFileData(byte[], int, int) + */ + public void parseFromLocalFileData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + parseFileFormat(data, offset, length); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) + */ + public void parseFromCentralDirectoryData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setCentralDirectoryData(tmp); + parseCentralDirectoryFormat(data, offset, length); + } +} http://git-wip-us.apache.org/repos/asf/commons-compress/blob/a433f625/src/main/java/org/apache/commons/compress/archivers/zip/X0019_EncryptionRecipientCertificateList.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/X0019_EncryptionRecipientCertificateList.java b/src/main/java/org/apache/commons/compress/archivers/zip/X0019_EncryptionRecipientCertificateList.java new file mode 100644 index 0000000..2fad277 --- /dev/null +++ b/src/main/java/org/apache/commons/compress/archivers/zip/X0019_EncryptionRecipientCertificateList.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.compress.archivers.zip; + +/** + * PKCS#7 Encryption Recipient Certificate List (0x0019) + * + * This field MAY contain information about each of the certificates used in + * encryption processing and it can be used to identify who is allowed to + * decrypt encrypted files. This field should only appear in the archive extra + * data record. This field is not required and serves only to aid archive + * modifications by preserving public encryption key data. Individual security + * requirements may dictate that this data be omitted to deter information + * exposure. + * + * Note: all fields stored in Intel low-byte/high-byte order. + * + * <pre> + * Value Size Description + * ----- ---- ----------- + * (CStore) 0x0019 2 bytes Tag for this "extra" block type + * TSize 2 bytes Size of the store data + * Version 2 bytes Format version number - must 0x0001 at this time + * CStore (var) PKCS#7 data blob + * </pre> + * + * <b>See the section describing the Strong Encryption Specification for + * details. Refer to the section in this document entitled + * "Incorporating PKWARE Proprietary Technology into Your Product" for more + * information.</b> + * + * @NotThreadSafe + */ +public class X0019_EncryptionRecipientCertificateList extends PKWareExtraHeader implements ZipExtraField { + private static final ZipShort HEADER_ID = new ZipShort(0x0019); + private static final long serialVersionUID = 1L; + + /** + * Get the header id. + * + * @return the header id + */ + public ZipShort getHeaderId() { + return HEADER_ID; + } + + /** + * Extra field data in local file data - without Header-ID or length + * specifier. + */ + private byte[] localData; + + /** + * Set the extra field data in the local file data - without Header-ID or + * length specifier. + * + * @param data + * the field data to use + */ + public void setLocalFileDataData(byte[] data) { + localData = ZipUtil.copy(data); + } + + /** + * Get the length of the local data. + * + * @return the length of the local data + */ + public ZipShort getLocalFileDataLength() { + return new ZipShort(localData != null ? localData.length : 0); + } + + /** + * Get the local data. + * + * @return the local data + */ + public byte[] getLocalFileDataData() { + return ZipUtil.copy(localData); + } + + /** + * Extra field data in central directory - without Header-ID or length + * specifier. + */ + private byte[] centralData; + + /** + * Set the extra field data in central directory. + * + * @param data + * the data to use + */ + public void setCentralDirectoryData(byte[] data) { + centralData = ZipUtil.copy(data); + } + + /** + * Get the central data length. If there is no central data, get the local + * file data length. + * + * @return the central data length + */ + public ZipShort getCentralDirectoryLength() { + if (centralData != null) { + return new ZipShort(centralData.length); + } + return getLocalFileDataLength(); + } + + /** + * Get the central data. + * + * @return the central data if present, else return the local file data + */ + public byte[] getCentralDirectoryData() { + if (centralData != null) { + return ZipUtil.copy(centralData); + } + return getLocalFileDataData(); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromLocalFileData(byte[], int, int) + */ + public void parseFromLocalFileData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setLocalFileDataData(tmp); + } + + /** + * @param data + * the array of bytes. + * @param offset + * the source location in the data array. + * @param length + * the number of bytes to use in the data array. + * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) + */ + public void parseFromCentralDirectoryData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setCentralDirectoryData(tmp); + if (localData == null) { + setLocalFileDataData(tmp); + } + } +}
