[英]OpenXML Protect Spreadsheet Workbook with Password
我正在嘗試使用OpenXML SDK創建受保護的電子表格文檔。 但是,生成的WorkbookHashValue
不正確,因此不能取消保護該工作簿。
var password = Encoding.UTF8.GetBytes("123");
var salt = new byte[16];
new RNGCryptoServiceProvider().GetNonZeroBytes(salt);
var spinCount = 100000U;
using (var document = SpreadsheetDocument.Create("text.xlsx", SpreadsheetDocumentType.Workbook))
{
var workbookPart = document.AddWorkbookPart();
var workbook = new Workbook();
WorkbookProtection workbookProtection = new WorkbookProtection()
{
LockStructure = true,
WorkbookAlgorithmName = "SHA-512",
WorkbookHashValue = Convert.ToBase64String(GetPasswordHash(password, salt, spinCount)),
WorkbookSaltValue = Convert.ToBase64String(salt),
WorkbookSpinCount = spinCount
};
var sheets = new Sheets();
var sheet = new Sheet
{
Name = "Sheet 1",
SheetId = 1U,
Id = "rId1"
};
sheets.Append(sheet);
workbook.Append(workbookProtection);
workbook.Append(sheets);
workbookPart.Workbook = workbook;
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>("rId1");
var worksheet = new Worksheet();
var sheetData = new SheetData();
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
}
private byte[] GetPasswordHash(byte[] password, byte[] salt, uint spinCount)
{
using (var sha512 = SHA512.Create())
{
var buffer = new byte[salt.Length + password.Length];
Array.Copy(salt, buffer, salt.Length);
Array.Copy(password, 0, buffer, salt.Length, password.Length);
byte[] hash = sha512.ComputeHash(buffer);
buffer = new byte[hash.Length + 4];
for (var i = 0U; i < spinCount; i++)
{
Array.Copy(hash, buffer, hash.Length);
Array.Copy(BitConverter.GetBytes(i), 0, buffer, hash.Length, 4);
hash = sha512.ComputeHash(buffer);
}
return hash;
}
}
正確的散列密碼123
與鹽VAQd0dyl7U67APquHio1lQ==
應2ZwXmW83qax0iUfzSkbhwAOVSDHAm6S/v9irWWTzdoFDgzO2Kc82P3Z9BAwbWqFLzN4rKaL0APOMzQ5tA7TBDw==
,但所產生的上述代碼z5ebojaXN/sD4ps9yurRCpSTDp+kSuTz+HN2PyKmGuicNgszAPKxfsE+kTgOEbGhT/VqSbwTd++oyAJxJh0L3A==
。
我嘗試比較Apache POI的源代碼,但沒有發現任何錯誤
public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte[] salt, int spinCount, boolean iteratorFirst) {
// If no password was given, use the default
if (password == null) {
password = Decryptor.DEFAULT_PASSWORD;
}
MessageDigest hashAlg = getMessageDigest(hashAlgorithm);
hashAlg.update(salt);
byte[] hash = hashAlg.digest(StringUtil.getToUnicodeLE(password));
byte[] iterator = new byte[LittleEndianConsts.INT_SIZE];
byte[] first = (iteratorFirst ? iterator : hash);
byte[] second = (iteratorFirst ? hash : iterator);
try {
for (int i = 0; i < spinCount; i++) {
LittleEndian.putInt(iterator, 0, i);
hashAlg.reset();
hashAlg.update(first);
hashAlg.update(second);
hashAlg.digest(hash, 0, hash.length); // don't create hash buffer everytime new
}
} catch (DigestException e) {
throw new EncryptedDocumentException("error in password hashing");
}
return hash;
}
( iteratorFirst
對於工作簿保護為false)
用於獲取密碼字節的編碼應為UTF16LE
Encoding.Unicode.GetBytes("123");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.