вторник, февраля 02, 2010

Подписание данных в браузере

Гора Сен-МишельПотратил несколько дней на обзор ситуации с подписанием документов в браузере. Что удалось выяснить...

Простое подписание документа при помощи DSIG в Windows (IE) невозможно. Для этого необходимо либо использовать нерекомендованную версию MSXML (5.0), либо самостоятельно заниматься такими малоприятными вещами, как покусочное формирование XML Digital Signature. Бесплатного COM-компонента или библиотеки для выполнения этой работы не существует. Это с одной стороны. С другой, есть .NET и XSign, но требования к текущей задачи не позволяют рассчитывать на наличие у пользователя хотя бы .NET 1.1, не говоря уже об XSign.

Хоть и очень хотелось, но от XMLDSIG пришлось отказаться. Вместо этого принято решение использовать цифровые подписи PKCS #7. На клиенте (в Internet Explorer) документ подписывается и в составе скрытого поля отправляется на сервер, там подпись проверяется и если всё в порядке, то и ... всё.

Если кратко. На клиенте (CAPICOM + C++)


  1. Дать выбрать сертификат из персонального хранилища текущего пользователя.


  2. IStorePtr store(__uuidof(Store));
    HRESULT hr = store->Open(CAPICOM_CURRENT_USER_STORE, _T("My"), CAPICOM_STORE_OPEN_READ_ONLY);
    if (FAILED(hr))
    return hr;

    ICertificates2Ptr ptr(store->Certificates);
    ICertificates2Ptr selected = ptr->Select(_T(""), _T(""), VARIANT_FALSE);

  3. Проверить факт выбора сертификата и наличие закрытого ключа (а то мало ли...)

  4. Подписать сообщение

  5.  ICertificate2Ptr p(selected->Item[1]);

    ISignerPtr signer(__uuidof(CAPICOM::Signer));
    signer->PutCertificate(CComQIPtr(p));

    ISignedDataPtr data(__uuidof(SignedData));
    data->put_Content(*m_Xml);

    bstr_t signature = data->Sign(signer, FALSE, CAPICOM_ENCODE_BASE64);
    m_Signature = new CComBSTR(signature.GetBSTR());


На сервере (.NET)

  1. Декодировать (привести в бинарный вид) полученное сообщение

  2.  byte[] data = Convert.FromBase64String(signature);
    SignedCms cms = new SignedCms();
    cms.Decode(signed);


Поскольку подпись объединена с исходным сообшением, то это всё. Только не забывайте, что CAPICOM выдаёт Unicode-строки, а .NET - если его не трогать специально - ожидает UTF-8 строк.

Комментариев нет: