/// <summary> /// Create the hash. /// This method is written with the help of Lyquidity library, many thanks for this nice sample /// </summary> /// <param name="password">The password</param> /// <param name="encryptionInfo">The encryption info extracted from the ENCRYPTIOINFO stream inside the OLE document</param> /// <returns>The hash to encrypt the document</returns> private byte[] GetPasswordHash(string password, EncryptionInfo encryptionInfo) { byte[] hash = null; byte[] tempHash = new byte[4 + 20]; //Iterator + prev. hash try { HashAlgorithm hashProvider; if (encryptionInfo.Header.AlgIDHash == AlgorithmHashID.SHA1 || encryptionInfo.Header.AlgIDHash == AlgorithmHashID.App && (encryptionInfo.Flags & Flags.fExternal) == 0) { hashProvider = new SHA1CryptoServiceProvider(); } else if (encryptionInfo.Header.KeySize > 0 && encryptionInfo.Header.KeySize < 80) { throw new NotSupportedException("RC4 Hash provider is not supported. Must be SHA1(AlgIDHash == 0x8004)"); } else { throw new NotSupportedException("Hash provider is invalid. Must be SHA1(AlgIDHash == 0x8004)"); } hash = hashProvider.ComputeHash(CombinePassword(encryptionInfo.Verifier.Salt, password)); //Iterate 50 000 times, inserting i in first 4 bytes and then the prev. hash in byte 5-24 for (int i = 0; i < 50000; i++) { Array.Copy(BitConverter.GetBytes(i), tempHash, 4); Array.Copy(hash, 0, tempHash, 4, hash.Length); hash = hashProvider.ComputeHash(tempHash); } // Append "block" (0) Array.Copy(hash, tempHash, hash.Length); Array.Copy(System.BitConverter.GetBytes(0), 0, tempHash, hash.Length, 4); hash = hashProvider.ComputeHash(tempHash); /***** Now use the derived key algorithm *****/ byte[] derivedKey = new byte[64]; int keySizeBytes = encryptionInfo.Header.KeySize / 8; //First XOR hash bytes with 0x36 and fill the rest with 0x36 for (int i = 0; i < derivedKey.Length; i++) { derivedKey[i] = (byte)(i < hash.Length ? 0x36 ^ hash[i] : 0x36); } byte[] X1 = hashProvider.ComputeHash(derivedKey); //if verifier size is bigger than the key size we can return X1 if (encryptionInfo.Verifier.VerifierHashSize > keySizeBytes) { return(FixHashSize(X1, keySizeBytes)); } //Else XOR hash bytes with 0x5C and fill the rest with 0x5C for (int i = 0; i < derivedKey.Length; i++) { derivedKey[i] = (byte)(i < hash.Length ? 0x5C ^ hash[i] : 0x5C); } byte[] X2 = hashProvider.ComputeHash(derivedKey); //Join the two and return byte[] join = new byte[X1.Length + X2.Length]; Array.Copy(X1, 0, join, 0, X1.Length); Array.Copy(X2, 0, join, X1.Length, X2.Length); return(FixHashSize(join, keySizeBytes)); } catch (Exception ex) { throw (new Exception("An error occured when the encryptionkey was created", ex)); } }