Ответы пользователя по тегу Электронная цифровая подпись
  • Есть ли какой-то сервис, умеющий делать усиленную ЭЦП на документ?

    udjin123
    @udjin123
    PHP, Golang, React
    Я думаю любое ЭДО так может, в рамках внутреннего документооборота, вот сейчас в своем аккаунте в Диадоке проверил, отправил документ сам себе (сделал подразделение и ему отправил), после этого можно скачать архив с документом его подписью. API у Диадока есть )
    Ответ написан
    2 комментария
  • Как должен выглядеть процесс подписания документов квалифицированной электронной подписью?

    udjin123
    @udjin123
    PHP, Golang, React
    Почему не задать этот вопрос технокаду, по самой процедуре и тд и тп?
    Вообще закрытая часть ключа должна быть у вас, с помощью нее и формируется подпись (при подписании документа формируется его хеш и уже он подписывается, если внести изменения в документ хеш уже будет новый и подпись не действительна), передача КЭП третьим лицам сразу же компрометирует.

    Конечно еще есть вариант облачного хранения ЭЦП, решения типа КриптоПро DSS (Например сервис Контур Эльба такое использует для отчетности ИП), но что то я сомневаюсь что там это стоит...
    Ответ написан
  • Как вынуть публичный ключ из сертификата, для дальнейшей подписи и шифрования данных через библиотеку CryptoAPI?

    udjin123
    @udjin123
    PHP, Golang, React
    В КриптоПро SDK есть хорошие примеры работы с подписями на C++. Мне когда то очень помогли, когда в Golang делал работу с ЭЦП. Пришлось вставки на Си делать. Посоветую скачать и изучить.

    Вот пример от туда - открывается хранилище, получается сертификат, далее получает ссылку на закрытый ключ ну и создание и подписание хеша

    #pragma warning(disable : 4996)
    
    #include <iterator>
    #include <vector>
    #include <iostream>
    #include <wchar.h>
    
    #ifdef _WIN32
    #include <tchar.h>
    #else
    #include <cstdio>
    #include "reader/tchar.h"
    #endif
    
    #include "cades.h"
    
    /*
    Пример создания усовершенствованной подписи CADES_X_LONG_TYPE_1 с помощью
    упрощённых функций КриптоПро ЭЦП SDK по хэш-значению. Пример подписывает
    произвольные данные, которые формирует самостоятельно. Результат будет сохранен
    в файл sign.dat. Для подписи необходимо чтобы в хранилище сертификатов
    присутствовал сертификат с закрытым ключом и ссылкой на работающую OCSP службу
    */
    
    using namespace std;
    
    #include "../samples_util.h"
    
    int main(int argc, char *argv[]) {
        // Открываем хранилище сертификатов пользователя
        HCERTSTORE hStoreHandle = CertOpenSystemStore(0, _TEXT("MY"));
    
        if (!hStoreHandle) {
            cout << "Store handle was not got" << endl;
            return -1;
        }
    
        wchar_t *wa = NULL;
        if (argc > 1) {
            size_t len = strlen(argv[1]) + 1;
            wa = new wchar_t[len];
            mbstowcs(wa, argv[1], len);
        }
    
        // Получаем сертификат для подписания
        PCCERT_CONTEXT context = GetRecipientCert(hStoreHandle, wa);
        if (wa) delete[] wa;
    
        // Если сертификат не найден, завершаем работу
        if (!context) {
            cout << "There is no certificate with a CERT_KEY_CONTEXT_PROP_ID "
                 << endl << "property and an AT_KEYEXCHANGE private key available."
                 << endl
                 << "While the message could be sign, in this case, it could"
                 << endl << "not be verify in this program." << endl
                 << "For more information, read the documentation "
                    "http://cpdn.cryptopro.ru/" << endl;
            return -1;
        }
    
        HCRYPTPROV hProv(0);
    
        DWORD dwProvType = PROV_GOST_2012_256;
    
        // Получаем ссылку на закрытый ключ сертификата и дестриптор
        // криптопровайдера
        if (!CryptAcquireContext(&hProv, 0, NULL, dwProvType,
                                 CRYPT_VERIFYCONTEXT)) {
            CertFreeCertificateContext(context);
            cout << "CryptAcquireContext() failed" << endl;
            return -1;
        }
    
        // Задаем параметры
        CRYPT_SIGN_MESSAGE_PARA signPara = {sizeof(signPara)};
        signPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
        signPara.pSigningCert = context;
        signPara.HashAlgorithm.pszObjId = (LPSTR)szOID_CP_GOST_R3411_12_256;
    
        CADES_SIGN_PARA cadesSignPara = {sizeof(cadesSignPara)};
        cadesSignPara.dwCadesType = CADES_BES;
    
        CADES_SIGN_MESSAGE_PARA para = {sizeof(para)};
        para.pSignMessagePara = &signPara;
        para.pCadesSignPara = &cadesSignPara;
    
        // Формируем данные для подписания
        vector<unsigned char> data(10, 25);
    
        CERT_CHAIN_PARA		ChainPara = { sizeof(ChainPara) };
        PCCERT_CHAIN_CONTEXT	pChainContext = NULL;
    
        std::vector<PCCERT_CONTEXT> certs;
    
        if (CertGetCertificateChain(
            NULL,
            context,
            NULL,
            NULL,
            &ChainPara,
            0,
            NULL,
            &pChainContext)) {
    
            for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement - 1; ++i)
            {
                certs.push_back(pChainContext->rgpChain[0]->rgpElement[i]->pCertContext);
            }
        }
        // Добавляем в сообщение цепочку сертификатов без корневого
        if (certs.size() > 0)
        {
            signPara.cMsgCert = (DWORD)certs.size();
            signPara.rgpMsgCert = &certs[0];
        }
    
        // Получение хэша данных
        HCRYPTHASH hash(0);
        if (!CryptCreateHash(hProv, CALG_GR3411_2012_256, 0, 0, &hash)) {
            CryptReleaseContext(hProv, 0);
            CertFreeCertificateContext(context);
            cout << "CryptCreateHash() failed" << endl;
            return -1;
        }
    
        DWORD cbToBeSigned(0);
        DWORD cb = sizeof(cbToBeSigned);
        BYTE *pbToBeSigned;
    
        switch (CryptHashData(hash, &data[0], (DWORD) data.size(), 0)) {
            case TRUE:
                if (!CryptGetHashParam(hash, HP_HASHSIZE, (LPBYTE) &cbToBeSigned, &cb,
                                       0)) {
                    CryptReleaseContext(hProv, 0);
                    CertFreeCertificateContext(context);
                    CryptDestroyHash(hash);
                    cout << "CryptGetHashParam() failed" << endl;
                    return -1;
                }
    
                pbToBeSigned = new BYTE[cbToBeSigned];
    
                if (!CryptGetHashParam(hash, HP_HASHVAL, pbToBeSigned, &cbToBeSigned,
                                       0)) {
                    delete[] pbToBeSigned;
                    CryptReleaseContext(hProv, 0);
                    CertFreeCertificateContext(context);
                    CryptDestroyHash(hash);
                    cout << "CryptGetHashParam() failed" << endl;
                    return -1;
                }
                break;
            default:
                CryptReleaseContext(hProv, 0);
                CertFreeCertificateContext(context);
                CryptDestroyHash(hash);
                cout << "CryptHashData() failed" << endl;
                return -1;
        }
    
        PCRYPT_DATA_BLOB pSignedMessage = 0;
    
        string contentType(szOID_RSA_data);
    
        // Создаем подписанное сообщение
        if (!CadesSignHash(&para, pbToBeSigned, cbToBeSigned, contentType.c_str(), &pSignedMessage)) {
            delete[] pbToBeSigned;
            CryptReleaseContext(hProv, 0);
            CertFreeCertificateContext(context);
            CryptDestroyHash(hash);
            cout << "CadesSignHash() failed" << endl;
            return -1;
        }
    
         delete[] pbToBeSigned;
    
        vector<unsigned char> message(pSignedMessage->cbData);
        copy(pSignedMessage->pbData,
             pSignedMessage->pbData + pSignedMessage->cbData, message.begin());
    
        // Сохраняем результат в файл sign.dat
        if (SaveVectorToFile<unsigned char>("sign.dat", message)) {
            CryptReleaseContext(hProv, 0);
            CertFreeCertificateContext(context);
            CryptDestroyHash(hash);
            cout << "CryptHashData() failed" << endl;
            cout << "Signature was not saved" << endl;
            return -1;
        }
    
        cout << "Signature was saved successfully" << endl;
    
        // Освобождаем структуру с закодированным подписанным сообщением
        if (!CadesFreeBlob(pSignedMessage)) {
            CryptReleaseContext(hProv, 0);
            CertFreeCertificateContext(context);
            CryptDestroyHash(hash);
            cout << "CadesFreeBlob() failed" << endl;
            return -1;
        }
    
        // Закрываем хранилище
        if (!CertCloseStore(hStoreHandle, 0)) {
            CryptReleaseContext(hProv, 0);
            CertFreeCertificateContext(context);
            CryptDestroyHash(hash);
            cout << "Certificate store handle was not closed." << endl;
            return -1;
        }
    
        // Освобождаем ресурсы
        CryptReleaseContext(hProv, 0);
        CertFreeCertificateContext(context);
        CryptDestroyHash(hash);
    
        return 0;
    }
    Ответ написан
    2 комментария
  • Как реализовать эл. подпись документов на сайте?

    udjin123
    @udjin123
    PHP, Golang, React
    Модулей нормальных нет. Есть якобы электронный документооборот от КриптоАРМ, но мне не удалось его заставить нормально работать, плюс лишняя прослойка добавляется поверх КриптоПро. И если КриптоПро стоит у всех кто пользуется ЭЦП, то КриптоАРМ так не распространен и придется клиентам его ставить.

    Своими силами реализуется с помощью КриптоПро ЭЦП Browser Plugin, там есть js библиотека cades для взаимодействия с КриптоПро и подписания документов и т.д. Но проблема в том что подписать это пол дела, на сервере надо проверять подпись и верифицировать ее, иначе легко можно подписать не своей подписью или не квалифицированной (квалифицированная = юридически значимая). Для этого нужно на сервер устанавливать КриптоПро SDK и патчить php 7 чтобы добавить в php библиотеку cades, которая как раз и позволит работать с ЭЦП на сервере, проверки подписей и т.д. и т.п.
    Все это есть в документации к КриптоПро тут https://docs.cryptopro.ru/cades/
    Ответ написан
    2 комментария