Page 1 of 1

Password set for DocumentProtection not accepted in MSWord

PostPosted: Fri Jul 01, 2016 12:03 am
by McPeter
I try to protect a Word document to READ Only mode using the docx4java API. Using the following code;

Code: Select all
       
                CTDocProtect cdtP = new CTDocProtect();
                cdtP.setEnforcement(Boolean.TRUE);
                cdtP.setEdit(STDocProtect.READ_ONLY);
               
                // Set password for protection               
                String password = "aaaa" ;

                Random rnd = new SecureRandom();
                int spins = 100000 ;
                int keylength = 256 ;
                byte[] salt = new byte[16];
                rnd.nextBytes(salt);
                byte[] hash = new byte[0] ;
                hash = password.getBytes() ;
               
           
                cdtP.setCryptProviderType(STCryptProv.RSA_FULL);
                cdtP.setCryptAlgorithmClass(STAlgClass.HASH);
                cdtP.setCryptAlgorithmType(STAlgType.TYPE_ANY);
                cdtP.setCryptAlgorithmSid(BigInteger.valueOf(4));
                cdtP.setCryptSpinCount(BigInteger.valueOf(spins));

                cdtP.setHash(hash);
                cdtP.setSalt(salt);
               
                // set document protection
                dsp.getContents().setDocumentProtection(cdtP);


When opening the Word document it is protected and a password is asked the expected password seems not to match the password used in the code "aaaa".
Any idea what is not correct in the code ?

Cheers,

Peter

== Update 1 ==

When setting the hash and the salt using the value of password

Code: Select all
      hash = password.getBytes() ;               
                salt = password.getBytes() ;


And printing the hash and salt

Code: Select all
       
           System.out.println("Hash: " + new String(hash)) ;
           cdtP.setHash(hash);
           System.out.println("Salt: " + new String(salt)) ;
           cdtP.setSalt(salt);


The salt and hash seem to be ok;
Code: Select all
Protection is enabled , protection mode is READ_ONLY
Hash: aaaa
Salt: aaaa


What seems to be odd is that the salt and hash in the settings.xml is the same. I would expect that the hash and salt in the settings.xml would differ (Hashed pasword would be salted using salt value)

Code: Select all
w:hash="YWFhYQ==" w:salt="YWFhYQ=="/



== Update 2 =

Also tried to create a hash using the salt value using the following method;

Code: Select all
public static byte[] hash(char[] password, byte[] salt, int iterations, int keylength) {
    PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keylength);
    Arrays.fill(password, Character.MIN_VALUE);
    try {
        for (Object obj : java.security.Security.getAlgorithms("Cipher")) {
         System.out.println(obj);
    }
    SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    System.out.println(skf.generateSecret(spec).getEncoded()) ;
      return skf.generateSecret(spec).getEncoded();
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
      throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
    } finally {
      spec.clearPassword();
    }
}

Re: Password set for DocumentProtection not accepted in MSWo

PostPosted: Fri Jul 01, 2016 2:45 pm
by jason
Please see https://github.com/plutext/docx4j/blob/ ... ected.java

Note: docx4j v3.3.0 or later

Re: Password set for DocumentProtection not accepted in MSWo

PostPosted: Fri Jul 01, 2016 4:58 pm
by McPeter
That example is about protecting a paragraph. In the example i gave the whole document is protected.
The issue is that i was not able set the correct hash and salt.

Re: Password set for DocumentProtection not accepted in MSWo

PostPosted: Fri Jul 01, 2016 6:56 pm
by jason
My understanding is in terms of Word's UI, you can

- Protect Document > Restrict Editing > No changes (Read only)

- Protect Document > Encrypt with Password

CreateDocxProtected, where it says:

Code: Select all
    ProtectDocument protection = new ProtectDocument(wordMLPackage);
    protection.restrictEditing(STDocProtect.READ_ONLY, "foobaa");


is an example of the former.

To do the latter, you:

Code: Select all
      Docx4J.save(wordMLPackage, new java.io.File(filename), Docx4J.FLAG_SAVE_ENCRYPTED_AGILE, "some password");


and the resulting file will be an OLE compound file.

I'm not aware of some third option you are alluding to?

Re: Password set for DocumentProtection not accepted in MSWo

PostPosted: Fri Jul 01, 2016 8:42 pm
by McPeter
Hi Jason,

docx4j has the setHash and setSalt methods on CTDocProtect to set the password. Only there seem to be no Crypto classes available to create the correct hash and salt.
I 'borrowed' those from the other docx Java API, POI. Here an example.

Code: Select all
            // Get settings.xml part of docx
            WordprocessingMLPackage wordMLPackage = Docx4J.load(new java.io.File(filename));
            MainDocumentPart mdp = wordMLPackage.getMainDocumentPart();
            Relationship rs = mdp.getRelationshipsPart().getRelationshipByType(Namespaces.SETTINGS);
            DocumentSettingsPart dsp = (DocumentSettingsPart) mdp.getRelationshipsPart().getPart(rs);

            // Create CTDocProtect instance and configure it for Enforcing read-only
            CTDocProtect cdtP = new CTDocProtect();
            cdtP.setEnforcement(Boolean.TRUE);
            cdtP.setEdit(STDocProtect.READ_ONLY);

            SecureRandom random = new SecureRandom();
           
            // Set password for protection   
            String password = new BigInteger(130, random).toString(32);
            result.setPassword(password);
               
            byte salt[] = random.generateSeed(16);
            int spins = 100000;
            HashAlgorithm hashAlgorithm = HashAlgorithm.sha1;
            String legacyHash = CryptoFunctions.xorHashPasswordReversed(password);
            byte hash[] = CryptoFunctions.hashPassword(legacyHash, hashAlgorithm, salt, spins, false);

            cdtP.setCryptProviderType(STCryptProv.RSA_FULL);
            cdtP.setCryptAlgorithmClass(STAlgClass.HASH);
            cdtP.setCryptAlgorithmType(STAlgType.TYPE_ANY);
            cdtP.setCryptAlgorithmSid(BigInteger.valueOf(4));
            cdtP.setCryptSpinCount(BigInteger.valueOf(spins));

            cdtP.setHash(hash);
            cdtP.setSalt(salt);

            // set document protection
            dsp.getContents().setDocumentProtection(cdtP);

Re: Password set for DocumentProtection not accepted in MSWo

PostPosted: Fri Jul 01, 2016 8:45 pm
by McPeter
Tried your suggestion to save a Docx with an password.

Code: Select all
Docx4J.save(wordMLPackage, new java.io.File(filename), Docx4J.FLAG_SAVE_ENCRYPTED_AGILE, "some password");


The attribute FLAG_SAVE_ENCRYPTED_AGILE seems not to exist in docx4j version 3.2.2.

Re: Password set for DocumentProtection not accepted in MSWo

PostPosted: Sat Jul 02, 2016 11:28 am
by jason
Everything I have said in this thread is wrt docx4j v3.3.0, which repackages some POI code; see https://github.com/plutext/docx4j/tree/ ... apache/poi

and builds on that.