AXForum  
Вернуться   AXForum > Блоги > b_nosoff
NAV
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

Рейтинг: 5.00. Голосов: 3.

Excel OXML Book Protection

Запись от b_nosoff размещена 21.07.2015 в 00:27
Обновил(-а) b_nosoff 21.07.2015 в 12:07

Защита книги и/или листа Excel паролем оказалась занимательной задачей. Статья на MSDN говорит, что для защиты книги требуется указать целых четыре параметра - workbookAlgorithmName, workbookHashValue, workbookSaltValue, workbookSpinCount. Excel при установке защиты через интерфейс использует алгоритм SHA-512 и 100000 раундов, будем использовать аналогичные параметры.

Для установки защиты книги на классы 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
Размещено в Без категории
Просмотров 31699 Комментарии 0
Всего комментариев 0

Комментарии

 


Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 17:16.