Excel OXML Book Protection
Защита книги и/или листа Excel паролем оказалась занимательной задачей. Статья на MSDN говорит, что для защиты книги требуется указать целых четыре параметра - workbookAlgorithmName, workbookHashValue, workbookSaltValue, workbookSpinCount. Excel при установке защиты через интерфейс использует алгоритм SHA-512 и 100000 раундов, будем использовать аналогичные параметры.
Для установки защиты книги на классы OxmlWorkBook_RU и Oxml_RU достаточно добавить два метода:
OxmlWorkBook_RU
Oxml_RU
Стоит заметить, что расчет ста тысяч раундов хэша лучше проводить в CIL
Для установки защиты книги на классы OxmlWorkBook_RU и Oxml_RU достаточно добавить два метода:
OxmlWorkBook_RU
X++:
public void protect( str _password = "", boolean _structure = false, boolean _windows = false) { DocumentFormat.OpenXml.Spreadsheet.WorkbookProtection workbookProtection; str hashValue, saltValue; int spinCount; workbookProtection = workbook.get_WorkbookProtection(); if (!workbookProtection) { workbookProtection = new DocumentFormat.OpenXml.Spreadsheet.WorkbookProtection(); workbook.set_WorkbookProtection(workbookProtection); } [hashValue, saltValue, spinCount] = Oxml_RU::calcHash(_password, saltValue, spinCount); workbookProtection.set_WorkbookAlgorithmName(OXML_RU::setStringValue("SHA-512")); workbookProtection.set_WorkbookHashValue(DocumentFormat.OpenXml.Base64BinaryValue::FromString(hashValue)); workbookProtection.set_WorkbookSaltValue(DocumentFormat.OpenXml.Base64BinaryValue::FromString(saltValue)); workbookProtection.set_WorkbookSpinCount(DocumentFormat.OpenXml.UInt32Value::FromUInt32(System.Convert::ToUInt32(spinCount))); workbookProtection.set_LockStructure(OXML_RU::setBooleanValue(_structure)); workbookProtection.set_LockWindows(OXML_RU::setBooleanValue(_windows)); }
Oxml_RU
X++:
private static server container calcHash(container _params) { System.Text.Encoding encoding = System.Text.Encoding::GetEncoding("UTF-16LE"); System.Security.Cryptography.SHA512Managed sha512 = new System.Security.Cryptography.SHA512Managed(); System.Byte[] buffer, hash, salt; System.UInt32 uint32; str password, hashValue, saltValue; int i, spinCount, hashLength, bufferLength, saltLength; str createSalt() { System.DateTime randomValue = System.DateTime::get_Now(); buffer = encoding.GetBytes(randomValue.ToString()); hash = sha512.ComputeHash(buffer); buffer = new System.Byte[16](); System.Array::Copy(hash, 0, buffer, 0, buffer.get_Length()); saltValue = System.Convert::ToBase64String(buffer); return saltValue; } [password, saltValue, spinCount] = _params; saltValue = saltValue ? saltValue : createSalt(); spinCount = spinCount > 0 ? spinCount : 100000; salt = System.Convert::FromBase64String(saltValue); saltLength = salt.get_Length(); buffer = encoding.GetBytes(password); bufferLength = buffer.get_Length(); hash = new System.Byte[bufferLength + saltLength](); System.Array::Copy(salt, 0, hash, 0, salt.get_Length()); System.Array::Copy(buffer, 0, hash, salt.get_Length(), buffer.get_Length()); hashLength = hash.get_Length(); buffer = new System.Byte[hashLength](); bufferLength = buffer.get_Length(); System.Array::Copy(hash, 0, buffer, 0, buffer.get_Length()); sha512.Initialize(); hash = new System.Byte[0](); for (i = 0; i <= spinCount; i++) { hash = sha512.ComputeHash(buffer); hashLength = hash.get_Length(); bufferLength = buffer.get_Length(); if (bufferLength != hashLength + 4) { bufferLength = hashLength + 4; buffer = new System.Byte[bufferLength](); } uint32 = System.Convert::ToUInt32(i); System.Array::Copy(hash, 0, buffer, 0, hash.get_Length()); System.Array::Copy(System.BitConverter::GetBytes(uint32), 0, buffer, hash.get_Length(), 4); } hashValue = System.Convert::ToBase64String(hash); encoding = null; sha512.Clear(); sha512 = null; return [hashValue, saltValue, spinCount]; }
Стоит заметить, что расчет ста тысяч раундов хэша лучше проводить в CIL
Всего комментариев 0