Author: kiwiwings
Date: Thu Sep  4 22:50:28 2014
New Revision: 1622577

URL: http://svn.apache.org/r1622577
Log:
Bug 51483 - XSSF locking of specific features not working
Added some documentation to the crypto functions and adapted xor1verifier code 
to the OFFCrypto-Docs

Added:
    
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPaswordHelper.java
    poi/trunk/test-data/spreadsheet/workbookProtection-sheet_password-2013.xlsx 
  (with props)
    
poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password-2013.xlsx  
 (with props)
    
poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password_user_range-2010.xlsx
   (with props)
Modified:
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/hssf/record/PasswordRecord.java
    
poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/WorksheetProtectionBlock.java
    poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
    poi/trunk/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestSheetProtection.java
    
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestWorkbookProtection.java
    
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: 
http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Thu Sep  4 22:50:28 2014
@@ -38,6 +38,7 @@
     </devs>
 
     <release version="3.11-beta3" date="2014-??-??">
+        <action dev="PD" type="fix" fixes-bug="51483">XSSF locking of specific 
features not working</action>
         <action dev="PD" type="fix" fixes-bug="48195">Formulas: Fix incorrect 
evaluation of IF() with ROW()/COLUMN() as else-result.</action>
         <action dev="PD" type="fix" fixes-bug="55280">XSSF: Greatly improve 
performance of shifting rows in sheets with many merged regions.</action>
         <action dev="PD" type="fix" fixes-bug="51222">XSSFColor.getARGBHex() 
returns wrong color for Excel 2007 xlsx file</action>

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/PasswordRecord.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/PasswordRecord.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/PasswordRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/PasswordRecord.java Thu Sep  
4 22:50:28 2014
@@ -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 extend
         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);
     }
 
     /**

Modified: 
poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/WorksheetProtectionBlock.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/WorksheetProtectionBlock.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- 
poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/WorksheetProtectionBlock.java
 (original)
+++ 
poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/WorksheetProtectionBlock.java
 Thu Sep  4 22:50:28 2014
@@ -24,6 +24,7 @@ import org.apache.poi.hssf.record.Protec
 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 WorksheetProtectionBl
                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);

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java Thu Sep  
4 22:50:28 2014
@@ -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;
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java Thu Sep  4 
22:50:28 2014
@@ -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");
+    }
 }
\ No newline at end of file

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java Thu 
Sep  4 22:50:28 2014
@@ -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.Pack
 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.CellRefere
 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 POIXMLDoc
      */
     @Override
     public boolean getProtect() {
-        return worksheet.isSetSheetProtection() && sheetProtectionEnabled();
+        return isSheetLocked();
     }
 
     /**
@@ -1068,10 +1070,9 @@ public class XSSFSheet extends POIXMLDoc
      */
     @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,19 +1082,28 @@ public class XSSFSheet extends POIXMLDoc
     }
 
     /**
-     * Converts a String to a {@link STUnsignedShortHex} value that contains 
the {@link PasswordRecord#hashPassword(String)}
-     * value in hexadecimal format
-     *
-     * @param password the password string you wish convert to an {@link 
STUnsignedShortHex}
-     * @return {@link STUnsignedShortHex} that contains Excel hashed password 
in Hex format
+     * Sets the sheet password. 
+     * 
+     * @param password if null, the password will be removed
+     * @param hashAlgo if null, the password will be set as XOR password 
(Excel 2010 and earlier)
+     *  otherwise the given algorithm is used for calculating the hash 
password (Excel 2013)
      */
-    private STUnsignedShortHex stringToExcelPassword(String password) {
-        STUnsignedShortHex hexPassword = 
STUnsignedShortHex.Factory.newInstance();
-        
hexPassword.setStringValue(String.valueOf(HexDump.shortToHex(PasswordRecord.hashPassword(password))).substring(2));
-        return hexPassword;
+    public void setSheetPassword(String password, HashAlgorithm hashAlgo) {
+        if (password == null && !isSheetProtectionEnabled()) return;
+        setPassword(safeGetProtectionField(), password, hashAlgo, null);
     }
 
     /**
+     * Validate the password against the stored hash, the hashing method will 
be determined
+     *  by the existing password attributes
+     * @return true, if the hashes match (... though original password may 
differ ...)
+     */
+    public boolean validateSheetPassword(String password) {
+        if (!isSheetProtectionEnabled()) return (password == null);
+        return validatePassword(safeGetProtectionField(), password, null);
+    }
+    
+    /**
      * Returns the logical row ( 0-based).  If you ask for a row that is not
      * defined you get a null.  This is to say row 4 represents the fifth row 
on a sheet.
      *
@@ -1546,7 +1556,7 @@ public class XSSFSheet extends POIXMLDoc
             worksheet.unsetMergeCells();
         }
     }
-    
+
     /**
      * Removes a number of merged regions of cells (hence letting them free)
      * 
@@ -2910,304 +2920,440 @@ public class XSSFSheet extends POIXMLDoc
      * @return true when Autofilters are locked and the sheet is protected.
      */
     public boolean isAutoFilterLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getAutoFilter();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getAutoFilter();
+        }
+        return false;
     }
 
     /**
      * @return true when Deleting columns is locked and the sheet is protected.
      */
     public boolean isDeleteColumnsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getDeleteColumns();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getDeleteColumns();
+        }
+        return false;
     }
 
     /**
      * @return true when Deleting rows is locked and the sheet is protected.
      */
     public boolean isDeleteRowsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getDeleteRows();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getDeleteRows();
+        }
+        return false;
     }
 
     /**
      * @return true when Formatting cells is locked and the sheet is protected.
      */
     public boolean isFormatCellsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getFormatCells();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getFormatCells();
+        }
+        return false;
     }
 
     /**
      * @return true when Formatting columns is locked and the sheet is 
protected.
      */
     public boolean isFormatColumnsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getFormatColumns();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getFormatColumns();
+        }
+        return false;
     }
 
     /**
      * @return true when Formatting rows is locked and the sheet is protected.
      */
     public boolean isFormatRowsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getFormatRows();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getFormatRows();
+        }
+        return false;
     }
 
     /**
      * @return true when Inserting columns is locked and the sheet is 
protected.
      */
     public boolean isInsertColumnsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getInsertColumns();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getInsertColumns();
+        }
+        return false;
     }
 
     /**
      * @return true when Inserting hyperlinks is locked and the sheet is 
protected.
      */
     public boolean isInsertHyperlinksLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getInsertHyperlinks();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getInsertHyperlinks();
+        }
+        return false;
     }
 
     /**
      * @return true when Inserting rows is locked and the sheet is protected.
      */
     public boolean isInsertRowsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getInsertRows();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getInsertRows();
+        }
+        return false;
     }
 
     /**
      * @return true when Pivot tables are locked and the sheet is protected.
      */
     public boolean isPivotTablesLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getPivotTables();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getPivotTables();
+        }
+        return false;
     }
 
     /**
      * @return true when Sorting is locked and the sheet is protected.
      */
     public boolean isSortLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getSort();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getSort();
+        }
+        return false;
     }
 
     /**
      * @return true when Objects are locked and the sheet is protected.
      */
     public boolean isObjectsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getObjects();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getObjects();
+        }
+        return false;
     }
 
     /**
      * @return true when Scenarios are locked and the sheet is protected.
      */
     public boolean isScenariosLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getScenarios();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getScenarios();
+        }
+        return false;
     }
 
     /**
      * @return true when Selection of locked cells is locked and the sheet is 
protected.
      */
     public boolean isSelectLockedCellsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getSelectLockedCells();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getSelectLockedCells();
+        }
+        return false;
     }
 
     /**
      * @return true when Selection of unlocked cells is locked and the sheet 
is protected.
      */
     public boolean isSelectUnlockedCellsLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getSelectUnlockedCells();
+        if (isSheetLocked()) {
+            return safeGetProtectionField().getSelectUnlockedCells();
+        }
+        return false;
     }
 
     /**
      * @return true when Sheet is Protected.
      */
     public boolean isSheetLocked() {
-        createProtectionFieldIfNotPresent();
-        return sheetProtectionEnabled() && 
worksheet.getSheetProtection().getSheet();
+        if (worksheet.isSetSheetProtection()) {
+            return safeGetProtectionField().getSheet();
+        }
+        return false;
     }
 
     /**
      * Enable sheet protection
      */
     public void enableLocking() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setSheet(true);
+        safeGetProtectionField().setSheet(true);
     }
 
     /**
      * Disable sheet protection
      */
     public void disableLocking() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setSheet(false);
+        safeGetProtectionField().setSheet(false);
     }
 
     /**
      * Enable Autofilters locking.
-     * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * @deprecated use {@link #lockAutoFilter(boolean)}
      */
     public void lockAutoFilter() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setAutoFilter(true);
+        lockAutoFilter(true);
     }
 
     /**
-     * Enable Deleting columns locking.
+     * Enable or disable Autofilters locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockAutoFilter(boolean enabled) {
+        safeGetProtectionField().setAutoFilter(enabled);
+    }
+
+    /**
+     * Enable Deleting columns locking.
+     * @deprecated use {@link #lockDeleteColumns(boolean)}
      */
     public void lockDeleteColumns() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setDeleteColumns(true);
+        lockDeleteColumns(true);
     }
 
     /**
-     * Enable Deleting rows locking.
+     * Enable or disable Deleting columns locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockDeleteColumns(boolean enabled) {
+        safeGetProtectionField().setDeleteColumns(enabled);
+    }
+
+    /**
+     * Enable Deleting rows locking.
+     * @deprecated use {@link #lockDeleteRows(boolean)}
      */
     public void lockDeleteRows() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setDeleteRows(true);
+        lockDeleteRows(true);
     }
 
     /**
-     * Enable Formatting cells locking.
+     * Enable or disable Deleting rows locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockDeleteRows(boolean enabled) {
+        safeGetProtectionField().setDeleteRows(enabled);
+    }
+
+    /**
+     * Enable Formatting cells locking.
+     * @deprecated use {@link #lockFormatCells(boolean)}
      */
     public void lockFormatCells() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setDeleteColumns(true);
+        lockFormatCells(true);
     }
 
     /**
-     * Enable Formatting columns locking.
+     * Enable or disable Formatting cells locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockFormatCells(boolean enabled) {
+        safeGetProtectionField().setFormatCells(enabled);
+    }
+
+    /**
+     * Enable Formatting columns locking.
+     * @deprecated use {@link #lockFormatColumns(boolean)}
      */
     public void lockFormatColumns() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setFormatColumns(true);
+        lockFormatColumns(true);
     }
 
     /**
-     * Enable Formatting rows locking.
+     * Enable or disable Formatting columns locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockFormatColumns(boolean enabled) {
+        safeGetProtectionField().setFormatColumns(enabled);
+    }
+
+    /**
+     * Enable Formatting rows locking.
+     * @deprecated use {@link #lockFormatRows(boolean)}
      */
     public void lockFormatRows() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setFormatRows(true);
+        lockFormatRows(true);
     }
 
     /**
-     * Enable Inserting columns locking.
+     * Enable or disable Formatting rows locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockFormatRows(boolean enabled) {
+        safeGetProtectionField().setFormatRows(enabled);
+    }
+
+    /**
+     * Enable Inserting columns locking.
+     * @deprecated use {@link #lockInsertColumns(boolean)}
      */
     public void lockInsertColumns() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setInsertColumns(true);
+        lockInsertColumns(true);
     }
 
     /**
-     * Enable Inserting hyperlinks locking.
+     * Enable or disable Inserting columns locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockInsertColumns(boolean enabled) {
+        safeGetProtectionField().setInsertColumns(enabled);
+    }
+
+    /**
+     * Enable Inserting hyperlinks locking.
+     * @deprecated use {@link #lockInsertHyperlinks(boolean)}
      */
     public void lockInsertHyperlinks() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setInsertHyperlinks(true);
+        lockInsertHyperlinks(true);
     }
 
     /**
-     * Enable Inserting rows locking.
+     * Enable or disable Inserting hyperlinks locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockInsertHyperlinks(boolean enabled) {
+        safeGetProtectionField().setInsertHyperlinks(enabled);
+    }
+
+    /**
+     * Enable Inserting rows locking.
+     * @deprecated use {@link #lockInsertRows(boolean)}
      */
     public void lockInsertRows() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setInsertRows(true);
+        lockInsertRows(true);
     }
 
     /**
-     * Enable Pivot Tables locking.
+     * Enable or disable Inserting rows locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockInsertRows(boolean enabled) {
+        safeGetProtectionField().setInsertRows(enabled);
+    }
+
+    /**
+     * Enable Pivot Tables locking.
+     * @deprecated use {@link #lockPivotTables(boolean)}
      */
     public void lockPivotTables() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setPivotTables(true);
+        lockPivotTables(true);
     }
 
     /**
-     * Enable Sort locking.
+     * Enable or disable Pivot Tables locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockPivotTables(boolean enabled) {
+        safeGetProtectionField().setPivotTables(enabled);
+    }
+
+    /**
+     * Enable Sort locking.
+     * @deprecated use {@link #lockSort(boolean)}
      */
     public void lockSort() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setSort(true);
+        lockSort(true);
     }
 
     /**
-     * Enable Objects locking.
+     * Enable or disable Sort locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockSort(boolean enabled) {
+        safeGetProtectionField().setSort(enabled);
+    }
+
+    /**
+     * Enable Objects locking.
+     * @deprecated use {@link #lockObjects(boolean)}
      */
     public void lockObjects() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setObjects(true);
+        lockObjects(true);
     }
 
     /**
-     * Enable Scenarios locking.
+     * Enable or disable Objects locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockObjects(boolean enabled) {
+        safeGetProtectionField().setObjects(enabled);
+    }
+
+    /**
+     * Enable Scenarios locking.
+     * @deprecated use {@link #lockScenarios(boolean)}
      */
     public void lockScenarios() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setScenarios(true);
+        lockScenarios(true);
     }
 
     /**
-     * Enable Selection of locked cells locking.
+     * Enable or disable Scenarios locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockScenarios(boolean enabled) {
+        safeGetProtectionField().setScenarios(enabled);
+    }
+
+    /**
+     * Enable Selection of locked cells locking.
+     * @deprecated use {@link #lockSelectLockedCells(boolean)}
      */
     public void lockSelectLockedCells() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setSelectLockedCells(true);
+        lockSelectLockedCells(true);
     }
 
     /**
-     * Enable Selection of unlocked cells locking.
+     * Enable or disable Selection of locked cells locking.
      * This does not modify sheet protection status.
-     * To enforce this locking, call {@link #enableLocking()}
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockSelectLockedCells(boolean enabled) {
+        safeGetProtectionField().setSelectLockedCells(enabled);
+    }
+
+    /**
+     * Enable Selection of unlocked cells locking.
+     * @deprecated use {@link #lockSelectUnlockedCells(boolean)}
      */
     public void lockSelectUnlockedCells() {
-        createProtectionFieldIfNotPresent();
-        worksheet.getSheetProtection().setSelectUnlockedCells(true);
+        lockSelectUnlockedCells(true);
+    }
+
+    /**
+     * Enable or disable Selection of unlocked cells locking.
+     * This does not modify sheet protection status.
+     * To enforce this un-/locking, call {@link #disableLocking()} or {@link 
#enableLocking()}
+     */
+    public void lockSelectUnlockedCells(boolean enabled) {
+        safeGetProtectionField().setSelectUnlockedCells(enabled);
     }
 
