Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenXML Protect Spreadsheet Workbook with Password

I am trying to create a protected spreadsheet document with OpenXML SDK. However, the WorkbookHashValue generated is not correct and thus the workbook cannot be unprotected.

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;
    }
}

The correct hash for password 123 with salt VAQd0dyl7U67APquHio1lQ== should be 2ZwXmW83qax0iUfzSkbhwAOVSDHAm6S/v9irWWTzdoFDgzO2Kc82P3Z9BAwbWqFLzN4rKaL0APOMzQ5tA7TBDw==, but the above code generated z5ebojaXN/sD4ps9yurRCpSTDp+kSuTz+HN2PyKmGuicNgszAPKxfsE+kTgOEbGhT/VqSbwTd++oyAJxJh0L3A==.

like image 693
Lucius Avatar asked Mar 25 '26 23:03

Lucius


1 Answers

The encoding used to get the password bytes should be UTF16LE

Encoding.Unicode.GetBytes("123");
like image 83
Lucius Avatar answered Mar 28 '26 13:03

Lucius



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!