Added some documentation to the crypto functions and adapted xor1verifier code to the OFFCrypto-Docs git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1622577 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_11_BETA3
@@ -17,6 +17,7 @@ | |||
package org.apache.poi.hssf.record; | |||
import org.apache.poi.poifs.crypt.CryptoFunctions; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
@@ -39,23 +40,13 @@ public final class PasswordRecord extends StandardRecord { | |||
field_1_password = in.readShort(); | |||
} | |||
//this is the world's lamest "security". thanks to Wouter van Vugt for making me | |||
//not have to try real hard. -ACO | |||
/** | |||
* Return the password hash | |||
* | |||
* @deprecated use {@link CryptoFunctions#createXorVerifier1(String)} | |||
*/ | |||
public static short hashPassword(String password) { | |||
byte[] passwordCharacters = password.getBytes(); | |||
int hash = 0; | |||
if (passwordCharacters.length > 0) { | |||
int charIndex = passwordCharacters.length; | |||
while (charIndex-- > 0) { | |||
hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); | |||
hash ^= passwordCharacters[charIndex]; | |||
} | |||
// also hash with charcount | |||
hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); | |||
hash ^= passwordCharacters.length; | |||
hash ^= (0x8000 | ('N' << 8) | 'K'); | |||
} | |||
return (short)hash; | |||
return (short)CryptoFunctions.createXorVerifier1(password); | |||
} | |||
/** |
@@ -24,6 +24,7 @@ import org.apache.poi.hssf.record.ProtectRecord; | |||
import org.apache.poi.hssf.record.Record; | |||
import org.apache.poi.hssf.record.RecordFormatException; | |||
import org.apache.poi.hssf.record.ScenarioProtectRecord; | |||
import org.apache.poi.poifs.crypt.CryptoFunctions; | |||
/** | |||
* Groups the sheet protection records for a worksheet. | |||
@@ -186,7 +187,7 @@ public final class WorksheetProtectionBlock extends RecordAggregate { | |||
ProtectRecord prec = getProtect(); | |||
PasswordRecord pass = getPassword(); | |||
prec.setProtect(true); | |||
pass.setPassword(PasswordRecord.hashPassword(password)); | |||
pass.setPassword((short)CryptoFunctions.createXorVerifier1(password)); | |||
if (_objectProtectRecord == null && shouldProtectObjects) { | |||
ObjectProtectRecord rec = createObjectProtect(); | |||
rec.setProtect(true); |
@@ -180,14 +180,20 @@ public class CryptoFunctions { | |||
} | |||
/** | |||
* | |||
* Initialize a new cipher object with the given cipher properties | |||
* If the given algorithm is not implemented in the JCE, it will try to load it from the bouncy castle | |||
* provider. | |||
* | |||
* @param key | |||
* @param chain | |||
* @param vec | |||
* @param key the secrect key | |||
* @param cipherAlgorithm the cipher algorithm | |||
* @param chain the chaining mode | |||
* @param vec the initialization vector (IV), can be null | |||
* @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE | |||
* @param padding | |||
* @return the requested cipher | |||
* @throws GeneralSecurityException | |||
* @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified, | |||
* which depends on a missing bouncy castle provider | |||
*/ | |||
public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) { | |||
int keySizeInBytes = key.getEncoded().length; | |||
@@ -226,10 +232,26 @@ public class CryptoFunctions { | |||
} | |||
} | |||
/** | |||
* Returns a new byte array with a truncated to the given size. | |||
* If the hash has less then size bytes, it will be filled with 0x36-bytes | |||
* | |||
* @param hash the to be truncated/filled hash byte array | |||
* @param size the size of the returned byte array | |||
* @return the padded hash | |||
*/ | |||
public static byte[] getBlock36(byte[] hash, int size) { | |||
return getBlockX(hash, size, (byte)0x36); | |||
} | |||
/** | |||
* Returns a new byte array with a truncated to the given size. | |||
* If the hash has less then size bytes, it will be filled with 0-bytes | |||
* | |||
* @param hash the to be truncated/filled hash byte array | |||
* @param size the size of the returned byte array | |||
* @return the padded hash | |||
*/ | |||
public static byte[] getBlock0(byte[] hash, int size) { | |||
return getBlockX(hash, size, (byte)0); | |||
} | |||
@@ -331,11 +353,11 @@ public class CryptoFunctions { | |||
byte[] generatedKey = new byte[4]; | |||
//Maximum length of the password is 15 chars. | |||
final int intMaxPasswordLength = 15; | |||
final int maxPasswordLength = 15; | |||
if (!"".equals(password)) { | |||
// Truncate the password to 15 characters | |||
password = password.substring(0, Math.min(password.length(), intMaxPasswordLength)); | |||
password = password.substring(0, Math.min(password.length(), maxPasswordLength)); | |||
// Construct a new NULL-terminated string consisting of single-byte characters: | |||
// -- > Get the single-byte values by iterating through the Unicode characters of the truncated Password. | |||
@@ -359,7 +381,7 @@ public class CryptoFunctions { | |||
// the most significant, if the bit is set, XOR the keys high-order word with the corresponding word from | |||
// the Encryption Matrix | |||
for (int i = 0; i < arrByteChars.length; i++) { | |||
int tmp = intMaxPasswordLength - arrByteChars.length + i; | |||
int tmp = maxPasswordLength - arrByteChars.length + i; | |||
for (int intBit = 0; intBit < 7; intBit++) { | |||
if ((arrByteChars[i] & (0x0001 << intBit)) != 0) { | |||
highOrderWord ^= EncryptionMatrix[tmp][intBit]; | |||
@@ -369,22 +391,28 @@ public class CryptoFunctions { | |||
// Compute the low-order word of the new key: | |||
// Initialize with 0 | |||
int lowOrderWord = 0; | |||
// SET Verifier TO 0x0000 | |||
short verifier = 0; | |||
// For each character in the password, going backwards | |||
for (int i = arrByteChars.length - 1; i >= 0; i--) { | |||
// low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character | |||
lowOrderWord = (((lowOrderWord >> 14) & 0x0001) | ((lowOrderWord << 1) & 0x7FFF)) ^ arrByteChars[i]; | |||
// FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER | |||
for (int i = arrByteChars.length-1; i >= 0; i--) { | |||
// SET Verifier TO Intermediate3 BITWISE XOR PasswordByte | |||
verifier = rotateLeftBase15Bit(verifier); | |||
verifier ^= arrByteChars[i]; | |||
} | |||
// Lastly,low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR password length XOR 0xCE4B. | |||
lowOrderWord = (((lowOrderWord >> 14) & 0x0001) | ((lowOrderWord << 1) & 0x7FFF)) ^ arrByteChars.length ^ 0xCE4B; | |||
// as we haven't prepended the password length into the input array | |||
// we need to do it now separately ... | |||
verifier = rotateLeftBase15Bit(verifier); | |||
verifier ^= arrByteChars.length; | |||
// RETURN Verifier BITWISE XOR 0xCE4B | |||
verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K') | |||
// The byte order of the result shall be reversed [password "Example": 0x64CEED7E becomes 7EEDCE64], | |||
// and that value shall be hashed as defined by the attribute values. | |||
LittleEndian.putShort(generatedKey, 0, (short)lowOrderWord); | |||
LittleEndian.putShort(generatedKey, 0, verifier); | |||
LittleEndian.putShort(generatedKey, 2, (short)highOrderWord); | |||
} | |||
@@ -421,7 +449,7 @@ public class CryptoFunctions { | |||
* @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a> | |||
* | |||
* @param password the password | |||
* @return the verifier | |||
* @return the verifier (actually a short value) | |||
*/ | |||
public static int createXorVerifier1(String password) { | |||
// the verifier for method 1 is part of the verifier for method 2 | |||
@@ -480,4 +508,25 @@ public class CryptoFunctions { | |||
private static byte rotateLeft(byte bits, int shift) { | |||
return (byte)(((bits & 0xff) << shift) | ((bits & 0xff) >>> (8 - shift))); | |||
} | |||
private static short rotateLeftBase15Bit(short verifier) { | |||
/* | |||
* IF (Verifier BITWISE AND 0x4000) is 0x0000 | |||
* SET Intermediate1 TO 0 | |||
* ELSE | |||
* SET Intermediate1 TO 1 | |||
* ENDIF | |||
*/ | |||
short intermediate1 = (short)(((verifier & 0x4000) == 0) ? 0 : 1); | |||
/* | |||
* SET Intermediate2 TO Verifier MULTIPLED BY 2 | |||
* SET most significant bit of Intermediate2 TO 0 | |||
*/ | |||
short intermediate2 = (short)((verifier<<1) & 0x7FFF); | |||
/* | |||
* SET Intermediate3 TO Intermediate1 BITWISE OR Intermediate2 | |||
*/ | |||
short intermediate3 = (short)(intermediate1 | intermediate2); | |||
return intermediate3; | |||
} | |||
} |
@@ -64,4 +64,11 @@ public enum HashAlgorithm { | |||
} | |||
throw new EncryptedDocumentException("hash algorithm not found"); | |||
} | |||
public static HashAlgorithm fromString(String string) { | |||
for (HashAlgorithm ha : values()) { | |||
if (ha.ecmaString.equalsIgnoreCase(string) || ha.jceId.equalsIgnoreCase(string)) return ha; | |||
} | |||
throw new EncryptedDocumentException("hash algorithm not found"); | |||
} | |||
} |
@@ -17,6 +17,9 @@ | |||
package org.apache.poi.xssf.usermodel; | |||
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.setPassword; | |||
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.validatePassword; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
@@ -33,7 +36,6 @@ import javax.xml.namespace.QName; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.POIXMLException; | |||
import org.apache.poi.hssf.record.PasswordRecord; | |||
import org.apache.poi.hssf.util.PaneInformation; | |||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
import org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException; | |||
@@ -41,6 +43,7 @@ import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.openxml4j.opc.PackageRelationship; | |||
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; | |||
import org.apache.poi.openxml4j.opc.TargetMode; | |||
import org.apache.poi.poifs.crypt.HashAlgorithm; | |||
import org.apache.poi.ss.SpreadsheetVersion; | |||
import org.apache.poi.ss.formula.FormulaShifter; | |||
import org.apache.poi.ss.formula.SheetNameFormatter; | |||
@@ -52,7 +55,6 @@ import org.apache.poi.ss.util.CellReference; | |||
import org.apache.poi.ss.util.SSCellRange; | |||
import org.apache.poi.ss.util.SheetUtil; | |||
import org.apache.poi.util.Beta; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
@@ -1056,7 +1058,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { | |||
*/ | |||
@Override | |||
public boolean getProtect() { | |||
return worksheet.isSetSheetProtection() && sheetProtectionEnabled(); | |||
return isSheetLocked(); | |||
} | |||
/** | |||
@@ -1068,10 +1070,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { | |||
*/ | |||
@Override | |||
public void protectSheet(String password) { | |||
if(password != null) { | |||
CTSheetProtection sheetProtection = worksheet.addNewSheetProtection(); | |||
sheetProtection.xsetPassword(stringToExcelPassword(password)); | |||
if (password != null) { | |||
CTSheetProtection sheetProtection = safeGetProtectionField(); | |||
setSheetPassword(password, null); // defaults to xor password | |||
sheetProtection.setSheet(true); | |||
sheetProtection.setScenarios(true); | |||
sheetProtection.setObjects(true); | |||
@@ -1081,18 +1082,27 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { | |||
} | |||
/** | |||
* Converts a String to a {@link STUnsignedShortHex} value that contains the {@link PasswordRecord#hashPassword(String)} | |||
* value in hexadecimal format | |||
* | |||
* @param password the password string you wish convert to an {@link STUnsignedShortHex} | |||
* @return {@link STUnsignedShortHex} that contains Excel hashed password in Hex format | |||
* Sets the sheet password. | |||
* | |||
* @param password if null, the password will be removed | |||
* @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier) | |||
* otherwise the given algorithm is used for calculating the hash password (Excel 2013) | |||
*/ | |||
private STUnsignedShortHex stringToExcelPassword(String password) { | |||
STUnsignedShortHex hexPassword = STUnsignedShortHex.Factory.newInstance(); | |||
hexPassword.setStringValue(String.valueOf(HexDump.shortToHex(PasswordRecord.hashPassword(password))).substring(2)); | |||
return hexPassword; | |||
public void setSheetPassword(String password, HashAlgorithm hashAlgo) { | |||
if (password == null && !isSheetProtectionEnabled()) return; | |||
setPassword(safeGetProtectionField(), password, hashAlgo, null); | |||
} | |||
/** | |||
* Validate the password against the stored hash, the hashing method will be determined | |||
* by the existing password attributes | |||
* @return true, if the hashes match (... though original password may differ ...) | |||
*/ | |||
public boolean validateSheetPassword(String password) { | |||
if (!isSheetProtectionEnabled()) return (password == null); | |||
return validatePassword(safeGetProtectionField(), password, null); | |||
} | |||
/** | |||
* Returns the logical row ( 0-based). If you ask for a row that is not | |||
* defined you get a null. This is to say row 4 represents the fifth row on a sheet. | |||
@@ -1546,7 +1556,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { | |||
worksheet.unsetMergeCells(); | |||
} | |||
} | |||
/** | |||
* Removes a number of merged regions of cells (hence letting them free) | |||
* | |||
@@ -2910,304 +2920,440 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { | |||
* @return true when Autofilters are locked and the sheet is protected. | |||
*/ | |||
public boolean isAutoFilterLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getAutoFilter(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getAutoFilter(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Deleting columns is locked and the sheet is protected. | |||
*/ | |||
public boolean isDeleteColumnsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getDeleteColumns(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getDeleteColumns(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Deleting rows is locked and the sheet is protected. | |||
*/ | |||
public boolean isDeleteRowsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getDeleteRows(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getDeleteRows(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Formatting cells is locked and the sheet is protected. | |||
*/ | |||
public boolean isFormatCellsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getFormatCells(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getFormatCells(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Formatting columns is locked and the sheet is protected. | |||
*/ | |||
public boolean isFormatColumnsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getFormatColumns(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getFormatColumns(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Formatting rows is locked and the sheet is protected. | |||
*/ | |||
public boolean isFormatRowsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getFormatRows(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getFormatRows(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Inserting columns is locked and the sheet is protected. | |||
*/ | |||
public boolean isInsertColumnsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getInsertColumns(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getInsertColumns(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Inserting hyperlinks is locked and the sheet is protected. | |||
*/ | |||
public boolean isInsertHyperlinksLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getInsertHyperlinks(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getInsertHyperlinks(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Inserting rows is locked and the sheet is protected. | |||
*/ | |||
public boolean isInsertRowsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getInsertRows(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getInsertRows(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Pivot tables are locked and the sheet is protected. | |||
*/ | |||
public boolean isPivotTablesLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getPivotTables(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getPivotTables(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Sorting is locked and the sheet is protected. | |||
*/ | |||
public boolean isSortLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getSort(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getSort(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Objects are locked and the sheet is protected. | |||
*/ | |||
public boolean isObjectsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getObjects(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getObjects(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Scenarios are locked and the sheet is protected. | |||
*/ | |||
public boolean isScenariosLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getScenarios(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getScenarios(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Selection of locked cells is locked and the sheet is protected. | |||
*/ | |||
public boolean isSelectLockedCellsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getSelectLockedCells(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getSelectLockedCells(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Selection of unlocked cells is locked and the sheet is protected. | |||
*/ | |||
public boolean isSelectUnlockedCellsLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getSelectUnlockedCells(); | |||
if (isSheetLocked()) { | |||
return safeGetProtectionField().getSelectUnlockedCells(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true when Sheet is Protected. | |||
*/ | |||
public boolean isSheetLocked() { | |||
createProtectionFieldIfNotPresent(); | |||
return sheetProtectionEnabled() && worksheet.getSheetProtection().getSheet(); | |||
if (worksheet.isSetSheetProtection()) { | |||
return safeGetProtectionField().getSheet(); | |||
} | |||
return false; | |||
} | |||
/** | |||
* Enable sheet protection | |||
*/ | |||
public void enableLocking() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setSheet(true); | |||
safeGetProtectionField().setSheet(true); | |||
} | |||
/** | |||
* Disable sheet protection | |||
*/ | |||
public void disableLocking() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setSheet(false); | |||
safeGetProtectionField().setSheet(false); | |||
} | |||
/** | |||
* Enable Autofilters locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* @deprecated use {@link #lockAutoFilter(boolean)} | |||
*/ | |||
public void lockAutoFilter() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setAutoFilter(true); | |||
lockAutoFilter(true); | |||
} | |||
/** | |||
* Enable Deleting columns locking. | |||
* Enable or disable Autofilters locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockAutoFilter(boolean enabled) { | |||
safeGetProtectionField().setAutoFilter(enabled); | |||
} | |||
/** | |||
* Enable Deleting columns locking. | |||
* @deprecated use {@link #lockDeleteColumns(boolean)} | |||
*/ | |||
public void lockDeleteColumns() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setDeleteColumns(true); | |||
lockDeleteColumns(true); | |||
} | |||
/** | |||
* Enable Deleting rows locking. | |||
* Enable or disable Deleting columns locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockDeleteColumns(boolean enabled) { | |||
safeGetProtectionField().setDeleteColumns(enabled); | |||
} | |||
/** | |||
* Enable Deleting rows locking. | |||
* @deprecated use {@link #lockDeleteRows(boolean)} | |||
*/ | |||
public void lockDeleteRows() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setDeleteRows(true); | |||
lockDeleteRows(true); | |||
} | |||
/** | |||
* Enable Formatting cells locking. | |||
* Enable or disable Deleting rows locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockDeleteRows(boolean enabled) { | |||
safeGetProtectionField().setDeleteRows(enabled); | |||
} | |||
/** | |||
* Enable Formatting cells locking. | |||
* @deprecated use {@link #lockFormatCells(boolean)} | |||
*/ | |||
public void lockFormatCells() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setDeleteColumns(true); | |||
lockFormatCells(true); | |||
} | |||
/** | |||
* Enable Formatting columns locking. | |||
* Enable or disable Formatting cells locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockFormatCells(boolean enabled) { | |||
safeGetProtectionField().setFormatCells(enabled); | |||
} | |||
/** | |||
* Enable Formatting columns locking. | |||
* @deprecated use {@link #lockFormatColumns(boolean)} | |||
*/ | |||
public void lockFormatColumns() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setFormatColumns(true); | |||
lockFormatColumns(true); | |||
} | |||
/** | |||
* Enable Formatting rows locking. | |||
* Enable or disable Formatting columns locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockFormatColumns(boolean enabled) { | |||
safeGetProtectionField().setFormatColumns(enabled); | |||
} | |||
/** | |||
* Enable Formatting rows locking. | |||
* @deprecated use {@link #lockFormatRows(boolean)} | |||
*/ | |||
public void lockFormatRows() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setFormatRows(true); | |||
lockFormatRows(true); | |||
} | |||
/** | |||
* Enable Inserting columns locking. | |||
* Enable or disable Formatting rows locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockFormatRows(boolean enabled) { | |||
safeGetProtectionField().setFormatRows(enabled); | |||
} | |||
/** | |||
* Enable Inserting columns locking. | |||
* @deprecated use {@link #lockInsertColumns(boolean)} | |||
*/ | |||
public void lockInsertColumns() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setInsertColumns(true); | |||
lockInsertColumns(true); | |||
} | |||
/** | |||
* Enable Inserting hyperlinks locking. | |||
* Enable or disable Inserting columns locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockInsertColumns(boolean enabled) { | |||
safeGetProtectionField().setInsertColumns(enabled); | |||
} | |||
/** | |||
* Enable Inserting hyperlinks locking. | |||
* @deprecated use {@link #lockInsertHyperlinks(boolean)} | |||
*/ | |||
public void lockInsertHyperlinks() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setInsertHyperlinks(true); | |||
lockInsertHyperlinks(true); | |||
} | |||
/** | |||
* Enable Inserting rows locking. | |||
* Enable or disable Inserting hyperlinks locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockInsertHyperlinks(boolean enabled) { | |||
safeGetProtectionField().setInsertHyperlinks(enabled); | |||
} | |||
/** | |||
* Enable Inserting rows locking. | |||
* @deprecated use {@link #lockInsertRows(boolean)} | |||
*/ | |||
public void lockInsertRows() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setInsertRows(true); | |||
lockInsertRows(true); | |||
} | |||
/** | |||
* Enable Pivot Tables locking. | |||
* Enable or disable Inserting rows locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockInsertRows(boolean enabled) { | |||
safeGetProtectionField().setInsertRows(enabled); | |||
} | |||
/** | |||
* Enable Pivot Tables locking. | |||
* @deprecated use {@link #lockPivotTables(boolean)} | |||
*/ | |||
public void lockPivotTables() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setPivotTables(true); | |||
lockPivotTables(true); | |||
} | |||
/** | |||
* Enable Sort locking. | |||
* Enable or disable Pivot Tables locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockPivotTables(boolean enabled) { | |||
safeGetProtectionField().setPivotTables(enabled); | |||
} | |||
/** | |||
* Enable Sort locking. | |||
* @deprecated use {@link #lockSort(boolean)} | |||
*/ | |||
public void lockSort() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setSort(true); | |||
lockSort(true); | |||
} | |||
/** | |||
* Enable Objects locking. | |||
* Enable or disable Sort locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockSort(boolean enabled) { | |||
safeGetProtectionField().setSort(enabled); | |||
} | |||
/** | |||
* Enable Objects locking. | |||
* @deprecated use {@link #lockObjects(boolean)} | |||
*/ | |||
public void lockObjects() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setObjects(true); | |||
lockObjects(true); | |||
} | |||
/** | |||
* Enable Scenarios locking. | |||
* Enable or disable Objects locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockObjects(boolean enabled) { | |||
safeGetProtectionField().setObjects(enabled); | |||
} | |||
/** | |||
* Enable Scenarios locking. | |||
* @deprecated use {@link #lockScenarios(boolean)} | |||
*/ | |||
public void lockScenarios() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setScenarios(true); | |||
lockScenarios(true); | |||
} | |||
/** | |||
* Enable Selection of locked cells locking. | |||
* Enable or disable Scenarios locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockScenarios(boolean enabled) { | |||
safeGetProtectionField().setScenarios(enabled); | |||
} | |||
/** | |||
* Enable Selection of locked cells locking. | |||
* @deprecated use {@link #lockSelectLockedCells(boolean)} | |||
*/ | |||
public void lockSelectLockedCells() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setSelectLockedCells(true); | |||
lockSelectLockedCells(true); | |||
} | |||
/** | |||
* Enable Selection of unlocked cells locking. | |||
* Enable or disable Selection of locked cells locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this locking, call {@link #enableLocking()} | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockSelectLockedCells(boolean enabled) { | |||
safeGetProtectionField().setSelectLockedCells(enabled); | |||
} | |||
/** | |||
* Enable Selection of unlocked cells locking. | |||
* @deprecated use {@link #lockSelectUnlockedCells(boolean)} | |||
*/ | |||
public void lockSelectUnlockedCells() { | |||
createProtectionFieldIfNotPresent(); | |||
worksheet.getSheetProtection().setSelectUnlockedCells(true); | |||
lockSelectUnlockedCells(true); | |||
} | |||
/** | |||
* Enable or disable Selection of unlocked cells locking. | |||
* This does not modify sheet protection status. | |||
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()} | |||
*/ | |||
public void lockSelectUnlockedCells(boolean enabled) { | |||
safeGetProtectionField().setSelectUnlockedCells(enabled); | |||
} | |||
private void createProtectionFieldIfNotPresent() { | |||
if (worksheet.getSheetProtection() == null) { | |||
worksheet.setSheetProtection(CTSheetProtection.Factory.newInstance()); | |||
private CTSheetProtection safeGetProtectionField() { | |||
if (!isSheetProtectionEnabled()) { | |||
return worksheet.addNewSheetProtection(); | |||
} | |||
return worksheet.getSheetProtection(); | |||
} | |||
private boolean sheetProtectionEnabled() { | |||
return worksheet.getSheetProtection().getSheet(); | |||
/* package */ boolean isSheetProtectionEnabled() { | |||
return (worksheet.isSetSheetProtection()); | |||
} | |||
/* package */ boolean isCellInArrayFormulaContext(XSSFCell cell) { |
@@ -17,6 +17,9 @@ | |||
package org.apache.poi.xssf.usermodel; | |||
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.setPassword; | |||
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.validatePassword; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
@@ -47,6 +50,7 @@ import org.apache.poi.openxml4j.opc.PackageRelationship; | |||
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; | |||
import org.apache.poi.openxml4j.opc.PackagingURIHelper; | |||
import org.apache.poi.openxml4j.opc.TargetMode; | |||
import org.apache.poi.poifs.crypt.HashAlgorithm; | |||
import org.apache.poi.ss.formula.SheetNameFormatter; | |||
import org.apache.poi.ss.formula.udf.IndexedUDFFinder; | |||
import org.apache.poi.ss.formula.udf.UDFFinder; | |||
@@ -1736,60 +1740,108 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X | |||
* Locks the structure of workbook. | |||
*/ | |||
public void lockStructure() { | |||
createProtectionFieldIfNotPresent(); | |||
workbook.getWorkbookProtection().setLockStructure(true); | |||
safeGetWorkbookProtection().setLockStructure(true); | |||
} | |||
/** | |||
* Unlocks the structure of workbook. | |||
*/ | |||
public void unLockStructure() { | |||
createProtectionFieldIfNotPresent(); | |||
workbook.getWorkbookProtection().setLockStructure(false); | |||
safeGetWorkbookProtection().setLockStructure(false); | |||
} | |||
/** | |||
* Locks the windows that comprise the workbook. | |||
*/ | |||
public void lockWindows() { | |||
createProtectionFieldIfNotPresent(); | |||
workbook.getWorkbookProtection().setLockWindows(true); | |||
safeGetWorkbookProtection().setLockWindows(true); | |||
} | |||
/** | |||
* Unlocks the windows that comprise the workbook. | |||
*/ | |||
public void unLockWindows() { | |||
createProtectionFieldIfNotPresent(); | |||
workbook.getWorkbookProtection().setLockWindows(false); | |||
safeGetWorkbookProtection().setLockWindows(false); | |||
} | |||
/** | |||
* Locks the workbook for revisions. | |||
*/ | |||
public void lockRevision() { | |||
createProtectionFieldIfNotPresent(); | |||
workbook.getWorkbookProtection().setLockRevision(true); | |||
safeGetWorkbookProtection().setLockRevision(true); | |||
} | |||
/** | |||
* Unlocks the workbook for revisions. | |||
*/ | |||
public void unLockRevision() { | |||
createProtectionFieldIfNotPresent(); | |||
workbook.getWorkbookProtection().setLockRevision(false); | |||
safeGetWorkbookProtection().setLockRevision(false); | |||
} | |||
private boolean workbookProtectionPresent() { | |||
return workbook.getWorkbookProtection() != null; | |||
/** | |||
* Sets the workbook password. | |||
* | |||
* @param password if null, the password will be removed | |||
* @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier) | |||
* otherwise the given algorithm is used for calculating the hash password (Excel 2013) | |||
*/ | |||
public void setWorkbookPassword(String password, HashAlgorithm hashAlgo) { | |||
if (password == null && !workbookProtectionPresent()) return; | |||
setPassword(safeGetWorkbookProtection(), password, hashAlgo, "workbook"); | |||
} | |||
private void createProtectionFieldIfNotPresent() { | |||
if (workbook.getWorkbookProtection() == null){ | |||
workbook.setWorkbookProtection(CTWorkbookProtection.Factory.newInstance()); | |||
} | |||
/** | |||
* Validate the password against the stored hash, the hashing method will be determined | |||
* by the existing password attributes | |||
* @return true, if the hashes match (... though original password may differ ...) | |||
*/ | |||
public boolean validateWorkbookPassword(String password) { | |||
if (!workbookProtectionPresent()) return (password == null); | |||
return validatePassword(safeGetWorkbookProtection(), password, "workbook"); | |||
} | |||
/** | |||
* Sets the revisions password. | |||
* | |||
* @param password if null, the password will be removed | |||
* @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier) | |||
* otherwise the given algorithm is used for calculating the hash password (Excel 2013) | |||
*/ | |||
public void setRevisionsPassword(String password, HashAlgorithm hashAlgo) { | |||
if (password == null && !workbookProtectionPresent()) return; | |||
setPassword(safeGetWorkbookProtection(), password, hashAlgo, "revisions"); | |||
} | |||
/** | |||
* Validate the password against the stored hash, the hashing method will be determined | |||
* by the existing password attributes | |||
* @return true if the hashes match (... though original password may differ ...) | |||
*/ | |||
public boolean validateRevisionsPassword(String password) { | |||
if (!workbookProtectionPresent()) return (password == null); | |||
return validatePassword(safeGetWorkbookProtection(), password, "revisions"); | |||
} | |||
/** | |||
* Removes the workbook protection settings | |||
*/ | |||
public void unLock() { | |||
if (workbookProtectionPresent()) { | |||
workbook.unsetWorkbookProtection(); | |||
} | |||
} | |||
private boolean workbookProtectionPresent() { | |||
return workbook.isSetWorkbookProtection(); | |||
} | |||
private CTWorkbookProtection safeGetWorkbookProtection() { | |||
if (!workbookProtectionPresent()){ | |||
return workbook.addNewWorkbookProtection(); | |||
} | |||
return workbook.getWorkbookProtection(); | |||
} | |||
/** | |||
* | |||
* Returns the locator of user-defined functions. |
@@ -0,0 +1,128 @@ | |||
/* | |||
* ==================================================================== | |||
* 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.poi.xssf.usermodel.helpers; | |||
import java.security.SecureRandom; | |||
import java.util.Arrays; | |||
import javax.xml.bind.DatatypeConverter; | |||
import javax.xml.namespace.QName; | |||
import org.apache.poi.poifs.crypt.CryptoFunctions; | |||
import org.apache.poi.poifs.crypt.HashAlgorithm; | |||
import org.apache.xmlbeans.XmlCursor; | |||
import org.apache.xmlbeans.XmlObject; | |||
public class XSSFPaswordHelper { | |||
/** | |||
* Sets the XORed or hashed password | |||
* | |||
* @param xobj the xmlbeans object which contains the password attributes | |||
* @param password the password, if null, the password attributes will be removed | |||
* @param hashAlgo the hash algorithm, if null the password will be XORed | |||
* @param prefix the prefix of the password attributes, may be null | |||
*/ | |||
public static void setPassword(XmlObject xobj, String password, HashAlgorithm hashAlgo, String prefix) { | |||
XmlCursor cur = xobj.newCursor(); | |||
if (password == null) { | |||
cur.removeAttribute(getAttrName(prefix, "password")); | |||
cur.removeAttribute(getAttrName(prefix, "algorithmName")); | |||
cur.removeAttribute(getAttrName(prefix, "hashValue")); | |||
cur.removeAttribute(getAttrName(prefix, "saltValue")); | |||
cur.removeAttribute(getAttrName(prefix, "spinCount")); | |||
return; | |||
} | |||
cur.toFirstContentToken(); | |||
if (hashAlgo == null) { | |||
int hash = CryptoFunctions.createXorVerifier1(password); | |||
cur.insertAttributeWithValue(getAttrName(prefix, "password"), Integer.toHexString(hash).toUpperCase()); | |||
} else { | |||
SecureRandom random = new SecureRandom(); | |||
byte salt[] = random.generateSeed(16); | |||
// Iterations specifies the number of times the hashing function shall be iteratively run (using each | |||
// iteration's result as the input for the next iteration). | |||
int spinCount = 100000; | |||
// Implementation Notes List: | |||
// --> In this third stage, the reversed byte order legacy hash from the second stage shall | |||
// be converted to Unicode hex string representation | |||
byte hash[] = CryptoFunctions.hashPassword(password, hashAlgo, salt, spinCount, false); | |||
cur.insertAttributeWithValue(getAttrName(prefix, "algorithmName"), hashAlgo.jceId); | |||
cur.insertAttributeWithValue(getAttrName(prefix, "hashValue"), DatatypeConverter.printBase64Binary(hash)); | |||
cur.insertAttributeWithValue(getAttrName(prefix, "saltValue"), DatatypeConverter.printBase64Binary(salt)); | |||
cur.insertAttributeWithValue(getAttrName(prefix, "spinCount"), ""+spinCount); | |||
} | |||
cur.dispose(); | |||
} | |||
/** | |||
* Validates the password, i.e. | |||
* calculates the hash of the given password and compares it against the stored hash | |||
* | |||
* @param xobj the xmlbeans object which contains the password attributes | |||
* @param password the password, if null the method will always return false, | |||
* even if there's no password set | |||
* @param prefix the prefix of the password attributes, may be null | |||
* | |||
* @return true, if the hashes match | |||
*/ | |||
public static boolean validatePassword(XmlObject xobj, String password, String prefix) { | |||
// TODO: is "velvetSweatshop" the default password? | |||
if (password == null) return false; | |||
XmlCursor cur = xobj.newCursor(); | |||
String xorHashVal = cur.getAttributeText(getAttrName(prefix, "password")); | |||
String algoName = cur.getAttributeText(getAttrName(prefix, "algorithmName")); | |||
String hashVal = cur.getAttributeText(getAttrName(prefix, "hashValue")); | |||
String saltVal = cur.getAttributeText(getAttrName(prefix, "saltValue")); | |||
String spinCount = cur.getAttributeText(getAttrName(prefix, "spinCount")); | |||
cur.dispose(); | |||
if (xorHashVal != null) { | |||
int hash1 = Integer.parseInt(xorHashVal, 16); | |||
int hash2 = CryptoFunctions.createXorVerifier1(password); | |||
return hash1 == hash2; | |||
} else { | |||
if (hashVal == null || algoName == null || saltVal == null || spinCount == null) { | |||
return false; | |||
} | |||
byte hash1[] = DatatypeConverter.parseBase64Binary(hashVal); | |||
HashAlgorithm hashAlgo = HashAlgorithm.fromString(algoName); | |||
byte salt[] = DatatypeConverter.parseBase64Binary(saltVal); | |||
int spinCnt = Integer.parseInt(spinCount); | |||
byte hash2[] = CryptoFunctions.hashPassword(password, hashAlgo, salt, spinCnt, false); | |||
return Arrays.equals(hash1, hash2); | |||
} | |||
} | |||
private static QName getAttrName(String prefix, String name) { | |||
if (prefix == null || "".equals(prefix)) { | |||
return new QName(name); | |||
} else { | |||
return new QName(prefix+Character.toUpperCase(name.charAt(0))+name.substring(1)); | |||
} | |||
} | |||
} |
@@ -16,12 +16,11 @@ | |||
==================================================================== */ | |||
package org.apache.poi.xssf; | |||
import org.apache.poi.xssf.usermodel.XSSFSheet; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.xssf.usermodel.XSSFSheet; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
import junit.framework.TestCase; | |||
public class TestSheetProtection extends TestCase { | |||
private XSSFSheet sheet; | |||
@@ -75,6 +74,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isAutoFilterLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isAutoFilterLocked()); | |||
sheet.lockAutoFilter(false); | |||
assertFalse(sheet.isAutoFilterLocked()); | |||
} | |||
public void testWriteDeleteColumns() throws Exception { | |||
@@ -83,6 +84,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isDeleteColumnsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isDeleteColumnsLocked()); | |||
sheet.lockDeleteColumns(false); | |||
assertFalse(sheet.isDeleteColumnsLocked()); | |||
} | |||
public void testWriteDeleteRows() throws Exception { | |||
@@ -91,6 +94,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isDeleteRowsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isDeleteRowsLocked()); | |||
sheet.lockDeleteRows(false); | |||
assertFalse(sheet.isDeleteRowsLocked()); | |||
} | |||
public void testWriteFormatCells() throws Exception { | |||
@@ -99,6 +104,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isFormatCellsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isFormatCellsLocked()); | |||
sheet.lockFormatCells(false); | |||
assertFalse(sheet.isFormatCellsLocked()); | |||
} | |||
public void testWriteFormatColumns() throws Exception { | |||
@@ -107,6 +114,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isFormatColumnsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isFormatColumnsLocked()); | |||
sheet.lockFormatColumns(false); | |||
assertFalse(sheet.isFormatColumnsLocked()); | |||
} | |||
public void testWriteFormatRows() throws Exception { | |||
@@ -115,6 +124,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isFormatRowsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isFormatRowsLocked()); | |||
sheet.lockFormatRows(false); | |||
assertFalse(sheet.isFormatRowsLocked()); | |||
} | |||
public void testWriteInsertColumns() throws Exception { | |||
@@ -123,6 +134,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isInsertColumnsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isInsertColumnsLocked()); | |||
sheet.lockInsertColumns(false); | |||
assertFalse(sheet.isInsertColumnsLocked()); | |||
} | |||
public void testWriteInsertHyperlinks() throws Exception { | |||
@@ -131,6 +144,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isInsertHyperlinksLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isInsertHyperlinksLocked()); | |||
sheet.lockInsertHyperlinks(false); | |||
assertFalse(sheet.isInsertHyperlinksLocked()); | |||
} | |||
public void testWriteInsertRows() throws Exception { | |||
@@ -139,6 +154,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isInsertRowsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isInsertRowsLocked()); | |||
sheet.lockInsertRows(false); | |||
assertFalse(sheet.isInsertRowsLocked()); | |||
} | |||
public void testWritePivotTables() throws Exception { | |||
@@ -147,6 +164,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isPivotTablesLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isPivotTablesLocked()); | |||
sheet.lockPivotTables(false); | |||
assertFalse(sheet.isPivotTablesLocked()); | |||
} | |||
public void testWriteSort() throws Exception { | |||
@@ -155,6 +174,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isSortLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isSortLocked()); | |||
sheet.lockSort(false); | |||
assertFalse(sheet.isSortLocked()); | |||
} | |||
public void testWriteObjects() throws Exception { | |||
@@ -163,6 +184,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isObjectsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isObjectsLocked()); | |||
sheet.lockObjects(false); | |||
assertFalse(sheet.isObjectsLocked()); | |||
} | |||
public void testWriteScenarios() throws Exception { | |||
@@ -171,6 +194,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isScenariosLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isScenariosLocked()); | |||
sheet.lockScenarios(false); | |||
assertFalse(sheet.isScenariosLocked()); | |||
} | |||
public void testWriteSelectLockedCells() throws Exception { | |||
@@ -179,6 +204,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isSelectLockedCellsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isSelectLockedCellsLocked()); | |||
sheet.lockSelectLockedCells(false); | |||
assertFalse(sheet.isSelectLockedCellsLocked()); | |||
} | |||
public void testWriteSelectUnlockedCells() throws Exception { | |||
@@ -187,6 +214,8 @@ public class TestSheetProtection extends TestCase { | |||
assertFalse(sheet.isSelectUnlockedCellsLocked()); | |||
sheet.enableLocking(); | |||
assertTrue(sheet.isSelectUnlockedCellsLocked()); | |||
sheet.lockSelectUnlockedCells(false); | |||
assertFalse(sheet.isSelectUnlockedCellsLocked()); | |||
} | |||
public void testWriteSelectEnableLocking() throws Exception { |
@@ -16,42 +16,94 @@ | |||
==================================================================== */ | |||
package org.apache.poi.xssf; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.util.TempFile; | |||
import static org.apache.poi.xssf.XSSFTestDataSamples.openSampleWorkbook; | |||
import static org.apache.poi.xssf.XSSFTestDataSamples.writeOutAndReadBack; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertNull; | |||
import static org.junit.Assert.assertTrue; | |||
import org.apache.poi.poifs.crypt.CryptoFunctions; | |||
import org.apache.poi.poifs.crypt.HashAlgorithm; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
public class TestWorkbookProtection extends TestCase { | |||
public void testShouldReadWorkbookProtection() throws Exception { | |||
XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
import org.junit.Test; | |||
public class TestWorkbookProtection { | |||
@Test | |||
public void workbookAndRevisionPassword() throws Exception { | |||
XSSFWorkbook workbook; | |||
String password = "test"; | |||
// validate password with an actual office file (Excel 2010) | |||
workbook = openSampleWorkbook("workbookProtection-workbook_password_user_range-2010.xlsx"); | |||
assertTrue(workbook.validateWorkbookPassword(password)); | |||
// validate with another office file (Excel 2013) | |||
workbook = openSampleWorkbook("workbookProtection-workbook_password-2013.xlsx"); | |||
assertTrue(workbook.validateWorkbookPassword(password)); | |||
workbook = openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
// setting a null password shouldn't introduce the protection element | |||
workbook.setWorkbookPassword(null, null); | |||
assertNull(workbook.getCTWorkbook().getWorkbookProtection()); | |||
// compare the hashes | |||
workbook.setWorkbookPassword(password, null); | |||
int hashVal = CryptoFunctions.createXorVerifier1(password); | |||
int actualVal = Integer.parseInt(workbook.getCTWorkbook().getWorkbookProtection().xgetWorkbookPassword().getStringValue(),16); | |||
assertEquals(hashVal, actualVal); | |||
assertTrue(workbook.validateWorkbookPassword(password)); | |||
// removing the password again | |||
workbook.setWorkbookPassword(null, null); | |||
assertFalse(workbook.getCTWorkbook().getWorkbookProtection().isSetWorkbookPassword()); | |||
// removing the whole protection structure | |||
workbook.unLock(); | |||
assertNull(workbook.getCTWorkbook().getWorkbookProtection()); | |||
// setting a null password shouldn't introduce the protection element | |||
workbook.setRevisionsPassword(null, null); | |||
assertNull(workbook.getCTWorkbook().getWorkbookProtection()); | |||
// compare the hashes | |||
password = "T\u0400ST\u0100passwordWhichIsLongerThan15Chars"; | |||
workbook.setRevisionsPassword(password, null); | |||
hashVal = CryptoFunctions.createXorVerifier1(password); | |||
actualVal = Integer.parseInt(workbook.getCTWorkbook().getWorkbookProtection().xgetRevisionsPassword().getStringValue(),16); | |||
assertEquals(hashVal, actualVal); | |||
assertTrue(workbook.validateRevisionsPassword(password)); | |||
} | |||
@Test | |||
public void shouldReadWorkbookProtection() throws Exception { | |||
XSSFWorkbook workbook = openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
assertFalse(workbook.isStructureLocked()); | |||
assertFalse(workbook.isWindowsLocked()); | |||
assertFalse(workbook.isRevisionLocked()); | |||
workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_workbook_structure_protected.xlsx"); | |||
workbook = openSampleWorkbook("workbookProtection_workbook_structure_protected.xlsx"); | |||
assertTrue(workbook.isStructureLocked()); | |||
assertFalse(workbook.isWindowsLocked()); | |||
assertFalse(workbook.isRevisionLocked()); | |||
workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_workbook_windows_protected.xlsx"); | |||
workbook = openSampleWorkbook("workbookProtection_workbook_windows_protected.xlsx"); | |||
assertTrue(workbook.isWindowsLocked()); | |||
assertFalse(workbook.isStructureLocked()); | |||
assertFalse(workbook.isRevisionLocked()); | |||
workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_workbook_revision_protected.xlsx"); | |||
workbook = openSampleWorkbook("workbookProtection_workbook_revision_protected.xlsx"); | |||
assertTrue(workbook.isRevisionLocked()); | |||
assertFalse(workbook.isWindowsLocked()); | |||
assertFalse(workbook.isStructureLocked()); | |||
} | |||
public void testShouldWriteStructureLock() throws Exception { | |||
XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
@Test | |||
public void shouldWriteStructureLock() throws Exception { | |||
XSSFWorkbook workbook = openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
assertFalse(workbook.isStructureLocked()); | |||
workbook.lockStructure(); | |||
@@ -63,8 +115,9 @@ public class TestWorkbookProtection extends TestCase { | |||
assertFalse(workbook.isStructureLocked()); | |||
} | |||
public void testShouldWriteWindowsLock() throws Exception { | |||
XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
@Test | |||
public void shouldWriteWindowsLock() throws Exception { | |||
XSSFWorkbook workbook = openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
assertFalse(workbook.isWindowsLocked()); | |||
workbook.lockWindows(); | |||
@@ -76,8 +129,9 @@ public class TestWorkbookProtection extends TestCase { | |||
assertFalse(workbook.isWindowsLocked()); | |||
} | |||
public void testShouldWriteRevisionLock() throws Exception { | |||
XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
@Test | |||
public void shouldWriteRevisionLock() throws Exception { | |||
XSSFWorkbook workbook = openSampleWorkbook("workbookProtection_not_protected.xlsx"); | |||
assertFalse(workbook.isRevisionLocked()); | |||
workbook.lockRevision(); | |||
@@ -89,22 +143,32 @@ public class TestWorkbookProtection extends TestCase { | |||
assertFalse(workbook.isRevisionLocked()); | |||
} | |||
@SuppressWarnings("resource") | |||
@Test | |||
public void testHashPassword() throws Exception { | |||
XSSFWorkbook wb = new XSSFWorkbook(); | |||
wb.lockRevision(); | |||
wb.setRevisionsPassword("test", HashAlgorithm.sha1); | |||
wb = writeOutAndReadBack(wb); | |||
assertTrue(wb.isRevisionLocked()); | |||
assertTrue(wb.validateRevisionsPassword("test")); | |||
} | |||
@SuppressWarnings("resource") | |||
@Test | |||
public void testIntegration() throws Exception { | |||
XSSFWorkbook wb = new XSSFWorkbook(); | |||
wb.createSheet("Testing purpose sheet"); | |||
assertFalse(wb.isRevisionLocked()); | |||
wb.lockRevision(); | |||
wb.setRevisionsPassword("test", null); | |||
File tempFile = TempFile.createTempFile("workbookProtection", ".xlsx"); | |||
FileOutputStream out = new FileOutputStream(tempFile); | |||
wb.write(out); | |||
out.close(); | |||
FileInputStream inputStream = new FileInputStream(tempFile); | |||
XSSFWorkbook workbook = new XSSFWorkbook(inputStream); | |||
inputStream.close(); | |||
assertTrue(workbook.isRevisionLocked()); | |||
wb = writeOutAndReadBack(wb); | |||
assertTrue(wb.isRevisionLocked()); | |||
assertTrue(wb.validateRevisionsPassword("test")); | |||
} | |||
} |
@@ -19,6 +19,8 @@ package org.apache.poi.xssf.usermodel; | |||
import static junit.framework.TestCase.assertNotNull; | |||
import static junit.framework.TestCase.assertTrue; | |||
import static org.apache.poi.xssf.XSSFTestDataSamples.openSampleWorkbook; | |||
import static org.apache.poi.xssf.XSSFTestDataSamples.writeOutAndReadBack; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertNotSame; | |||
@@ -29,7 +31,8 @@ import static org.junit.Assert.fail; | |||
import java.util.List; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.hssf.record.PasswordRecord; | |||
import org.apache.poi.poifs.crypt.CryptoFunctions; | |||
import org.apache.poi.poifs.crypt.HashAlgorithm; | |||
import org.apache.poi.ss.usermodel.AutoFilter; | |||
import org.apache.poi.ss.usermodel.BaseTestSheet; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
@@ -41,7 +44,6 @@ import org.apache.poi.ss.usermodel.Workbook; | |||
import org.apache.poi.ss.util.AreaReference; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.ss.util.CellReference; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.xssf.SXSSFITestDataProvider; | |||
import org.apache.poi.xssf.XSSFITestDataProvider; | |||
import org.apache.poi.xssf.XSSFTestDataSamples; | |||
@@ -1068,13 +1070,27 @@ public final class TestXSSFSheet extends BaseTestSheet { | |||
assertTrue("sheet protection should be on", pr.isSetSheet()); | |||
assertTrue("object protection should be on", pr.isSetObjects()); | |||
assertTrue("scenario protection should be on", pr.isSetScenarios()); | |||
String hash = String.valueOf(HexDump.shortToHex(PasswordRecord.hashPassword(password))).substring(2); | |||
assertEquals("well known value for top secret hash should be "+ hash, hash, pr.xgetPassword().getStringValue()); | |||
int hashVal = CryptoFunctions.createXorVerifier1(password); | |||
int actualVal = Integer.parseInt(pr.xgetPassword().getStringValue(),16); | |||
assertEquals("well known value for top secret hash should match", hashVal, actualVal); | |||
sheet.protectSheet(null); | |||
assertNull("protectSheet(null) should unset CTSheetProtection", sheet.getCTWorksheet().getSheetProtection()); | |||
} | |||
@Test | |||
public void protectSheet_lowlevel_2013() { | |||
String password = "test"; | |||
XSSFWorkbook wb = new XSSFWorkbook(); | |||
XSSFSheet xs = wb.createSheet(); | |||
xs.setSheetPassword(password, HashAlgorithm.sha384); | |||
wb = writeOutAndReadBack(wb); | |||
assertTrue(wb.getSheetAt(0).validateSheetPassword(password)); | |||
wb = openSampleWorkbook("workbookProtection-sheet_password-2013.xlsx"); | |||
assertTrue(wb.getSheetAt(0).validateSheetPassword("pwd")); | |||
} | |||
@Test | |||
public void bug49966() { |