-    private void createProtectionFieldIfNotPresent() {
-        if (worksheet.getSheetProtection() == null) {
-            
worksheet.setSheetProtection(CTSheetProtection.Factory.newInstance());
+    private CTSheetProtection safeGetProtectionField() {
+        if (!isSheetProtectionEnabled()) {
+            return worksheet.addNewSheetProtection();
         }
+        return worksheet.getSheetProtection();
     }
 
-    private boolean sheetProtectionEnabled() {
-        return worksheet.getSheetProtection().getSheet();
+    /* package */ boolean isSheetProtectionEnabled() {
+        return (worksheet.isSetSheetProtection());
     }
 
     /* package */ boolean isCellInArrayFormulaContext(XSSFCell cell) {

Modified: 
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java 
Thu Sep  4 22:50:28 2014
@@ -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.Pack
 import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
 import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.apache.poi.openxml4j.opc.TargetMode;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
 import org.apache.poi.ss.formula.SheetNameFormatter;
 import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
 import org.apache.poi.ss.formula.udf.UDFFinder;
@@ -1736,60 +1740,108 @@ public class XSSFWorkbook extends POIXML
         * Locks the structure of workbook.
         */
        public void lockStructure() {
-               createProtectionFieldIfNotPresent();
-               workbook.getWorkbookProtection().setLockStructure(true);
+           safeGetWorkbookProtection().setLockStructure(true);
        }
 
        /**
         * Unlocks the structure of workbook.
         */
        public void unLockStructure() {
-               createProtectionFieldIfNotPresent();
-               workbook.getWorkbookProtection().setLockStructure(false);
+           safeGetWorkbookProtection().setLockStructure(false);
        }
 
        /**
         * Locks the windows that comprise the workbook.
         */
        public void lockWindows() {
-               createProtectionFieldIfNotPresent();
-               workbook.getWorkbookProtection().setLockWindows(true);
+           safeGetWorkbookProtection().setLockWindows(true);
        }
 
        /**
         * Unlocks the windows that comprise the workbook.
         */
        public void unLockWindows() {
-               createProtectionFieldIfNotPresent();
-               workbook.getWorkbookProtection().setLockWindows(false);
+           safeGetWorkbookProtection().setLockWindows(false);
        }
 
        /**
         * Locks the workbook for revisions.
         */
        public void lockRevision() {
-               createProtectionFieldIfNotPresent();
-               workbook.getWorkbookProtection().setLockRevision(true);
+           safeGetWorkbookProtection().setLockRevision(true);
        }
 
        /**
         * Unlocks the workbook for revisions.
         */
        public void unLockRevision() {
-               createProtectionFieldIfNotPresent();
-               workbook.getWorkbookProtection().setLockRevision(false);
+           safeGetWorkbookProtection().setLockRevision(false);
        }
 
-       private boolean workbookProtectionPresent() {
-               return workbook.getWorkbookProtection() != null;
+       /**
+        * Sets the workbook password. 
+        * 
+        * @param password if null, the password will be removed
+        * @param hashAlgo if null, the password will be set as XOR password 
(Excel 2010 and earlier)
+        *  otherwise the given algorithm is used for calculating the hash 
password (Excel 2013)
+        */
+       public void setWorkbookPassword(String password, HashAlgorithm 
hashAlgo) {
+        if (password == null && !workbookProtectionPresent()) return;
+        setPassword(safeGetWorkbookProtection(), password, hashAlgo, 
"workbook");
        }
 
-       private void createProtectionFieldIfNotPresent() {
-               if (workbook.getWorkbookProtection() == null){
-                       
workbook.setWorkbookProtection(CTWorkbookProtection.Factory.newInstance());
-               }
+    /**
+     * Validate the password against the stored hash, the hashing method will 
be determined
+     *  by the existing password attributes
+     * @return true, if the hashes match (... though original password may 
differ ...)
+     */
+    public boolean validateWorkbookPassword(String password) {
+        if (!workbookProtectionPresent()) return (password == null);
+        return validatePassword(safeGetWorkbookProtection(), password, 
"workbook");
+    }
+
+    /**
+     * Sets the revisions password.
+     * 
+     * @param password if null, the password will be removed
+     * @param hashAlgo if null, the password will be set as XOR password 
(Excel 2010 and earlier)
+     *  otherwise the given algorithm is used for calculating the hash 
password (Excel 2013)
+     */
+    public void setRevisionsPassword(String password, HashAlgorithm hashAlgo) {
+        if (password == null && !workbookProtectionPresent()) return;
+        setPassword(safeGetWorkbookProtection(), password, hashAlgo, 
"revisions");
+    }
+
+    /**
+     * Validate the password against the stored hash, the hashing method will 
be determined
+     *  by the existing password attributes
+     * @return true if the hashes match (... though original password may 
differ ...)
+     */
+    public boolean validateRevisionsPassword(String password) {
+        if (!workbookProtectionPresent()) return (password == null);
+        return validatePassword(safeGetWorkbookProtection(), password, 
"revisions");
+    }
+    
+    /**
+     * Removes the workbook protection settings
+     */
+    public void unLock() {
+        if (workbookProtectionPresent()) {
+            workbook.unsetWorkbookProtection();
+        }
+    }
+    
+       private boolean workbookProtectionPresent() {
+               return workbook.isSetWorkbookProtection();
        }
 
+    private CTWorkbookProtection safeGetWorkbookProtection() {
+        if (!workbookProtectionPresent()){
+            return workbook.addNewWorkbookProtection();
+        }
+        return workbook.getWorkbookProtection();
+    }
+       
     /**
      *
      * Returns the locator of user-defined functions.

Added: 
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPaswordHelper.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPaswordHelper.java?rev=1622577&view=auto
==============================================================================
--- 
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPaswordHelper.java
 (added)
+++ 
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPaswordHelper.java
 Thu Sep  4 22:50:28 2014
@@ -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));
+        }
+    }
+}

Modified: 
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestSheetProtection.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestSheetProtection.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestSheetProtection.java 
(original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestSheetProtection.java 
Thu Sep  4 22:50:28 2014
@@ -16,12 +16,11 @@
 ==================================================================== */
 package org.apache.poi.xssf;
 
-import org.apache.poi.xssf.usermodel.XSSFSheet;
+import junit.framework.TestCase;
 
+import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
-import junit.framework.TestCase;
-
 public class TestSheetProtection extends TestCase {
        private XSSFSheet sheet;
        
@@ -75,6 +74,8 @@ public class TestSheetProtection extends
                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
                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
                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
                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
                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
                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
                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
                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
                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
                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
                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
                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
                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
                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
                assertFalse(sheet.isSelectUnlockedCellsLocked());
                sheet.enableLocking();
                assertTrue(sheet.isSelectUnlockedCellsLocked());
+        sheet.lockSelectUnlockedCells(false);
+        assertFalse(sheet.isSelectUnlockedCellsLocked());
        }
 
        public void testWriteSelectEnableLocking() throws Exception {

Modified: 
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestWorkbookProtection.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestWorkbookProtection.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- 
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestWorkbookProtection.java 
(original)
+++ 
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/TestWorkbookProtection.java 
Thu Sep  4 22:50:28 2014
@@ -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 exte
                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 exte
                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 exte
                assertFalse(workbook.isRevisionLocked());
        }
 
+    @SuppressWarnings("resource")
+    @Test
+    public void testHashPassword() throws Exception {
+        XSSFWorkbook wb = new XSSFWorkbook();
+        wb.lockRevision();
+        wb.setRevisionsPassword("test", HashAlgorithm.sha1);
+        
+        wb = writeOutAndReadBack(wb);
+        
+        assertTrue(wb.isRevisionLocked());
+        assertTrue(wb.validateRevisionsPassword("test"));
+    }
+    
+    @SuppressWarnings("resource")
+    @Test
        public void testIntegration() throws Exception {
                XSSFWorkbook wb = new XSSFWorkbook();
                wb.createSheet("Testing purpose sheet");
                assertFalse(wb.isRevisionLocked());
 
                wb.lockRevision();
+               wb.setRevisionsPassword("test", null);
 
-               File tempFile = TempFile.createTempFile("workbookProtection", 
".xlsx");
-               FileOutputStream out = new FileOutputStream(tempFile);
-               wb.write(out);
-               out.close();
-
-               FileInputStream inputStream = new FileInputStream(tempFile);
-               XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
-               inputStream.close();
-
-               assertTrue(workbook.isRevisionLocked());
+               wb = writeOutAndReadBack(wb);
+               
+               assertTrue(wb.isRevisionLocked());
+               assertTrue(wb.validateRevisionsPassword("test"));
        }
 }

Modified: 
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java?rev=1622577&r1=1622576&r2=1622577&view=diff
==============================================================================
--- 
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java 
(original)
+++ 
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java 
Thu Sep  4 22:50:28 2014
@@ -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.Workb
 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
         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() {

Added: 
poi/trunk/test-data/spreadsheet/workbookProtection-sheet_password-2013.xlsx
URL: 
http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/workbookProtection-sheet_password-2013.xlsx?rev=1622577&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
poi/trunk/test-data/spreadsheet/workbookProtection-sheet_password-2013.xlsx
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password-2013.xlsx
URL: 
http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password-2013.xlsx?rev=1622577&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password-2013.xlsx
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password_user_range-2010.xlsx
URL: 
http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password_user_range-2010.xlsx?rev=1622577&view=auto
==============================================================================
Binary file - no diff available.

Propchange: 
poi/trunk/test-data/spreadsheet/workbookProtection-workbook_password_user_range-2010.xlsx
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@poi.apache.org
For additional commands, e-mail: commits-h...@poi.apache.org

Reply via email to