internal static EncryptionInfo ReadFile(FileStream dataStream) { EncryptionInfo ret; var bytes = new byte[8]; dataStream.Read(bytes, 0, 8); var majorVersion = BitConverter.ToInt16(bytes, 0); var minorVersion = BitConverter.ToInt16(bytes, 2); if ((minorVersion == 2 || minorVersion == 3) && majorVersion <= 4) // minorVersion==1 is RC4, not supported. { ret = new EncryptionInfoBinary(); } else if (majorVersion == 4 && minorVersion == 4) { ret = new EncryptionInfoAgile(); } else { throw (new NotSupportedException("Unsupported encryption format")); } ret.MajorVersion = majorVersion; ret.MinorVersion = minorVersion; ret.Read(dataStream); return(ret); }
/// <summary> /// Validate the password /// </summary> /// <param name="key">The encryption key</param> /// <param name="encryptionInfo">The encryption info extracted from the ENCRYPTIOINFO stream inside the OLE document</param> /// <returns></returns> private bool IsPasswordValid(byte[] key, EncryptionInfoBinary encryptionInfo) { #if (Core) var decryptKey = Aes.Create(); #else RijndaelManaged decryptKey = new RijndaelManaged(); #endif decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; ICryptoTransform decryptor = decryptKey.CreateDecryptor( key, null); //Decrypt the verifier MemoryStream dataStream; var decryptedVerifier = new byte[16]; var decryptedVerifierHash = new byte[16]; using (dataStream = RecyclableMemory.GetStream(encryptionInfo.Verifier.EncryptedVerifier)) { CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); cryptoStream.Read(decryptedVerifier, 0, 16); } using (dataStream = RecyclableMemory.GetStream(encryptionInfo.Verifier.EncryptedVerifierHash)) { var cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); //Decrypt the verifier hash cryptoStream.Read(decryptedVerifierHash, 0, 16); } //Get the hash for the decrypted verifier #if (Core) var sha = SHA1.Create(); #else var sha = new SHA1Managed(); #endif var hash = sha.ComputeHash(decryptedVerifier); //Equal? for (int i = 0; i < 16; i++) { if (hash[i] != decryptedVerifierHash[i]) { return(false); } } return(true); }
/// <summary> /// Create an EncryptionInfo object to encrypt a workbook /// </summary> /// <param name="password">The password</param> /// <param name="algID"></param> /// <param name="key">The Encryption key</param> /// <returns></returns> private EncryptionInfoBinary CreateEncryptionInfo(string password, AlgorithmID algID, out byte[] key) { if (algID == AlgorithmID.Flags || algID == AlgorithmID.RC4) { throw (new ArgumentException("algID must be AES128, AES192 or AES256")); } var encryptionInfo = new EncryptionInfoBinary(); encryptionInfo.MajorVersion = 4; encryptionInfo.MinorVersion = 2; encryptionInfo.Flags = Flags.fAES | Flags.fCryptoAPI; //Header encryptionInfo.Header = new EncryptionHeader(); encryptionInfo.Header.AlgID = algID; encryptionInfo.Header.AlgIDHash = AlgorithmHashID.SHA1; encryptionInfo.Header.Flags = encryptionInfo.Flags; encryptionInfo.Header.KeySize = (algID == AlgorithmID.AES128 ? 0x80 : algID == AlgorithmID.AES192 ? 0xC0 : 0x100); encryptionInfo.Header.ProviderType = ProviderType.AES; encryptionInfo.Header.CSPName = "Microsoft Enhanced RSA and AES Cryptographic Provider\0"; encryptionInfo.Header.Reserved1 = 0; encryptionInfo.Header.Reserved2 = 0; encryptionInfo.Header.SizeExtra = 0; //Verifier encryptionInfo.Verifier = new EncryptionVerifier(); encryptionInfo.Verifier.Salt = new byte[16]; var rnd = RandomNumberGenerator.Create(); rnd.GetBytes(encryptionInfo.Verifier.Salt); encryptionInfo.Verifier.SaltSize = 0x10; key = GetPasswordHashBinary(password, encryptionInfo); var verifier = new byte[16]; rnd.GetBytes(verifier); encryptionInfo.Verifier.EncryptedVerifier = EncryptData(key, verifier, true); //AES = 32 Bits encryptionInfo.Verifier.VerifierHashSize = 0x20; var sha = #if COREFX SHA1.Create(); #else new SHA1Managed(); #endif var verifierHash = sha.ComputeHash(verifier); encryptionInfo.Verifier.EncryptedVerifierHash = EncryptData(key, verifierHash, false); return(encryptionInfo); }
/// <summary> /// Validate the password /// </summary> /// <param name="key">The encryption key</param> /// <param name="encryptionInfo">The encryption info extracted from the ENCRYPTIOINFO stream inside the OLE document</param> /// <returns></returns> private bool IsPasswordValid(byte[] key, EncryptionInfoBinary encryptionInfo) { var decryptKey = Aes.Create(); decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; ICryptoTransform decryptor = decryptKey.CreateDecryptor( key, null); //Decrypt the verifier MemoryStream dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifier); CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); var decryptedVerifier = new byte[16]; cryptoStream.Read(decryptedVerifier, 0, 16); dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifierHash); cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); //Decrypt the verifier hash var decryptedVerifierHash = new byte[16]; cryptoStream.Read(decryptedVerifierHash, 0, (int)16); //Get the hash for the decrypted verifier var sha = SHA1.Create(); var hash = sha.ComputeHash(decryptedVerifier); //Equal? for (int i = 0; i < 16; i++) { if (hash[i] != decryptedVerifierHash[i]) { return(false); } } return(true); }
private MemoryStream DecryptBinary(EncryptionInfoBinary encryptionInfo, string password, long size, byte[] encryptedData) { var doc = RecyclableMemory.GetStream(); if (encryptionInfo.Header.AlgID == AlgorithmID.AES128 || (encryptionInfo.Header.AlgID == AlgorithmID.Flags && ((encryptionInfo.Flags & (Flags.fAES | Flags.fExternal | Flags.fCryptoAPI)) == (Flags.fAES | Flags.fCryptoAPI))) || encryptionInfo.Header.AlgID == AlgorithmID.AES192 || encryptionInfo.Header.AlgID == AlgorithmID.AES256 ) { #if (Core) var decryptKey = Aes.Create(); #else RijndaelManaged decryptKey = new RijndaelManaged(); #endif decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; var key = GetPasswordHashBinary(password, encryptionInfo); if (IsPasswordValid(key, encryptionInfo)) { ICryptoTransform decryptor = decryptKey.CreateDecryptor( key, null); using (var dataStream = RecyclableMemory.GetStream(encryptedData)) { var cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); var decryptedData = new byte[size]; cryptoStream.Read(decryptedData, 0, (int)size); doc.Write(decryptedData, 0, (int)size); } } else { throw (new UnauthorizedAccessException("Invalid password")); } } return(doc); }
private void DecryptBinary(EncryptionInfoBinary encryptionInfo, string password, long size, FileStream fs, Stream outputStream) { if (encryptionInfo.Header.AlgID == AlgorithmID.AES128 || (encryptionInfo.Header.AlgID == AlgorithmID.Flags && ((encryptionInfo.Flags & (Flags.fAES | Flags.fExternal | Flags.fCryptoAPI)) == (Flags.fAES | Flags.fCryptoAPI))) || encryptionInfo.Header.AlgID == AlgorithmID.AES192 || encryptionInfo.Header.AlgID == AlgorithmID.AES256 ) { #if (Core) var decryptKey = Aes.Create(); #else RijndaelManaged decryptKey = new RijndaelManaged(); #endif decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; var key = GetPasswordHashBinary(password, encryptionInfo); if (IsPasswordValid(key, encryptionInfo)) { ICryptoTransform decryptor = decryptKey.CreateDecryptor( key, null); var cryptoStream = new CryptoStream(fs, decryptor, CryptoStreamMode.Read); var buffer = new byte[4096]; long read = 0; while (read < size) { int s = (int)Math.Min(size - read, buffer.LongLength); cryptoStream.Read(buffer, 0, s); outputStream.Write(buffer, 0, s); read += s; } } else { throw (new UnauthorizedAccessException("Invalid password")); } } }
internal static EncryptionInfo ReadBinary(byte[] data) { var majorVersion = BitConverter.ToInt16(data, 0); var minorVersion = BitConverter.ToInt16(data, 2); EncryptionInfo ret; if ((minorVersion == 2 || minorVersion == 3) && majorVersion <= 4) // minorVersion==1 is RC4, not supported. { ret = new EncryptionInfoBinary(); } else if (majorVersion == 4 && minorVersion==4) { ret = new EncryptionInfoAgile(); } else { throw (new NotSupportedException("Unsupported encryption format")); } ret.MajorVersion = majorVersion; ret.MinorVersion = minorVersion; ret.Read(data); return ret; }
internal static EncryptionInfo ReadBinary(byte[] data) { var majorVersion = BitConverter.ToInt16(data, 0); var minorVersion = BitConverter.ToInt16(data, 2); EncryptionInfo ret; if ((minorVersion == 2 || minorVersion == 3) && majorVersion <= 4) // minorVersion==1 is RC4, not supported. { ret = new EncryptionInfoBinary(); } else if (majorVersion == 4 && minorVersion == 4) { ret = new EncryptionInfoAgile(); } else { throw (new NotSupportedException("Unsupported encryption format")); } ret.MajorVersion = majorVersion; ret.MinorVersion = minorVersion; ret.Read(data); return(ret); }
/// <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[] GetPasswordHashBinary(string password, EncryptionInfoBinary 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 = GetPasswordHash(hashProvider, encryptionInfo.Verifier.Salt, password, 50000, 20); // 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 ((int)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)); } }
/// <summary> /// Validate the password /// </summary> /// <param name="key">The encryption key</param> /// <param name="encryptionInfo">The encryption info extracted from the ENCRYPTIOINFO stream inside the OLE document</param> /// <returns></returns> private bool IsPasswordValid(byte[] key, EncryptionInfoBinary encryptionInfo) { RijndaelManaged decryptKey = new RijndaelManaged(); decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; ICryptoTransform decryptor = decryptKey.CreateDecryptor( key, null); //Decrypt the verifier MemoryStream dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifier); CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); var decryptedVerifier = new byte[16]; cryptoStream.Read(decryptedVerifier, 0, 16); dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifierHash); cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); //Decrypt the verifier hash var decryptedVerifierHash = new byte[16]; cryptoStream.Read(decryptedVerifierHash, 0, (int)16); //Get the hash for the decrypted verifier var sha = new SHA1Managed(); var hash = sha.ComputeHash(decryptedVerifier); //Equal? for (int i = 0; i < 16; i++) { if (hash[i] != decryptedVerifierHash[i]) { return false; } } return true; }
/// <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[] GetPasswordHashBinary(string password, EncryptionInfoBinary 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 = GetPasswordHash(hashProvider, encryptionInfo.Verifier.Salt, password, 50000, 20); // 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 ((int)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)); } }
private MemoryStream DecryptBinary(EncryptionInfoBinary encryptionInfo, string password, long size, byte[] encryptedData) { MemoryStream doc = new MemoryStream(); if (encryptionInfo.Header.AlgID == AlgorithmID.AES128 || (encryptionInfo.Header.AlgID == AlgorithmID.Flags && ((encryptionInfo.Flags & (Flags.fAES | Flags.fExternal | Flags.fCryptoAPI)) == (Flags.fAES | Flags.fCryptoAPI))) || encryptionInfo.Header.AlgID == AlgorithmID.AES192 || encryptionInfo.Header.AlgID == AlgorithmID.AES256 ) { RijndaelManaged decryptKey = new RijndaelManaged(); decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; var key = GetPasswordHashBinary(password, encryptionInfo); if (IsPasswordValid(key, encryptionInfo)) { ICryptoTransform decryptor = decryptKey.CreateDecryptor( key, null); var dataStream = new MemoryStream(encryptedData); var cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); var decryptedData = new byte[size]; cryptoStream.Read(decryptedData, 0, (int)size); doc.Write(decryptedData, 0, (int)size); } else { throw (new UnauthorizedAccessException("Invalid password")); } } return doc; }
/// <summary> /// Create an EncryptionInfo object to encrypt a workbook /// </summary> /// <param name="password">The password</param> /// <param name="algID"></param> /// <param name="key">The Encryption key</param> /// <returns></returns> private EncryptionInfoBinary CreateEncryptionInfo(string password, AlgorithmID algID, out byte[] key) { if (algID == AlgorithmID.Flags || algID == AlgorithmID.RC4) { throw (new ArgumentException("algID must be AES128, AES192 or AES256")); } var encryptionInfo = new EncryptionInfoBinary(); encryptionInfo.MajorVersion = 4; encryptionInfo.MinorVersion = 2; encryptionInfo.Flags = Flags.fAES | Flags.fCryptoAPI; //Header encryptionInfo.Header = new EncryptionHeader(); encryptionInfo.Header.AlgID = algID; encryptionInfo.Header.AlgIDHash = AlgorithmHashID.SHA1; encryptionInfo.Header.Flags = encryptionInfo.Flags; encryptionInfo.Header.KeySize = (algID == AlgorithmID.AES128 ? 0x80 : algID == AlgorithmID.AES192 ? 0xC0 : 0x100); encryptionInfo.Header.ProviderType = ProviderType.AES; encryptionInfo.Header.CSPName = "Microsoft Enhanced RSA and AES Cryptographic Provider\0"; encryptionInfo.Header.Reserved1 = 0; encryptionInfo.Header.Reserved2 = 0; encryptionInfo.Header.SizeExtra = 0; //Verifier encryptionInfo.Verifier = new EncryptionVerifier(); encryptionInfo.Verifier.Salt = new byte[16]; var rnd = RandomNumberGenerator.Create(); rnd.GetBytes(encryptionInfo.Verifier.Salt); encryptionInfo.Verifier.SaltSize = 0x10; key = GetPasswordHashBinary(password, encryptionInfo); var verifier = new byte[16]; rnd.GetBytes(verifier); encryptionInfo.Verifier.EncryptedVerifier = EncryptData(key, verifier, true); //AES = 32 Bits encryptionInfo.Verifier.VerifierHashSize = 0x20; SHA1 sha = new SHA1Managed(); var verifierHash = sha.ComputeHash(verifier); encryptionInfo.Verifier.EncryptedVerifierHash = EncryptData(key, verifierHash, false); return encryptionInfo; }