即使在打開文件之前,是否有任何方法可以知道文件是否受密碼保護?檢測受密碼保護的文檔
回答
這裏是一個密碼的粗版本detecter我做了。不需要打開任何Office對象。
public static bool IsPassworded(string file) {
var bytes = File.ReadAllBytes(file);
return IsPassworded(bytes);
return false;
}
public static bool IsPassworded(byte[] bytes) {
var prefix = Encoding.Default.GetString(bytes.Take(2).ToArray());
if (prefix == "PK") {
//ZIP and not password protected
return false;
}
if (prefix == "ÐÏ") {
//Office format.
//Flagged with password
if (bytes.Skip(0x20c).Take(1).ToArray()[0] == 0x2f) return true; //XLS 2003
if (bytes.Skip(0x214).Take(1).ToArray()[0] == 0x2f) return true; //XLS 2005
if (bytes.Skip(0x20B).Take(1).ToArray()[0] == 0x13) return true; //DOC 2005
if (bytes.Length < 2000) return false; //Guessing false
var start = Encoding.Default.GetString(bytes.Take(2000).ToArray()); //DOC/XLS 2007+
start = start.Replace("\0", " ");
if (start.Contains("E n c r y p t e d P a c k a g e")) return true;
return false;
}
//Unknown.
return false;
}
它可能不是100%。我通過比較幾個有和沒有密碼的Excel和Word文檔找到的標誌。爲PowerPoint添加只是做同樣的事情。
我創建了一種實用方法,試圖檢測給定的辦公文檔是否受密碼保護。這裏有優勢的名單:
- 支持Word,Excel和PowerPoint文檔,無論是傳統的(DOC,XLS,PPT)和新的OpenXML版本(DOCX,XLSX,PPTX)
- 不依賴於COM或任何其他庫
- 只需要系統,System.IO和System.Text命名空間
- 蠻快的,可靠的檢測(方面遺留的.doc,.PPT和.xls文件格式)
- 低內存佔用(最大64KB)
下面是代碼,希望有人會發現它有用:
public static class MsOfficeHelper
{
/// <summary>
/// Detects if a given office document is protected by a password or not.
/// Supported formats: Word, Excel and PowerPoint (both legacy and OpenXml).
/// </summary>
/// <param name="fileName">Path to an office document.</param>
/// <returns>True if document is protected by a password, false otherwise.</returns>
public static bool IsPasswordProtected(string fileName)
{
using (var stream = File.OpenRead(fileName))
return IsPasswordProtected(stream);
}
/// <summary>
/// Detects if a given office document is protected by a password or not.
/// Supported formats: Word, Excel and PowerPoint (both legacy and OpenXml).
/// </summary>
/// <param name="stream">Office document stream.</param>
/// <returns>True if document is protected by a password, false otherwise.</returns>
public static bool IsPasswordProtected(Stream stream)
{
// minimum file size for office file is 4k
if (stream.Length < 4096)
return false;
// read file header
stream.Seek(0, SeekOrigin.Begin);
var compObjHeader = new byte[0x20];
ReadFromStream(stream, compObjHeader);
// check if we have plain zip file
if (compObjHeader[0] == 'P' && compObjHeader[1] == 'K')
{
// this is a plain OpenXml document (not encrypted)
return false;
}
// check compound object magic bytes
if (compObjHeader[0] != 0xD0 || compObjHeader[1] != 0xCF)
{
// unknown document format
return false;
}
int sectionSizePower = compObjHeader[0x1E];
if (sectionSizePower < 8 || sectionSizePower > 16)
{
// invalid section size
return false;
}
int sectionSize = 2 << (sectionSizePower - 1);
const int defaultScanLength = 32768;
long scanLength = Math.Min(defaultScanLength, stream.Length);
// read header part for scan
stream.Seek(0, SeekOrigin.Begin);
var header = new byte[scanLength];
ReadFromStream(stream, header);
// check if we detected password protection
if (ScanForPassword(stream, header, sectionSize))
return true;
// if not, try to scan footer as well
// read footer part for scan
stream.Seek(-scanLength, SeekOrigin.End);
var footer = new byte[scanLength];
ReadFromStream(stream, footer);
// finally return the result
return ScanForPassword(stream, footer, sectionSize);
}
static void ReadFromStream(Stream stream, byte[] buffer)
{
int bytesRead, count = buffer.Length;
while (count > 0 && (bytesRead = stream.Read(buffer, 0, count)) > 0)
count -= bytesRead;
if (count > 0) throw new EndOfStreamException();
}
static bool ScanForPassword(Stream stream, byte[] buffer, int sectionSize)
{
const string afterNamePadding = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
try
{
string bufferString = Encoding.ASCII.GetString(buffer, 0, buffer.Length);
// try to detect password protection used in new OpenXml documents
// by searching for "EncryptedPackage" or "EncryptedSummary" streams
const string encryptedPackageName = "E\0n\0c\0r\0y\0p\0t\0e\0d\0P\0a\0c\0k\0a\0g\0e" + afterNamePadding;
const string encryptedSummaryName = "E\0n\0c\0r\0y\0p\0t\0e\0d\0S\0u\0m\0m\0a\0r\0y" + afterNamePadding;
if (bufferString.Contains(encryptedPackageName) ||
bufferString.Contains(encryptedSummaryName))
return true;
// try to detect password protection for legacy Office documents
const int coBaseOffset = 0x200;
const int sectionIdOffset = 0x74;
// check for Word header
const string wordDocumentName = "W\0o\0r\0d\0D\0o\0c\0u\0m\0e\0n\0t" + afterNamePadding;
int headerOffset = bufferString.IndexOf(wordDocumentName, StringComparison.InvariantCulture);
int sectionId;
if (headerOffset >= 0)
{
sectionId = BitConverter.ToInt32(buffer, headerOffset + sectionIdOffset);
int sectionOffset = coBaseOffset + sectionId * sectionSize;
const int fibScanSize = 0x10;
if (sectionOffset < 0 || sectionOffset + fibScanSize > stream.Length)
return false; // invalid document
var fibHeader = new byte[fibScanSize];
stream.Seek(sectionOffset, SeekOrigin.Begin);
ReadFromStream(stream, fibHeader);
short properties = BitConverter.ToInt16(fibHeader, 0x0A);
// check for fEncrypted FIB bit
const short fEncryptedBit = 0x0100;
return (properties & fEncryptedBit) == fEncryptedBit;
}
// check for Excel header
const string workbookName = "W\0o\0r\0k\0b\0o\0o\0k" + afterNamePadding;
headerOffset = bufferString.IndexOf(workbookName, StringComparison.InvariantCulture);
if (headerOffset >= 0)
{
sectionId = BitConverter.ToInt32(buffer, headerOffset + sectionIdOffset);
int sectionOffset = coBaseOffset + sectionId * sectionSize;
const int streamScanSize = 0x100;
if (sectionOffset < 0 || sectionOffset + streamScanSize > stream.Length)
return false; // invalid document
var workbookStream = new byte[streamScanSize];
stream.Seek(sectionOffset, SeekOrigin.Begin);
ReadFromStream(stream, workbookStream);
short record = BitConverter.ToInt16(workbookStream, 0);
short recordSize = BitConverter.ToInt16(workbookStream, sizeof(short));
const short bofMagic = 0x0809;
const short eofMagic = 0x000A;
const short filePassMagic = 0x002F;
if (record != bofMagic)
return false; // invalid BOF
// scan for FILEPASS record until the end of the buffer
int offset = sizeof(short) * 2 + recordSize;
int recordsLeft = 16; // simple infinite loop check just in case
do
{
record = BitConverter.ToInt16(workbookStream, offset);
if (record == filePassMagic)
return true;
recordSize = BitConverter.ToInt16(workbookStream, sizeof(short) + offset);
offset += sizeof(short) * 2 + recordSize;
recordsLeft--;
} while (record != eofMagic && recordsLeft > 0);
}
// check for PowerPoint user header
const string currentUserName = "C\0u\0r\0r\0e\0n\0t\0 \0U\0s\0e\0r" + afterNamePadding;
headerOffset = bufferString.IndexOf(currentUserName, StringComparison.InvariantCulture);
if (headerOffset >= 0)
{
sectionId = BitConverter.ToInt32(buffer, headerOffset + sectionIdOffset);
int sectionOffset = coBaseOffset + sectionId * sectionSize;
const int userAtomScanSize = 0x10;
if (sectionOffset < 0 || sectionOffset + userAtomScanSize > stream.Length)
return false; // invalid document
var userAtom = new byte[userAtomScanSize];
stream.Seek(sectionOffset, SeekOrigin.Begin);
ReadFromStream(stream, userAtom);
const int headerTokenOffset = 0x0C;
uint headerToken = BitConverter.ToUInt32(userAtom, headerTokenOffset);
// check for headerToken
const uint encryptedToken = 0xF3D1C4DF;
return headerToken == encryptedToken;
}
}
catch (Exception ex)
{
// BitConverter exceptions may be related to document format problems
// so we just treat them as "password not detected" result
if (ex is ArgumentException)
return false;
// respect all the rest exceptions
throw;
}
return false;
}
}
這是一些不錯的探索! – jschroedl 2014-11-21 15:46:44
偉大的工作!確認它符合Word和Excel 2013和97格式。 – 2015-09-06 16:55:20
看起來它無法正確檢測97個版本的Powerpoint。如果你試圖失敗而不是掛起。我建議在打開文件路徑後立即傳遞密碼,就像這樣。 @「c:\ path \ to \ file.ppt」+「:: BadPassword ::」 – 2015-09-06 18:34:53
- 1. 檢測受密碼保護的PPT和XLS文檔
- 2. 密碼保護文檔
- 3. 密碼保護readthedocs.org文檔
- 4. 檢測密碼保護字文件
- 5. 如何檢測ssh密鑰是否受密碼保護?
- 6. 在SevenZipsharp(C#)中檢查檔案是否受密碼保護
- 7. 連接受密碼保護的文件
- 8. 通過HTTP將受密碼保護的文檔發佈到Solr
- 9. 打開密碼保護的PDF文檔
- 10. c#檢測powerpoint密碼保護
- 11. 檢查是否存在受密碼保護的網頁文件
- 12. 用密碼保護word文檔
- 13. 如何將受密碼保護的PDF保存爲非密碼保護的PDF
- 14. jQuery Plupload在上傳時檢測加密(密碼保護)文件
- 15. 檢查壓縮文件是否受密碼保護
- 16. 檢查pst文件是否受密碼保護
- 17. 檢查Excel文件是否受密碼保護
- 18. 如何在將文件上傳到服務器之前檢測Word文檔是否受密碼保護?
- 19. 受密碼保護的SQL Server備份
- 20. 閱讀受密碼保護的頁面
- 21. 受密碼保護的應用程序
- 22. 創建受密碼保護的備份
- 23. 受密碼保護的PDF Jaspersoft
- 24. 訪問受密碼保護的URL
- 25. 受密碼保護的頁面
- 26. AppMaker中受密碼保護的頁面
- 27. 加密,受密碼保護的文件創建
- 28. WordPress受保護頁面密碼Cookie
- 29. Nginx proxy_pass受密碼保護上游
- 30. 如何檢查文件是否受密碼保護/加密或不在java中
尼斯。 @ Wolf5 +1 – 2015-04-07 06:41:19
太棒了。這隻適用於辦公文件嗎?如何PDFs? – echo 2016-04-15 18:37:33
以上代碼僅適用於辦公文檔(微軟)。 PDF是Adobe的產品,他們可能有不同的方式來做到這一點。但是,在比較PDF文檔之前和之後可能很容易找到一個標誌(位置),表明它已被密碼保護。然後,只需創建一個對該位置上的值起反應的代碼。 – Wolf5 2016-04-16 12:32:22