2020-03-15

42: Editing Password into an Office Open XML File with UNO

<The previous article in this series | The table of contents of this series | The next article in this series>

Using LibreOffice or OpenOffice, for an Office Open XML .docx, .xlsx, or .pptx file, from Java, C++, C#, Python, BeanShell, JavaScript, or Basic

Topics


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: The Java programming language
About: C++
About: Microsoft .NET Framework
About: The Python programming language
About: LibreOffice Basic
About: Apache OpenOffice Basic
About: BeanShell
About: JavaScript

The table of contents of this article


Starting Context



Target Context


  • The reader will know how to set any editing password into an Office Open XML ('.docx', '.xlsx', or '.pptx') file from his or her program.
Stage Direction
Here are Hypothesizer 7, Objector 42A, and Objector 42B in front of a computer.


Orientation


Hypothesizer 7
In the 1st part and the 2nd part of this article, we have known how to set any editing password into an OpenDocument format file and into a Microsoft Office binary Word or Excel format file from our program.

In this part of this article, we will know how to set any editing password into an Office Open XML format ('.docx', '.xlsx', or '.pptx') file from our program.

Objector 42A
The LibreOffice GUI lets me set the editing password, without any visible effect . . .

Hypothesizer 7
Yes, sir. The LibreOffice GUI does not set any editing password for any Office Open XML format file.

Objector 42A
Is that a bug?

Hypothesizer 7
Well, that is more abandonment than a bug.

Objector 42A
What do you mean?

Hypothesizer 7
The LibreOffice GUI is not even trying to set the editing password.

Objector 42A
"not even trying"?

Hypothesizer 7
Exactly.

Objector 42A
Then, why does the GUI show the option?

Hypothesizer 7
I do not know; it does not seem to bother to disbale the option which is not implemented at all.

Objector 42A
. . . I think, at least, it sould bother that.

Hypothesizer 7
I agree.

Objector 42A
So, you are saying that this article will explain how to set the editing password notwithstanding that the GUI does not implement the functionality?

Hypothesizer 7
Yes, I am.

Objector 42A
. . . OK.

Hypothesizer 7
Anyway, before you begin to implement the functionality, you may want to consider the implications of the functionality.

Objector 42B
What do you mean?

Hypothesizer 7
Madam, as has been discussed in 'Orientation' of the 1st part of this article, any document that is protected by any editing password can be edited by any program that does not respect the protection.

Objector 42B
. . . Should it be called "protection"?

Hypothesizer 7
You may want to rather call it 'suggestion'.

Objector 42B
Or 'appeal'.

Hypothesizer 7
In fact, as LibreOffice happens not to respect the protection, the file with the editing password can be edited by LibreOffice without the password known.


Main Body


1: The Mechanism


Hypothesizer 7
The mechanism for setting the editing password for any Office Open XML format ('.docx', '.xlsx', or '.pptx') file is fundamentally different from doing it for any OpenDocument format file or any Microsoft Office binary Word or Excel format file.

For any OpenDocument format file or any Microsoft Office binary Word or Excel format file, it is about setting a document-storing property; for any Office Open XML format file, it is about setting a document property.

Objector 42B
What's the difference?

Hypothesizer 7
Any document-storing property is a parameter for storing the document into a file, while any document property is something set into the opened document.

Objector 42B
So, this time, it isn't about setting a parameter for storing the document.


2: Creating a Hash of the Editing Password


Hypothesizer 7
The tedious part of setting the editing password into an Office document file is to create a hash of the editing password.

Objector 42B
What is the algorithm?

Hypothesizer 7
In fact, the algorithm is not any standard algorithm, but an original algorithm.

Objector 42B
Hmm.

Hypothesizer 7
Before we create the hash, we create a salt.

Objector 42B
How?

Hypothesizer 7
In any way you like, although you will probably want the salt to be random. As for me, I use the function that I have introduced in the 1st part of this article.

Objector 42B
Hmm.

Hypothesizer 7
In fact, the hashing algorithm consists of 2 phases, and the 1st phase is exactly the to-'32 bits' hashing algorithm introduced in the 2nd part of this article.

Objector 42B
That algorithm for '.doc' files?

Hypothesizer 7
Yes.

This is my Java function of the 2nd phase.

theBiasPlanet.coreUtilities.cryptography.Hasher

@Java Source Code
package theBiasPlanet.coreUtilities.cryptography;

import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;
~

public class Hasher {
	private static final String c_sha1HashingAlgorismName = "SHA-1";
	~
	private static MessageDigest s_sha1MessageDigester;
	~
	
	static {
		try {
			s_sha1MessageDigester = MessageDigest.getInstance (c_sha1HashingAlgorismName);
			~
		}
		catch (NoSuchAlgorithmException l_exception) {
		}
	}
	
	~
	
	public static byte [] hashInSha1 (byte [] a_originalDatumBytesArray, byte [] a_saltBytesArray) {
		s_sha1MessageDigester.update (a_saltBytesArray);
		return s_sha1MessageDigester.digest (a_originalDatumBytesArray);
	}
	
	~
}

theBiasPlanet.unoUtilities.cryptography.MicrosoftPasswordsHasher

@Java Source Code
package theBiasPlanet.unoUtilities.cryptography;
import java.io.UnsupportedEncodingException;
~
import theBiasPlanet.coreUtilities.cryptography.Hasher;

public class MicrosoftPasswordsHasher {
~

	public static byte [] hashHashWithSalt (int a_32bitsHash, byte [] a_saltArray, int a_numberOfIteration) {
		int l_hashBytesLength = Integer.SIZE / Byte.SIZE;
		byte [] l_hash = null;
		byte l_byte = 0x00;
		StringBuilder l_bytesReversedHexadecimalStringBuilder = new StringBuilder ();
		byte [] l_bytesReversedHexadecimalStringUtf16BytesArray = null;
		for (int l_byteIndex = 0; l_byteIndex < l_hashBytesLength; l_byteIndex ++) {
			l_byte = (byte) ((a_32bitsHash >>> (Byte.SIZE * l_byteIndex)) & 0x000000FF);
			l_bytesReversedHexadecimalStringBuilder.append (String.format ("%02X", (l_byte >= 0) ? l_byte: l_byte + 256));
		}
		try {
			l_bytesReversedHexadecimalStringUtf16BytesArray = l_bytesReversedHexadecimalStringBuilder.toString ().getBytes ("UTF-16LE");
		}
		catch (UnsupportedEncodingException l_exception) {
			// Supposed to not happen.
		}
		int l_saltLength = a_saltArray.length;
		int l_bytesReversedHexadecimalStringUtf16BytesArrayLength = l_bytesReversedHexadecimalStringUtf16BytesArray.length;
		l_hash = Hasher.hashInSha1 (l_bytesReversedHexadecimalStringUtf16BytesArray, a_saltArray);
		byte [] l_iterationSaltBytesArray = new byte [l_hashBytesLength];
		for (int l_iterationIndex = 0; l_iterationIndex < a_numberOfIteration; l_iterationIndex ++) {
			l_iterationSaltBytesArray [0] = (byte) ((l_iterationIndex >>> (Byte.SIZE * 0)) & 0x000000FF);
			l_iterationSaltBytesArray [0 + 1] = (byte) ((l_iterationIndex >>> (Byte.SIZE * 1)) & 0x000000FF);
			l_iterationSaltBytesArray [0 + 2] = (byte) ((l_iterationIndex >>> (Byte.SIZE * 2)) & 0x000000FF);
			l_iterationSaltBytesArray [0 + 3] = (byte) ((l_iterationIndex >>> (Byte.SIZE * 3)) & 0x000000FF);
			l_hash = Hasher.hashInSha1 (l_iterationSaltBytesArray, l_hash);
		}
		return l_hash;
	}

Objector 42B
. . . You mean that the output of the 1st phase is passed into the 1st argument?

Hypothesizer 7
Yes.

Objector 42B
So, let me see the C# version.

Hypothesizer 7
Actually, I have prepared only the Java version so far.

Objector 42B
What?

Hypothesizer 7
I have intention of showing a C++ version, a C# version, and a Python version in future, but for the time being, you would be able to derive your version from the above Java version.

Objector 42B
. . .


3: Setting a Document Property


Hypothesizer 7
The document property to be set is 'InteropGrabBag', whose value is a sequence of '::com::sun::star::beans::PropertyValue's, one of which is a 'DocumentProtection' property whose value is a sequence of '::com::sun::star::beans::PropertyValue's, which are these (the types are UNO datum types).

nametypevalue
editstringwhat are protected: 'none' -> none, 'readOnly' -> the whole, 'comments' -> the comments, 'trackedChanges' -> the tracked changes, 'forms' -> the forms
enforcementstringthe protection is enforced: 'true', 'false'
formattingstringthe format is protected: 'true', 'false'
cryptProviderTypestringthe encryption provider type: 'invalid', 'rsaAES', 'rsaFull'
cryptAlgorithmClassstringthe encryption algorithm class: 'hash'
cryptAlgorithmTypestringthe encryption algorithm type: 'typeAny'
cryptAlgorithmSidstringthe encryption algorithm identification: '1' -> MD2, '2' -> MD4, '3' -> MD5, '4' -> SHA1
cryptSpinCountstringthe encryption spins count
hashstringthe hashed password in Base64
saltstringthe hashing salt in Base64

Objector 42A
"in Base64"?

Hypothesizer 7
Yes. For example, this is my function that gets the Base64 string of the specified bytes array.

theBiasPlanet.coreUtilities.stringsHandling.StringHandler

@ Source Code
package theBiasPlanet.coreUtilities.stringsHandling;

~
import java.util.Base64;
~

public class StringHandler {
	~
	static private final Base64.Encoder c_base64Encoder;
	
	static {
		~
		c_base64Encoder = Base64.getEncoder ();
	}
	
	~
	
	public static String getBase64String (byte [] a_bytesArray) {
		return c_base64Encoder.encodeToString (a_bytesArray);
	}
	
	~
}

Objector 42A
When should I use "invalid"?

Hypothesizer 7
I do not know. Actually, I have tried only a single combination of 'readOnly', 'true', 'true', 'rsaFull', 'hash', 'typeAny', '4', '50000' with a 16 bytes salt. Please try another combination at your discretion.


4: The Conclusion and Beyond


Hypothesizer 7
Now, we know how to set any editing password into an Office Open XML (.docx, .xlsx, or .pptx) file from our program.

Although we should be aware of how effective editing password is for what purpose, there will be some practically valid use cases.

We will see how to encrypt any OpenDocument format file by GPG in a future article.


References


<The previous article in this series | The table of contents of this series | The next article in this series>