Pull to refresh

Электронная цифровая подпись на сайте при помощи КриптоПро ЭЦП Browser plug-in

В данной статье рассмотрим использование электронно-цифровой подписи на сайте.

Что необходимо, чтобы человек смог использовать электронно-цифровую подпись на сайте?

1) СКЗИ (средство криптографической защиты информации)
Мой опыт работы показывает, что порядка 90% использует КриптоПро CSP (скачать), который в явном или неявном виде продвигают удостоверяющие центры. Порядка 10% VipNet CSP (), который можно использовать бесплатно. С остальными СКЗИ на практике не встречался.
2) КриптоПро ЭЦП Browser plug-in (страница плагина).
3) Установленная подпись (хотя бы одна).

Проверка возможности осуществления подписи
javascript (+jquery)

1) Попытка создать объект cades.
Нужно сделать примечание, что тут и далее, будет деление на браузер с ActiveX(читай IE) и остальные.
Проверка будет осуществляться:

return ('ActiveXObject' in window);

для ActiveX:

try {
    store = new ActiveXObject('CAdESCOM.store');
    status = true;
} catch (e) {
    status = false;
}

Для остальных:

if (navigator.mimeTypes['application/x-cades']) {
    status = true;
} else {
    status = false;
}

Если проверка прошла неудачно, то уведомляем об этом пользователя.
Стоит иметь ввиду, что после обновления хрома до версии 42 (спасибо статье за информацию) нужно включить:

chrome://flags/#enable-npapi

Следующая проверка — а разрешен ли плагин для запуска (не для IE проверка)?

try {
    store = objSign.CreateObject('CAPICOM.store');
    status = true;
} catch (e) {
    status = false;
}

Где objSign:

objSign = $('<object/>', {
    'id': 'cadesplugin',
    'type': 'application/x-cades',
    'css': {
        'visibility': 'hidden',
        'height': '0px',
        'width': '0px',
        'position': 'absolute'
    }
}).appendTo('body').get(0);

Проверяем на СКЗИ путем попытки открыть хранилище.

try {
    store.Open();
    status = true;
} catch (e) {
    status = false;
}

Проверяем на существование сертификатов в хранилище:

if ('Certificates' in store) {
    certs = store.Certificates;
}


И их количество (бывает, что Certificates есть, но пуст, что нам тоже не подойдет):

if (certs.Count) {
    status = true;
} else {
    status = false;
}


Первый шаг сделали — проверили возможность подписания чего-либо.

Выбор электронной цифровой подписи

У клиента может быть установлено несколько сертификатов. Сертификаты могут быть от разных удостоверяющих центров (УЦ), выданными быть разным людям, с разными датами выдачами, поэтому надо предоставить выбор, каким именно он хочется воспользоваться.

1) Группируем по удостоверяющим центрам
Информация об удостоверяющем центре хранится в сертификате.

certs.Item(i).GetInfo(1)
где certs — сертификаты из хранилища, см выше
i — порядковый номер сертификата от 1 (обратите внимание) до certs.Count.
Обратите внимание, что, в случае «кривых» сертификатов, вернуться может и undefined, имеет смысл сделать один дефолтный УЦ для таких случаев.

Теперь мы знаем список УЦ, услугами которых воспользовался клиент.
Запоминаем их и выведем через optgroup.
Сам text у option будет таким:
cert.GetInfo(6) + ' (' + formatDate(cert.ValidFromDate) + ' - ' + formatDate(cert.ValidToDate) + ')'

в cert.GetInfo(6) — кому выдан сертификат
в ValidFromDate — с какого срока сертификат начал/начнет действие
в ValidToDate — соответственно, до какого срока

Ну и форматирование даты стандартное:

function formatDate(d) {
    try {
        d = new Date(d);
        return ('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2) + '.' + d.getFullYear();
    } catch (e) {
        return '';
    }
}

Еще можно подсветить option.
Зеленым — для работоспособных сертификатов, красным — нет.
Информацию можно получить при помощи самого сертификата.

try {
    return cert.IsValid().Result;
} catch (e) {
    return false;
}

Стоит отметить, что сама по себе данная проверка имеет малую ценность, ибо все причины не может отсечь.
Но самые базовые, например, проверка даты — проверяет.

В value у option запишем отпечаток cert.Thumbprint.
Можно порядковый номер записать, можно другие данные — на ваше усмотрение.

Подписание
Ну и, собственно, самый главный шаг, к которому мы стремились — подписание.

1) Находим выбранный сертификат.
Для нашего примера:

certs.Find(0, thumbprint).Item(1)

0 — означает, что мы ищем по отпечатку
1 — что используем первый результат выборки (по факту единственный)

2) Подписываем:

if (isActiveX()) {
    var CPSigner = new ActiveXObject('CAdESCOM.CPSigner');
} else {
    var CPSigner = objSign.CreateObject('CAdESCOM.CPSigner');
}
CPSigner.Certificate = cert;
if (isActiveX()) {
    var SignedData = new ActiveXObject('CAdESCOM.CadesSignedData');
} else {
    var SignedData = objSign.CreateObject('CAdESCOM.CadesSignedData');
}
SignedData.Content = text;
return SignedData.SignCades(CPSigner, 1, false);


где cert — сертификат, при помощи которого подписываем
text — собственно, что подписываем
Ну а в return возвращается подписанное сообщение.

p.s. По максимуму код постарался вычистить от специфики проекта. Если кому-то этот материал пригодится и будет интересно — напишу и серверную часть. Проверка подписанного сообщения (с цепочкой и без), проверка сертификата (ocsp и без), использования tsp и т.д.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.