mirror of https://github.com/apache/poi.git
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:
parent
f1a0b0d315
commit
d71574d531
|
@ -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,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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 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 {
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue