You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CryptoFunctions.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.poifs.crypt;
  16. import java.nio.charset.Charset;
  17. import java.security.DigestException;
  18. import java.security.GeneralSecurityException;
  19. import java.security.MessageDigest;
  20. import java.security.Provider;
  21. import java.security.Security;
  22. import java.security.spec.AlgorithmParameterSpec;
  23. import java.util.Arrays;
  24. import javax.crypto.Cipher;
  25. import javax.crypto.Mac;
  26. import javax.crypto.SecretKey;
  27. import javax.crypto.spec.IvParameterSpec;
  28. import javax.crypto.spec.RC2ParameterSpec;
  29. import org.apache.poi.EncryptedDocumentException;
  30. import org.apache.poi.util.LittleEndian;
  31. import org.apache.poi.util.LittleEndianConsts;
  32. /**
  33. * Helper functions used for standard and agile encryption
  34. */
  35. public class CryptoFunctions {
  36. /**
  37. * 2.3.4.7 ECMA-376 Document Encryption Key Generation (Standard Encryption)
  38. * 2.3.4.11 Encryption Key Generation (Agile Encryption)
  39. *
  40. * The encryption key for ECMA-376 document encryption [ECMA-376] using agile encryption MUST be
  41. * generated by using the following method, which is derived from PKCS #5: Password-Based
  42. * Cryptography Version 2.0 [RFC2898].
  43. *
  44. * Let H() be a hashing algorithm as determined by the PasswordKeyEncryptor.hashAlgorithm
  45. * element, H_n be the hash data of the n-th iteration, and a plus sign (+) represent concatenation. The
  46. * password MUST be provided as an array of Unicode characters. Limitations on the length of the
  47. * password and the characters used by the password are implementation-dependent. The initial
  48. * password hash is generated as follows:
  49. *
  50. * - H_0 = H(salt + password)
  51. *
  52. * The salt used MUST be generated randomly. The salt MUST be stored in the
  53. * PasswordKeyEncryptor.saltValue element contained within the \EncryptionInfo stream (1) as
  54. * specified in section 2.3.4.10. The hash is then iterated by using the following approach:
  55. *
  56. * - H_n = H(iterator + H_n-1)
  57. *
  58. * where iterator is an unsigned 32-bit value that is initially set to 0x00000000 and then incremented
  59. * monotonically on each iteration until PasswordKey.spinCount iterations have been performed.
  60. * The value of iterator on the last iteration MUST be one less than PasswordKey.spinCount.
  61. *
  62. * For POI, H_final will be calculated by {@link #generateKey(byte[],HashAlgorithm,byte[],int)}
  63. *
  64. * @param password
  65. * @param hashAlgorithm
  66. * @param salt
  67. * @param spinCount
  68. * @return the hashed password
  69. */
  70. public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte salt[], int spinCount) {
  71. return hashPassword(password, hashAlgorithm, salt, spinCount, true);
  72. }
  73. /**
  74. * Generalized method for read and write protection hash generation.
  75. * The difference is, read protection uses the order iterator then hash in the hash loop, whereas write protection
  76. * uses first the last hash value and then the current iterator value
  77. *
  78. * @param password
  79. * @param hashAlgorithm
  80. * @param salt
  81. * @param spinCount
  82. * @param iteratorFirst if true, the iterator is hashed before the n-1 hash value,
  83. * if false the n-1 hash value is applied first
  84. * @return the hashed password
  85. */
  86. public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte salt[], int spinCount, boolean iteratorFirst) {
  87. // If no password was given, use the default
  88. if (password == null) {
  89. password = Decryptor.DEFAULT_PASSWORD;
  90. }
  91. MessageDigest hashAlg = getMessageDigest(hashAlgorithm);
  92. hashAlg.update(salt);
  93. byte[] hash = hashAlg.digest(getUtf16LeString(password));
  94. byte[] iterator = new byte[LittleEndianConsts.INT_SIZE];
  95. byte[] first = (iteratorFirst ? iterator : hash);
  96. byte[] second = (iteratorFirst ? hash : iterator);
  97. try {
  98. for (int i = 0; i < spinCount; i++) {
  99. LittleEndian.putInt(iterator, 0, i);
  100. hashAlg.reset();
  101. hashAlg.update(first);
  102. hashAlg.update(second);
  103. hashAlg.digest(hash, 0, hash.length); // don't create hash buffer everytime new
  104. }
  105. } catch (DigestException e) {
  106. throw new EncryptedDocumentException("error in password hashing");
  107. }
  108. return hash;
  109. }
  110. /**
  111. * 2.3.4.12 Initialization Vector Generation (Agile Encryption)
  112. *
  113. * Initialization vectors are used in all cases for agile encryption. An initialization vector MUST be
  114. * generated by using the following method, where H() is a hash function that MUST be the same as
  115. * specified in section 2.3.4.11 and a plus sign (+) represents concatenation:
  116. * 1. If a blockKey is provided, let IV be a hash of the KeySalt and the following value:
  117. * blockKey: IV = H(KeySalt + blockKey)
  118. * 2. If a blockKey is not provided, let IV be equal to the following value:
  119. * KeySalt:IV = KeySalt.
  120. * 3. If the number of bytes in the value of IV is less than the the value of the blockSize attribute
  121. * corresponding to the cipherAlgorithm attribute, pad the array of bytes by appending 0x36 until
  122. * the array is blockSize bytes. If the array of bytes is larger than blockSize bytes, truncate the
  123. * array to blockSize bytes.
  124. **/
  125. public static byte[] generateIv(HashAlgorithm hashAlgorithm, byte[] salt, byte[] blockKey, int blockSize) {
  126. byte iv[] = salt;
  127. if (blockKey != null) {
  128. MessageDigest hashAlgo = getMessageDigest(hashAlgorithm);
  129. hashAlgo.update(salt);
  130. iv = hashAlgo.digest(blockKey);
  131. }
  132. return getBlock36(iv, blockSize);
  133. }
  134. /**
  135. * 2.3.4.11 Encryption Key Generation (Agile Encryption)
  136. *
  137. * ... continued ...
  138. *
  139. * The final hash data that is used for an encryption key is then generated by using the following
  140. * method:
  141. *
  142. * - H_final = H(H_n + blockKey)
  143. *
  144. * where blockKey represents an array of bytes used to prevent two different blocks from encrypting
  145. * to the same cipher text.
  146. *
  147. * If the size of the resulting H_final is smaller than that of PasswordKeyEncryptor.keyBits, the key
  148. * MUST be padded by appending bytes with a value of 0x36. If the hash value is larger in size than
  149. * PasswordKeyEncryptor.keyBits, the key is obtained by truncating the hash value.
  150. *
  151. * @param passwordHash
  152. * @param hashAlgorithm
  153. * @param blockKey
  154. * @param keySize
  155. * @return intermediate key
  156. */
  157. public static byte[] generateKey(byte[] passwordHash, HashAlgorithm hashAlgorithm, byte[] blockKey, int keySize) {
  158. MessageDigest hashAlgo = getMessageDigest(hashAlgorithm);
  159. hashAlgo.update(passwordHash);
  160. byte[] key = hashAlgo.digest(blockKey);
  161. return getBlock36(key, keySize);
  162. }
  163. public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode) {
  164. return getCipher(key, cipherAlgorithm, chain, vec, cipherMode, null);
  165. }
  166. /**
  167. * Initialize a new cipher object with the given cipher properties
  168. * If the given algorithm is not implemented in the JCE, it will try to load it from the bouncy castle
  169. * provider.
  170. *
  171. * @param key the secrect key
  172. * @param cipherAlgorithm the cipher algorithm
  173. * @param chain the chaining mode
  174. * @param vec the initialization vector (IV), can be null
  175. * @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
  176. * @param padding
  177. * @return the requested cipher
  178. * @throws GeneralSecurityException
  179. * @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
  180. * which depends on a missing bouncy castle provider
  181. */
  182. public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) {
  183. int keySizeInBytes = key.getEncoded().length;
  184. if (padding == null) padding = "NoPadding";
  185. try {
  186. // Ensure the JCE policies files allow for this sized key
  187. if (Cipher.getMaxAllowedKeyLength(cipherAlgorithm.jceId) < keySizeInBytes*8) {
  188. throw new EncryptedDocumentException("Export Restrictions in place - please install JCE Unlimited Strength Jurisdiction Policy files");
  189. }
  190. Cipher cipher;
  191. if (cipherAlgorithm == CipherAlgorithm.rc4) {
  192. cipher = Cipher.getInstance(cipherAlgorithm.jceId);
  193. } else if (cipherAlgorithm.needsBouncyCastle) {
  194. registerBouncyCastle();
  195. cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding, "BC");
  196. } else {
  197. cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding);
  198. }
  199. if (vec == null) {
  200. cipher.init(cipherMode, key);
  201. } else {
  202. AlgorithmParameterSpec aps;
  203. if (cipherAlgorithm == CipherAlgorithm.rc2) {
  204. aps = new RC2ParameterSpec(key.getEncoded().length*8, vec);
  205. } else {
  206. aps = new IvParameterSpec(vec);
  207. }
  208. cipher.init(cipherMode, key, aps);
  209. }
  210. return cipher;
  211. } catch (GeneralSecurityException e) {
  212. throw new EncryptedDocumentException(e);
  213. }
  214. }
  215. /**
  216. * Returns a new byte array with a truncated to the given size.
  217. * If the hash has less then size bytes, it will be filled with 0x36-bytes
  218. *
  219. * @param hash the to be truncated/filled hash byte array
  220. * @param size the size of the returned byte array
  221. * @return the padded hash
  222. */
  223. public static byte[] getBlock36(byte[] hash, int size) {
  224. return getBlockX(hash, size, (byte)0x36);
  225. }
  226. /**
  227. * Returns a new byte array with a truncated to the given size.
  228. * If the hash has less then size bytes, it will be filled with 0-bytes
  229. *
  230. * @param hash the to be truncated/filled hash byte array
  231. * @param size the size of the returned byte array
  232. * @return the padded hash
  233. */
  234. public static byte[] getBlock0(byte[] hash, int size) {
  235. return getBlockX(hash, size, (byte)0);
  236. }
  237. private static byte[] getBlockX(byte[] hash, int size, byte fill) {
  238. if (hash.length == size) return hash;
  239. byte[] result = new byte[size];
  240. Arrays.fill(result, fill);
  241. System.arraycopy(hash, 0, result, 0, Math.min(result.length, hash.length));
  242. return result;
  243. }
  244. public static byte[] getUtf16LeString(String str) {
  245. return str.getBytes(Charset.forName("UTF-16LE"));
  246. }
  247. public static MessageDigest getMessageDigest(HashAlgorithm hashAlgorithm) {
  248. try {
  249. if (hashAlgorithm.needsBouncyCastle) {
  250. registerBouncyCastle();
  251. return MessageDigest.getInstance(hashAlgorithm.jceId, "BC");
  252. } else {
  253. return MessageDigest.getInstance(hashAlgorithm.jceId);
  254. }
  255. } catch (GeneralSecurityException e) {
  256. throw new EncryptedDocumentException("hash algo not supported", e);
  257. }
  258. }
  259. public static Mac getMac(HashAlgorithm hashAlgorithm) {
  260. try {
  261. if (hashAlgorithm.needsBouncyCastle) {
  262. registerBouncyCastle();
  263. return Mac.getInstance(hashAlgorithm.jceHmacId, "BC");
  264. } else {
  265. return Mac.getInstance(hashAlgorithm.jceHmacId);
  266. }
  267. } catch (GeneralSecurityException e) {
  268. throw new EncryptedDocumentException("hmac algo not supported", e);
  269. }
  270. }
  271. @SuppressWarnings("unchecked")
  272. private static void registerBouncyCastle() {
  273. if (Security.getProvider("BC") != null) return;
  274. try {
  275. Class<Provider> clazz = (Class<Provider>)Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
  276. Security.addProvider(clazz.newInstance());
  277. } catch (Exception e) {
  278. throw new EncryptedDocumentException("Only the BouncyCastle provider supports your encryption settings - please add it to the classpath.");
  279. }
  280. }
  281. private static final int InitialCodeArray[] = {
  282. 0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE,
  283. 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A,
  284. 0x4EC3
  285. };
  286. private static final byte PadArray[] = {
  287. (byte)0xBB, (byte)0xFF, (byte)0xFF, (byte)0xBA, (byte)0xFF,
  288. (byte)0xFF, (byte)0xB9, (byte)0x80, (byte)0x00, (byte)0xBE,
  289. (byte)0x0F, (byte)0x00, (byte)0xBF, (byte)0x0F, (byte)0x00
  290. };
  291. private static final int EncryptionMatrix[][] = {
  292. /* char 1 */ {0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09},
  293. /* char 2 */ {0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF},
  294. /* char 3 */ {0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0},
  295. /* char 4 */ {0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40},
  296. /* char 5 */ {0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5},
  297. /* char 6 */ {0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A},
  298. /* char 7 */ {0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9},
  299. /* char 8 */ {0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0},
  300. /* char 9 */ {0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC},
  301. /* char 10 */ {0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10},
  302. /* char 11 */ {0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168},
  303. /* char 12 */ {0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C},
  304. /* char 13 */ {0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD},
  305. /* char 14 */ {0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC},
  306. /* char 15 */ {0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4}
  307. };
  308. /**
  309. * This method generates the xor verifier for word documents &lt; 2007 (method 2).
  310. * Its output will be used as password input for the newer word generations which
  311. * utilize a real hashing algorithm like sha1.
  312. *
  313. * @param password the password
  314. * @return the hashed password
  315. *
  316. * @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
  317. * @see <a href="http://blogs.msdn.com/b/vsod/archive/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0.aspx">How to set the editing restrictions in Word using Open XML SDK 2.0</a>
  318. * @see <a href="http://www.aspose.com/blogs/aspose-blogs/vladimir-averkin/archive/2007/08/20/funny-how-the-new-powerful-cryptography-implemented-in-word-2007-turns-it-into-a-perfect-tool-for-document-password-removal.html">Funny: How the new powerful cryptography implemented in Word 2007 turns it into a perfect tool for document password removal.</a>
  319. */
  320. public static int createXorVerifier2(String password) {
  321. //Array to hold Key Values
  322. byte[] generatedKey = new byte[4];
  323. //Maximum length of the password is 15 chars.
  324. final int maxPasswordLength = 15;
  325. if (!"".equals(password)) {
  326. // Truncate the password to 15 characters
  327. password = password.substring(0, Math.min(password.length(), maxPasswordLength));
  328. // Construct a new NULL-terminated string consisting of single-byte characters:
  329. // -- > Get the single-byte values by iterating through the Unicode characters of the truncated Password.
  330. // --> For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.
  331. byte[] arrByteChars = new byte[password.length()];
  332. for (int i = 0; i < password.length(); i++) {
  333. int intTemp = password.charAt(i);
  334. byte lowByte = (byte)(intTemp & 0x00FF);
  335. byte highByte = (byte)((intTemp & 0xFF00) >> 8);
  336. arrByteChars[i] = (lowByte != 0 ? lowByte : highByte);
  337. }
  338. // Compute the high-order word of the new key:
  339. // --> Initialize from the initial code array (see below), depending on the passwords length.
  340. int highOrderWord = InitialCodeArray[arrByteChars.length - 1];
  341. // --> For each character in the password:
  342. // --> For every bit in the character, starting with the least significant and progressing to (but excluding)
  343. // the most significant, if the bit is set, XOR the keys high-order word with the corresponding word from
  344. // the Encryption Matrix
  345. for (int i = 0; i < arrByteChars.length; i++) {
  346. int tmp = maxPasswordLength - arrByteChars.length + i;
  347. for (int intBit = 0; intBit < 7; intBit++) {
  348. if ((arrByteChars[i] & (0x0001 << intBit)) != 0) {
  349. highOrderWord ^= EncryptionMatrix[tmp][intBit];
  350. }
  351. }
  352. }
  353. // Compute the low-order word of the new key:
  354. // SET Verifier TO 0x0000
  355. short verifier = 0;
  356. // FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
  357. for (int i = arrByteChars.length-1; i >= 0; i--) {
  358. // SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
  359. verifier = rotateLeftBase15Bit(verifier);
  360. verifier ^= arrByteChars[i];
  361. }
  362. // as we haven't prepended the password length into the input array
  363. // we need to do it now separately ...
  364. verifier = rotateLeftBase15Bit(verifier);
  365. verifier ^= arrByteChars.length;
  366. // RETURN Verifier BITWISE XOR 0xCE4B
  367. verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
  368. // The byte order of the result shall be reversed [password "Example": 0x64CEED7E becomes 7EEDCE64],
  369. // and that value shall be hashed as defined by the attribute values.
  370. LittleEndian.putShort(generatedKey, 0, verifier);
  371. LittleEndian.putShort(generatedKey, 2, (short)highOrderWord);
  372. }
  373. return LittleEndian.getInt(generatedKey);
  374. }
  375. /**
  376. * This method generates the xored-hashed password for word documents &lt; 2007.
  377. */
  378. public static String xorHashPassword(String password) {
  379. int hashedPassword = createXorVerifier2(password);
  380. return String.format("%1$08X", hashedPassword);
  381. }
  382. /**
  383. * Convenience function which returns the reversed xored-hashed password for further
  384. * processing in word documents 2007 and newer, which utilize a real hashing algorithm like sha1.
  385. */
  386. public static String xorHashPasswordReversed(String password) {
  387. int hashedPassword = createXorVerifier2(password);
  388. return String.format("%1$02X%2$02X%3$02X%4$02X"
  389. , ( hashedPassword >>> 0 ) & 0xFF
  390. , ( hashedPassword >>> 8 ) & 0xFF
  391. , ( hashedPassword >>> 16 ) & 0xFF
  392. , ( hashedPassword >>> 24 ) & 0xFF
  393. );
  394. }
  395. /**
  396. * Create the verifier for xor obfuscation (method 1)
  397. *
  398. * @see <a href="http://msdn.microsoft.com/en-us/library/dd926947.aspx">2.3.7.1 Binary Document Password Verifier Derivation Method 1</a>
  399. * @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
  400. *
  401. * @param password the password
  402. * @return the verifier (actually a short value)
  403. */
  404. public static int createXorVerifier1(String password) {
  405. // the verifier for method 1 is part of the verifier for method 2
  406. // so we simply chop it from there
  407. return createXorVerifier2(password) & 0xFFFF;
  408. }
  409. /**
  410. * Create the xor key for xor obfuscation, which is used to create the xor array (method 1)
  411. *
  412. * @see <a href="http://msdn.microsoft.com/en-us/library/dd924704.aspx">2.3.7.2 Binary Document XOR Array Initialization Method 1</a>
  413. * @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
  414. *
  415. * @param password the password
  416. * @return the xor key
  417. */
  418. public static int createXorKey1(String password) {
  419. // the xor key for method 1 is part of the verifier for method 2
  420. // so we simply chop it from there
  421. return createXorVerifier2(password) >>> 16;
  422. }
  423. /**
  424. * Creates an byte array for xor obfuscation (method 1)
  425. *
  426. * @see <a href="http://msdn.microsoft.com/en-us/library/dd924704.aspx">2.3.7.2 Binary Document XOR Array Initialization Method 1</a>
  427. * @see <a href="http://docs.libreoffice.org/oox/html/binarycodec_8cxx_source.html">Libre Office implementation</a>
  428. *
  429. * @param password the password
  430. * @return the byte array for xor obfuscation
  431. */
  432. public static byte[] createXorArray1(String password) {
  433. if (password.length() > 15) password = password.substring(0, 15);
  434. byte passBytes[] = password.getBytes(Charset.forName("ASCII"));
  435. // this code is based on the libre office implementation.
  436. // The MS-OFFCRYPTO misses some infos about the various rotation sizes
  437. byte obfuscationArray[] = new byte[16];
  438. System.arraycopy(passBytes, 0, obfuscationArray, 0, passBytes.length);
  439. System.arraycopy(PadArray, 0, obfuscationArray, passBytes.length, PadArray.length-passBytes.length+1);
  440. int xorKey = createXorKey1(password);
  441. // rotation of key values is application dependent
  442. int nRotateSize = 2; /* Excel = 2; Word = 7 */
  443. byte baseKeyLE[] = { (byte)(xorKey & 0xFF), (byte)((xorKey >>> 8) & 0xFF) };
  444. for (int i=0; i<obfuscationArray.length; i++) {
  445. obfuscationArray[i] ^= baseKeyLE[i&1];
  446. obfuscationArray[i] = rotateLeft(obfuscationArray[i], nRotateSize);
  447. }
  448. return obfuscationArray;
  449. }
  450. private static byte rotateLeft(byte bits, int shift) {
  451. return (byte)(((bits & 0xff) << shift) | ((bits & 0xff) >>> (8 - shift)));
  452. }
  453. private static short rotateLeftBase15Bit(short verifier) {
  454. /*
  455. * IF (Verifier BITWISE AND 0x4000) is 0x0000
  456. * SET Intermediate1 TO 0
  457. * ELSE
  458. * SET Intermediate1 TO 1
  459. * ENDIF
  460. */
  461. short intermediate1 = (short)(((verifier & 0x4000) == 0) ? 0 : 1);
  462. /*
  463. * SET Intermediate2 TO Verifier MULTIPLED BY 2
  464. * SET most significant bit of Intermediate2 TO 0
  465. */
  466. short intermediate2 = (short)((verifier<<1) & 0x7FFF);
  467. /*
  468. * SET Intermediate3 TO Intermediate1 BITWISE OR Intermediate2
  469. */
  470. short intermediate3 = (short)(intermediate1 | intermediate2);
  471. return intermediate3;
  472. }
  473. }