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
- Orientation
- Main Body
- 1: The Mechanism
- 2: Creating a Hash of the Editing Password
- 3: Setting a Document Property
- 4: The Conclusion and Beyond
Starting Context
- The reader has a basic knowledge on one of the UNO-supported programming languages (Java, C++, the Microsoft .NET Framework programming languages (C# and Visual Basic.NET, in most cases), Python, LibreOffice or Apache OpenOffice Basic, BeanShell, and JavaScript).
- The reader has a knowledge on what UNO is and how it is related to LibreOffice or Apache OpenOffice.
- The reader has a knowledge on basic elements of UNO and the terminology used for them in this series.
- The reader has a knowledge on how to get a UNO objects context to any LibreOffice or Apache OpenOffice instance (there are some articles on how to do it for any external console Java, C++, C#, and Python programs; there is an article on how to do it for any external GUI Java program; some articles on how to do it for any external GUI C++, C#, and Python programs will come later).
- The reader has a knowledge on how to access the intended document and how to store the document into a file, using UNO (the article on how to use LibreOffice or Apache OpenOffice as a files converter (the concept, a Java implementation, a C++ implementation, a C# implementation, a Python implementation, and a LibreOffice or Apache OpenOffice Basic implementation) contains a major part of the information).
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.
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).
name | type | value |
---|---|---|
edit | string | what are protected: 'none' -> none, 'readOnly' -> the whole, 'comments' -> the comments, 'trackedChanges' -> the tracked changes, 'forms' -> the forms |
enforcement | string | the protection is enforced: 'true', 'false' |
formatting | string | the format is protected: 'true', 'false' |
cryptProviderType | string | the encryption provider type: 'invalid', 'rsaAES', 'rsaFull' |
cryptAlgorithmClass | string | the encryption algorithm class: 'hash' |
cryptAlgorithmType | string | the encryption algorithm type: 'typeAny' |
cryptAlgorithmSid | string | the encryption algorithm identification: '1' -> MD2, '2' -> MD4, '3' -> MD5, '4' -> SHA1 |
cryptSpinCount | string | the encryption spins count |
hash | string | the hashed password in Base64 |
salt | string | the 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.