Bug 51483 - XSSF locking of specific features not working

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-ffa450edef68
This commit is contained in:
Andreas Beeker 2014-09-04 22:50:28 +00:00
parent f1a0b0d315
commit d71574d531
13 changed files with 691 additions and 208 deletions

View File

@ -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);
}
/**

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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,16 +1082,25 @@ 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
* Sets the sheet password.
*
* @param password the password string you wish convert to an {@link STUnsignedShortHex}
* @return {@link STUnsignedShortHex} that contains Excel hashed password in Hex format
* @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);
}
/**
@ -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 or disable Autofilters locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockAutoFilter(boolean enabled) {
safeGetProtectionField().setAutoFilter(enabled);
}
/**
* Enable Deleting columns locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockDeleteColumns(boolean)}
*/
public void lockDeleteColumns() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setDeleteColumns(true);
lockDeleteColumns(true);
}
/**
* Enable or disable Deleting columns locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockDeleteColumns(boolean enabled) {
safeGetProtectionField().setDeleteColumns(enabled);
}
/**
* Enable Deleting rows locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockDeleteRows(boolean)}
*/
public void lockDeleteRows() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setDeleteRows(true);
lockDeleteRows(true);
}
/**
* Enable or disable Deleting rows locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockDeleteRows(boolean enabled) {
safeGetProtectionField().setDeleteRows(enabled);
}
/**
* Enable Formatting cells locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockFormatCells(boolean)}
*/
public void lockFormatCells() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setDeleteColumns(true);
lockFormatCells(true);
}
/**
* Enable or disable Formatting cells locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockFormatCells(boolean enabled) {
safeGetProtectionField().setFormatCells(enabled);
}
/**
* Enable Formatting columns locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockFormatColumns(boolean)}
*/
public void lockFormatColumns() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setFormatColumns(true);
lockFormatColumns(true);
}
/**
* Enable or disable Formatting columns locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockFormatColumns(boolean enabled) {
safeGetProtectionField().setFormatColumns(enabled);
}
/**
* Enable Formatting rows locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockFormatRows(boolean)}
*/
public void lockFormatRows() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setFormatRows(true);
lockFormatRows(true);
}
/**
* Enable or disable Formatting rows locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockFormatRows(boolean enabled) {
safeGetProtectionField().setFormatRows(enabled);
}
/**
* Enable Inserting columns locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockInsertColumns(boolean)}
*/
public void lockInsertColumns() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setInsertColumns(true);
lockInsertColumns(true);
}
/**
* Enable or disable Inserting columns locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockInsertColumns(boolean enabled) {
safeGetProtectionField().setInsertColumns(enabled);
}
/**
* Enable Inserting hyperlinks locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockInsertHyperlinks(boolean)}
*/
public void lockInsertHyperlinks() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setInsertHyperlinks(true);
lockInsertHyperlinks(true);
}
/**
* Enable or disable Inserting hyperlinks locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockInsertHyperlinks(boolean enabled) {
safeGetProtectionField().setInsertHyperlinks(enabled);
}
/**
* Enable Inserting rows locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockInsertRows(boolean)}
*/
public void lockInsertRows() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setInsertRows(true);
lockInsertRows(true);
}
/**
* Enable or disable Inserting rows locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockInsertRows(boolean enabled) {
safeGetProtectionField().setInsertRows(enabled);
}
/**
* Enable Pivot Tables locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockPivotTables(boolean)}
*/
public void lockPivotTables() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setPivotTables(true);
lockPivotTables(true);
}
/**
* Enable or disable Pivot Tables locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockPivotTables(boolean enabled) {
safeGetProtectionField().setPivotTables(enabled);
}
/**
* Enable Sort locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockSort(boolean)}
*/
public void lockSort() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setSort(true);
lockSort(true);
}
/**
* Enable or disable Sort locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockSort(boolean enabled) {
safeGetProtectionField().setSort(enabled);
}
/**
* Enable Objects locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockObjects(boolean)}
*/
public void lockObjects() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setObjects(true);
lockObjects(true);
}
/**
* Enable or disable Objects locking.
* This does not modify sheet protection status.
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
*/
public void lockObjects(boolean enabled) {
safeGetProtectionField().setObjects(enabled);
}
/**
* Enable Scenarios locking.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockScenarios(boolean)}
*/
public void lockScenarios() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setScenarios(true);
lockScenarios(true);
}
/**
* Enable or disable Scenarios locking.
* This does not modify sheet protection status.
* 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.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockSelectLockedCells(boolean)}
*/
public void lockSelectLockedCells() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setSelectLockedCells(true);
lockSelectLockedCells(true);
}
/**
* Enable or disable Selection of locked cells locking.
* This does not modify sheet protection status.
* 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.
* This does not modify sheet protection status.
* To enforce this locking, call {@link #enableLocking()}
* @deprecated use {@link #lockSelectUnlockedCells(boolean)}
*/
public void lockSelectUnlockedCells() {
createProtectionFieldIfNotPresent();
worksheet.getSheetProtection().setSelectUnlockedCells(true);
lockSelectUnlockedCells(true);
}
private void createProtectionFieldIfNotPresent() {
if (worksheet.getSheetProtection() == null) {
worksheet.setSheetProtection(CTSheetProtection.Factory.newInstance());
}
/**
* 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 boolean sheetProtectionEnabled() {
return worksheet.getSheetProtection().getSheet();
private CTSheetProtection safeGetProtectionField() {
if (!isSheetProtectionEnabled()) {
return worksheet.addNewSheetProtection();
}
return worksheet.getSheetProtection();
}
/* package */ boolean isSheetProtectionEnabled() {
return (worksheet.isSetSheetProtection());
}
/* package */ boolean isCellInArrayFormulaContext(XSSFCell cell) {

View File

@ -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,58 +1740,106 @@ 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);
}
/**
* 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");
}
/**
* 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.getWorkbookProtection() != null;
return workbook.isSetWorkbookProtection();
}
private void createProtectionFieldIfNotPresent() {
if (workbook.getWorkbookProtection() == null){
workbook.setWorkbookProtection(CTWorkbookProtection.Factory.newInstance());
private CTWorkbookProtection safeGetWorkbookProtection() {
if (!workbookProtectionPresent()){
return workbook.addNewWorkbookProtection();
}
return workbook.getWorkbookProtection();
}
/**

View File

@ -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));
}
}
}

View File

@ -16,12 +16,11 @@
==================================================================== */
package org.apache.poi.xssf;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import junit.framework.TestCase;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
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 {

View File

@ -16,42 +16,94 @@
==================================================================== */
package org.apache.poi.xssf;
import java.io.File;
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 java.io.FileInputStream;
import java.io.FileOutputStream;
import junit.framework.TestCase;
import org.apache.poi.util.TempFile;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;
public class TestWorkbookProtection extends TestCase {
public class TestWorkbookProtection {
public void testShouldReadWorkbookProtection() throws Exception {
XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("workbookProtection_not_protected.xlsx");
@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();
wb = writeOutAndReadBack(wb);
FileInputStream inputStream = new FileInputStream(tempFile);
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
inputStream.close();
assertTrue(workbook.isRevisionLocked());
assertTrue(wb.isRevisionLocked());
assertTrue(wb.validateRevisionsPassword("test"));
}
}

View File

@ -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() {