Показать/Скрыть содержание

    Общие сведения о клиентской подписи с использованием DSS SDK

    Цель : поддержать подпись и расшифрование документов с использованием ключей подписи, установленных в МП. При этом изменения в API DSS и SDK должны быть минимальны.

    В случае сценария подписи и расшифрования документов с подтверждением в мобильном приложении на базе DSS SDK полностью удается сохранить API DSS и SDK. Это означает, что прикладные системы и мобильные приложения, использующие DSS SDK, могут прозрачно перейти на подпись с использованием ключей, установленных в мобильном приложении.

    Примечание

    Изменения коснутся только внутреннего взаимодействия DSS SDK и серверной части КриптоПро DSS, а именно затронут процесс выпуска сертификата. При этом основным сценарием выпуска сертификата станет создание ключа подписи и запроса на сертификат в мобильном приложении средствами DSS SDK.

    Инициатором выпуска сертификата может быть как мобильное приложение (далее - МП), так и сервер DSS.

    Подтверждение операции с подписью установленными в МП ключами

    Общая схема обработки операции:

    clientside_operation.jpg

    1. DSS SDK, как и ранее, получает данные операции в структуре OperationsInfo.

    Структура содержит список операций, которые должен подтвердить пользователь. Каждая операция может содержать от 1 до N документов.

    Список операций для подтверждения DSS SDK получает с помощью метода operations.

    2. Каждую операцию из списка МП передает в DSS SDK для отображения пользователю и ее подтверждения/отклонения.

    Операция определяется структурой Operation, дополненной следующими новыми полями:

    • isClientSide [bool] - флаг, показывающий, что в операции используется клиентский ключ подписи;
    • isFullDocRequired [bool] - флаг, показывающий, что требуется загрузить документ полностью для подписи/расшифрования;
    • certificateId (int) - идентификатор сертификата подписи/расшифрования.

    Сведения о подписываемых/зашифрованных документах содержатся в поле documents структуры Operation.

    Сведения о документе определяются структурой DocumentInfo, дополненной следующими новыми полями:

    • documentPreSignedHash (string) - хэш-значение от документа, которое должен подписать пользователь. Хэш-значение представлено в формате HEX-строки.

    • XmlEncryptedKey, DocumentPreDataId (string) - данный для XML-расшифрования.

    • agreeKeyPublicKey, agreeKeyUkm, agreeKeyVkoAlgId (string) - данные для выработки ключа согласования на клиентских ключах

    Хэш-значение представлено в формате HEX-строки. XmlEncryptedKey содержит зашифрованный транспортный ключ. DocumentPreDataId содержит идентификатор документа с зашифрованными данными.

    Примечание

    Параметры операции общие для всех документов в составе операции. Это означает, что все документы могут быть подписаны либо на сервере, либо на клиенте. Смешанные сценарии подписи не допускаются.

    Примечание

    Значение, переданное в поле documentPreSignedHash, используется только в случае isFullDocRequired = false.

    Примечание

    Параметры XmlEncryptedKey, DocumentPreDataId заполняются только в случае подтверждения расшифрования XML документов. То есть когда Operation -> description -> type = decryptDocument и в раметрах указан тип расшифрования encryptionType=XML.

    Примечание

    Параметры agreeKeyPublicKey, agreeKeyUkm, agreeKeyVkoAlgId заполняются только в случае выработки ключа согласования. То есть когда Operation -> description -> type = privateKeyAccess.

    3. После того как пользователь подтвердил или отклонил операцию, DSS SDK должен определить

    • сценарий операции - клиентская или серверная - по флагу Operation->isClientSide.
    • тип операции - подпись/расшифрование/выработка ключа согласования - по параметру Operation -> description -> type (signDocument, decryptDocument, privateKeyAccess)

    Если сценарий операции серверный (isClientSide = false), то DSS SDK выполняет старый сценарий без изменений.

    Если сценарий операции клиентский (isClientSide = true), то DSS SDK выполняет новый сценарий.

    Тип операции Operation->description->type определяет методы SDK, которые необходимо будет вызвать для клиентского сценария (isClientSide = true):

    • signDocument - sign, signCms
    • decryptDocument - decryptCms, decryptXml
    • privateKeyAccess - createVKO
    • для иных типов операции сценарий подтверждение/отклонения не меняется

    Примеры запросов на выполнение различных операций приведены ниже.

    • XML-расшифрование. Метод decryptXml
    • CMS-расшифрование. Метод decryptCms
    • Подпись хэш-значения. Метод sign
    • CMS-подпись. Метод signCms
    • Выработка ключа согласования

    Операции типа Operation -> description -> type = privateKeyAccess не требуют обработки документов. Все необходимые данные передаются в составе операции. Из поля Operation -> DocumentInfo[0] необходимо взять значения полей agreeKeyPublicKey, agreeKeyUkm и декодировать из HEX-строки в бинарное представление. Параметр agreeKeyVkoAlgId передается в метод криптобиблиотеки как есть.

    Выработанный ключ согласования возвращается в параметре raw_data метода createVKO. Необходимо заранее выделить буфер размером 64 байта. Указатель на данный буфер передается в параметре raw_data. При успешном выполнении метода буфер будет заполнен значение ключа согласования. Фактический размер данных возвращается в параметре raw_data_length.

    Для передачи результата на сервер необходимо скопировать фактические данные размером raw_data_length и закодировать их в HEX строку. Полученную HEX-строку нужно передать через словарь параметров ApprovedOperation -> parameters в ключе AgreeKeyAgreeKey

    int res1 = MyDssNativeMethods.createVKOWrapper(
                                        containerInfo.ProviderName,
                                        containerInfo.ProviderType,
                                        containerInfo.ContainerName,
                                        "",
                                        pubKey,
                                        pubKey.Length,
                                        ukm,
                                        ukm.Length,
                                        vkoAlgId,
                                        rawKeyPre,
                                        ref rawKeyLength);
    if (res1 != 0)
    {
       // обработка ошибки
    }
    
    byte[] rawKey = new byte[rawKeyLength];
    Array.Copy(rawKeyPre, rawKey, rawKeyLength);
    
    approvedOp.Parameters = new Dictionary<string, string>()
    {
        { MessageParameters.AgreeKeyAgreeKey, BitConverter.ToString(rawKey).Replace("-", "") }
    };
    

    Ниже приводится порядок обработки операций типа signDocument, privateKeyAccess. Для клиентской операции DSS SDK определяет тип подписания - полный документ или его хэш-значение - по флагу isFullDocRequired:

    • isFullDocRequired = true - DSS SDK должен загрузить документ полностью для подписания.
    • isFullDocRequired = false - DSS SDK должен подписать хэш-значение, переданное в параметре documentPreSignedHash каждого из документов.
    Примечание

    Для операций расшифрования isFullDocRequired всегда равен true. В случае XML расшифрования идентификатор документа передается в параметре DocumentPreDataId структуры DocumentInfo.

    Для загрузки документа должен быть использован метод data с указанием идентификатора загружаемого документа. Идентификатор документа содержится в поле Id структуры DocumentInfo.

    Хэш-значение или загруженный документ DSS SDK должен подписать/расшифровать, используя методы криптобиблиотеки. Загруженный документ может быть подписан следующими способами:

    • Присоединенная CMS-подпись
    • Отделенная CMS-подпись
    • Необработанная подпись

    Загруженный документ может быть расшифрован следующими способами:

    • Enveloped CMS
    • Encrypted XML

    Способ подписания передается в поле parameters структуры Operation. Поле parameters представляет собой словарь string - string.

    Возможные значения ключей словаря и параметры подписи

    В приведенных выше сценариях возможны следующие значения ключей:

    • signatureType - тип подписи
    • isDetached - тип CMS-подписи

    Для ключа signatureType в сценарии с SDK значимы следующие значения:

    • GOST3410 (1) - Необработанная подпись
    • CMS (5) - CMS-подпись
    • CAdES (2) - CMS-подпись

    Для ключа isDetached в сценарии с SDK значимы следующие значения:

    • true - отделенная CMS-подпись
    • false - Присоединенная CMS-подпись

    Для ключа encryptionType в сценарии с SDK значимы следующие значения:

    • CMS - расшифрование CMS сообщения
    • XML - расшифрование XML документа

    В зависимости от переданного набора параметров подписи, DSS SDK выбирает метод криптобиблиотеки для подписания или расшифрования.

    Примечание

    Необходимость загрузки и подписи документа, а не его хэш-значения, продиктованы правилами использования CSP (белым списком методов API).

    В результате подписания DSS SDK получает либо Необработанную подпись, либо CMS-подпись, либо расшифрованный документ

    В случае подписания полного документа (isFullDocRequired = true) и расшифрования DSS SDK должен загрузить подписанный/расшифрованный документ на сервер, используя метод data. После загрузки подписанного/расшифрованного документа DSS SDK получит его идентификатор.

    Выбор сертификата подписи/расшифрования осуществляется при помощи параметра certificateId структуры Operation. МП должно хранить связь идентификатора сертификата подписи и имени контейнера с ключом подписи.

    Примечание

    Имя контейнера ключа подписи формируется в DSS SDK при создании (или установке) ключа подписи. Серверу DSS неизвестны имена контейнеров с ключами подписи в МП. Идентификатор сертификата создается на сервере в момент, когда МП регистрирует сертификат на сервере. Соответственно после регистрации сертификата на сервере DSS SDK должен сохранить связь "идентификатор сертификата - имя контейнера - идентификатор УЗ".

    Если в словаре параметров передан signatureType равный CMS (5) или CAdES (2) (данные значения эквивалентны), необходимо вызывать метод криптобиблиотеки signCms.

    Параметр is_detached метода signCms заполняется на основе флага isDetached переданного в словаре параметров операции Parameters.

    Пример:

    int res1 = signCms(
                    containerInfo.ProviderName,
                    containerInfo.ProviderType,
                    containerInfo.ContainerName,
                    "",
                    isDetached ? 1 : 0,
                    docBytes,
                    docBytes.Length,
                    null,
                    ref signatureLength);
    
    if (res1 != 234)
    {
        // error
    }
    
    byte[] singature = new byte[signatureLength];
    res1 = signCms(
                containerInfo.ProviderName,
                containerInfo.ProviderType,
                containerInfo.ContainerName,
                "",
                isDetached ? 1 : 0,
                docBytes,
                docBytes.Length,
                singature,
                ref signatureLength);
    
    if (res1 != 0)
    {
        // error
    }
    

    Если в словаре параметров передан signatureType, равный GOST3410 (1), необходимо вызывать метод криптобиблиотеки sign. Значение параметра is_hash метода sign определяется по параметру IsFullDocRequired в данных операции.

    Пример:

    int res1 = sign(
                    containerInfo.ProviderName,
                    containerInfo.ProviderType,
                    containerInfo.ContainerName,
                    "",
                    opData.IsFullDocRequired ? 0 : 1,
                    docBytes,
                    docBytes.Length,
                    null,
                    ref signatureLength);
    
    if (res1 != 234)
    {
        // error
    }
    
    byte[] singature = new byte[signatureLength];
    res1 = sign(
                containerInfo.ProviderName,
                containerInfo.ProviderType,
                containerInfo.ContainerName,
                "",
                opData.IsFullDocRequired ? 0 : 1,
                docBytes,
                docBytes.Length,
                singature,
                ref signatureLength);
    
    if (res1 != 0)
    {
        // error
    }
    

    В примерах вызова методов sign и signCms в параметре data_to_sign передаются:

    • isFullDocRequired = true - загруженный по идентификатору Operation->documents[i]->id документ
    • isFullDocRequired = false - значение переданное в параметре Operation->documents[i]->documentPreSignedHash

    В случае isFullDocRequired = true, необходимо будет загрузить подписанный документ на сервер, используя метод data. В ответ сервер вернет ID подписанного документа. Данный идентификатор будет использован на следующем шаге при заполенении параметра signedDocId.

    В случае isFullDocRequired = false полученное значение подписи будет использован на следующем шаге при заполенении параметра signedHash.

    Если в словаре параметров передан encryptionType равный CMS, то необходимо вызывать метод криптобиблиотеки decryptCMS.

    int res1 = MyDssNativeMethods.decryptCms(
                            containerInfo.ProviderName,
                            containerInfo.ProviderType,
                            containerInfo.ContainerName,
                            "",
                            docBytes,
                            docBytes.Length,
                            null,
                            ref signatureLength);
    
    if (res1 != 234)
    {
        // error
    }
    
    signature = new byte[signatureLength];
    res1 = MyDssNativeMethods.decryptCms(
                            containerInfo.ProviderName,
                            containerInfo.ProviderType,
                            containerInfo.ContainerName,
                            "",
                            docBytes,
                            docBytes.Length,
                            signature,
                            ref signatureLength);
    
    if (res1 != 0)
    {
       // error
    }
    

    Если в словаре параметров передан encryptionType равный XML, то необходимо вызывать метод криптобиблиотеки decryptXml.

    Так же необходимо проверить наличие ключа useFssScenario в словаре параметров. Если ключ useFssScenario отсутствует или равен false, то в метод decryptXml необходимо передать 0 в параметр is_fss. Если ключ присутствует и равен true, то в метод decryptXml необходимо передать 1 в параметр is_fss.

    Примечание

    DSS SDK должно сразу выделить буфер для расшифрованного документа. Размер буфера должен быть на 8 байт меньше размера зашифрованного документа.

    Размер расшифрованного текста возвращается в переменной plain_data_length. В сервис обработки документов должен быть загружен документ именно такой длинны.

    Зашифрованный транспортный ключ передается параметре XmlEncryptedKey в данных операции. Значение параметра XmlEncryptedKey передается в HEX-формате. Для передачи в метод decryptXml значение должно быть декодировано в бинарное представление.

    byte[] encryptedKey = doc.XmlEncryptedKey.FromHexToBytes();
    plainText = new byte[docBytes.Length - 8];
    plainTextLength = docBytes.Length - 8
    int res = MyDssNativeMethods.decryptXmlWrapper(
                            containerInfo.ProviderName,
                            containerInfo.ProviderType,
                            containerInfo.ContainerName,
                            "",
                            fUseFss
                            docBytes,
                            docBytes.Length,
                            encryptedKey,
                            encryptedKey.Length,
                            plainText,
                            ref plainTextLength);
    
    if (res != 0)
    {
        // error
    }
    

    4. Далее DSS SDK должен сформировать структуру с одобрением/отклонением операции ApproveRequest.

    Правила вычисления HMAC остаются прежними.

    Изменяются правила заполнения ApproveRequest -> ApprovedOperation -> confirmedDocuments.

    Структура ConfirmedDocument дополняется следующими новыми полями:

    • signedHash - string - значение подписи (в HEX-представлении)
    • signedDocId - string - идентификатор загруженного подписанного документа

    Поле signedHash заполняется в случае isFullDocRequired = false Поле signedDocId заполняется в случае isFullDocRequired = true

    При отклонении операции изменений нет. Структура DeclinedDocument заполняется как и ранее.

    Примеры запросов на подпись на ключах в мобильном приложении:

    Расшифрование XML документа:

    Пример XML расшифрования

    {
        "Description": {
            "Type": "DecryptDocument",
            "Caption": "{\"values\": {\"dss\": \"КриптоПро DSS\",\"date\": \"03.10.2021 18:40:56\",\"login\": \"xmv_self2\",\"certid\": \"57165\", \"sessionid\":\"tx584yay\",\"optype\":\"Расшифрование\" },\"keys\": {\"dss\": \"Сервер ЭП\",\"date\": \"Время\",\"login\": \"Логин пользователя\",\"certid\":\"Идентификатор сертификата\",\"sessionid\":\"Идентификатор запроса\",\"optype\":\"Операция\"}}"
        },
        "ExpiresAt": 1633276256,
        "DocumentCount": 1,
        "TransactionId": "a0bb0875-82ac-4a76-9592-594fb3f3247e",
        "Parameters": {
            "encryptionType": "XML"
        },
        "Documents": [
            {
                "Id": "cbb7f696-0bf9-4ccc-987c-696030d97e77",
                "DocumentInfo": "a38e8011-bab2-457d-bf0f-e76c5d45f51f",
                "DocumentHash": "D85E7427C362D4A1EC7F0707188259BB4F20DC52C75EFEB66D8C5F35A055BA2C",
                "Snippet": "<html lang=\"ru\">\r\n  <head>\r\n    <meta charset=\"UTF-8\" />\r\n    <style>body{{font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\"; padding: 16pt;}} @media (prefers-color-scheme: dark) {  body {    background: #484848; color:white; } }</style>\r\n  </head>\r\n  <body>\r\n    <p>Имя документа:</p>\r\n    <p>a38e8011-bab2-457d-bf0f-e76c5d45f51f</p>\r\n  </body>\r\n</html>",
                "SnippetHash": "20718A7AF682C856FF0850A1C1CA6734B6ABBA00E9CAFEEB6B4EA60191492C4C",
                "FileSize": 6993,
                "PageCount": 0,
                "IsPrintableViewAvailable": false,
                "IsSnippetViewAvailable": true,
                "IsRawViewAvailable": false,
                "DocumentPreSignedHash": null,
                "XmlEncryptedKey": "3081A9302804205D1B451087A33B2FAE1B4C384C1F5846DCBB3B9087CF57B50F8B19065137102604048836F977A07D06092A8503070102050101A066301F06082A85030701010101301306072A85030202240006082A850307010102020343000440A3B5E7A87C26F5BBEE81720385D3E749F25CF410114509617171C6FC2EAD37BF481AA03EA3EF247E998197B821278288C2BB38603D7829947573F5F2DABBC2EB0408F2DC498A419F263F",
                "DocumentPreDataId": "9f131c51-28e3-4aed-befc-2a226c33b4fe"
            }
        ],
        "IsClientSide": true,
        "IsFullDocRequired": true,
        "CertificateId": "57165"
    }
    

    Пример XML-расшифрования ФСС

    
    {
        "Description": {
            "Type": "DecryptDocument",
            "Caption": "{\"values\": {\"dss\": \"КриптоПро DSS\",\"date\": \"13.10.2021 14:47:06\",\"login\": \"xmv_self2\",\"certid\": \"57232\", \"sessionid\":\"zzzewyay\",\"optype\":\"Расшифрование\" },\"keys\": {\"dss\": \"Сервер ЭП\",\"date\": \"Время\",\"login\": \"Логин пользователя\",\"certid\":\"Идентификатор сертификата\",\"sessionid\":\"Идентификатор запроса\",\"optype\":\"Операция\"}}"
        },
        "ExpiresAt": 1634126226,
        "DocumentCount": 1,
        "TransactionId": "5537d397-2b02-41d6-a67e-ea8a0f9ca0f8",
        "Parameters": {
            "encryptionType": "XML",
            "useFssScenario": "true"
        },
        "Documents": [
            {
                "Id": "ffc8a286-942a-4e42-9f09-f73e58c43c91",
                "DocumentInfo": "C:\\Users\\xmv\\Downloads\\withClientSignature.enc.enc.xml",
                "DocumentHash": "E568F785A4A7D4558E99AFFD01E828EB9E334E8C2E33E39D2EBDD5901DDD6818",
                "Snippet": "<html lang=\"ru\">\r\n  <head>\r\n    <meta charset=\"UTF-8\" />\r\n    <style>body{{font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\"; padding: 16pt;}} @media (prefers-color-scheme: dark) {  body {    background: #484848; color:white; } }</style>\r\n  </head>\r\n  <body>\r\n    <p>Имя документа:</p>\r\n    <p>C:\\Users\\xmv\\Downloads\\withClientSignature.enc.enc.xml</p>\r\n  </body>\r\n</html>",
                "SnippetHash": "522B9FEDCC2EB8066AA44153097886CA28DCAF8168F06343E7A10C06CEF37211",
                "FileSize": 11271,
                "PageCount": 0,
                "IsPrintableViewAvailable": false,
                "IsSnippetViewAvailable": true,
                "IsRawViewAvailable": true,
                "DocumentPreSignedHash": null,
                "XmlEncryptedKey": "3081A930280420A090D8ED65DA49BEF5AF659B5CC86F1E14C9A20665DA791F00209ABD6D480F1D0404289BA260A07D06092A8503070102050101A066301F06082A85030701010101301306072A85030202240006082A850307010102020343000440C126CEFAC82DA5C88287AA6F423A6501E0470DD19E95C8535DDCB721113C945A12D426A405C04181B1603B22E32DB8C5DB6BE1AB2AB5D54FF60EE89DAC78DAA60408D4C1888AEF048D5C",
                "DocumentPreDataId": "23576d9b-3fbf-45a4-915a-e0840aafcfcd"
            }
        ],
        "IsClientSide": true,
        "IsFullDocRequired": true,
        "CertificateId": "57232"
    }
    

    Расшифрование CMS-документа:

    {
        "Description": {
            "Type": "DecryptDocument",
            "Caption": "{\"values\": {\"dss\": \"КриптоПро DSS\",\"date\": \"03.10.2021 21:10:41\",\"login\": \"xmv_self2\",\"certid\": \"57165\", \"sessionid\":\"woczhyay\",\"optype\":\"Расшифрование\" },\"keys\": {\"dss\": \"Сервер ЭП\",\"date\": \"Время\",\"login\": \"Логин пользователя\",\"certid\":\"Идентификатор сертификата\",\"sessionid\":\"Идентификатор запроса\",\"optype\":\"Операция\"}}"
        },
        "ExpiresAt": 1633285241,
        "DocumentCount": 1,
        "TransactionId": "72ee30c5-f972-44bf-af7c-d7ee9b3c57ae",
        "Parameters": {
            "encryptionType": "CMS"
        },
        "Documents": [
            {
                "Id": "e4448e74-1dc0-47b0-8171-1d81ac918cd4",
                "DocumentInfo": "93fa4207-08ee-4b44-922d-53437903c6c0",
                "DocumentHash": "4F2229EFDF3E532982F2C5E7183902D99EF334B6E62D98D807B663B36DAC97C2",
                "Snippet": "<html lang=\"ru\">\r\n  <head>\r\n    <meta charset=\"UTF-8\" />\r\n    <style>body{{font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\"; padding: 16pt;}} @media (prefers-color-scheme: dark) {  body {    background: #484848; color:white; } }</style>\r\n  </head>\r\n  <body>\r\n    <p>Имя документа:</p>\r\n    <p>93fa4207-08ee-4b44-922d-53437903c6c0</p>\r\n  </body>\r\n</html>",
                "SnippetHash": "883FF78DFE0FE5D7E3611FCA04D67D5C26BC669D120F18D46DB3093A9FFB1F03",
                "FileSize": 3095,
                "PageCount": 0,
                "IsPrintableViewAvailable": false,
                "IsSnippetViewAvailable": true,
                "IsRawViewAvailable": false,
                "DocumentPreSignedHash": null,
                "XmlEncryptedKey": null,
                "DocumentPreDataId": null
            }
        ],
        "IsClientSide": true,
        "IsFullDocRequired": true,
        "CertificateId": "57165"
    }
    

    Подпись хэш-значения:

    {
        "Description": {
            "Type": "SignDocument",
            "Caption": "{\"values\": {\"dss\": \"КриптоПро DSS\",\"date\": \"03.10.2021 19:13:34\",\"login\": \"xmv_self2\",\"certid\": \"57165\", \"sessionid\":\"g8984yay\",\"optype\":\"Подпись\" },\"keys\": {\"dss\": \"Сервер ЭП\",\"date\": \"Время\",\"login\": \"Логин пользователя\",\"certid\":\"Идентификатор сертификата\",\"sessionid\":\"Идентификатор запроса\",\"optype\":\"Операция\"}}"
        },
        "ExpiresAt": 1633278214,
        "DocumentCount": 1,
        "TransactionId": "10ffabbc-1102-491e-bc7d-22380c9478d5",
        "Parameters": {
            "signatureType": "GOST3410"
        },
        "Documents": [
            {
                "Id": "58a7b075-5366-4708-860d-175fceaa0f35",
                "DocumentInfo": "test2.txt",
                "DocumentHash": "90F4D89125915C46EBB74E9765DA2E34011AAC6101E70266F0129C90B942F5F8",
                "Snippet": "<html lang=\"ru\">\r\n  <head>\r\n    <meta charset=\"UTF-8\" />\r\n    <style>body{{font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\"; padding: 16pt;}} @media (prefers-color-scheme: dark) {  body {    background: #484848; color:white; } }</style>\r\n  </head>\r\n  <body>\r\n    <p>Имя документа:</p>\r\n    <p>test2.txt</p>\r\n  </body>\r\n</html>",
                "SnippetHash": "A8AC3F0FD4E5A4022BF1C64EA5ECD9D482B6D692375D4592A4EB7CCD930DCA39",
                "FileSize": 2431,
                "PageCount": 0,
                "IsPrintableViewAvailable": false,
                "IsSnippetViewAvailable": true,
                "IsRawViewAvailable": true,
                "DocumentPreSignedHash": "26ACC663CA65BEF1A64F5FE1385662E683489DB6CF7BFB44D1950BAE4BEEEB05",
                "XmlEncryptedKey": null,
                "DocumentPreDataId": null
            }
        ],
        "IsClientSide": true,
        "IsFullDocRequired": false,
        "CertificateId": "57165"
    }
    

    CMS-подпись отделенная (целый документ)

    
    {
        "Description": {
            "Type": "SignDocument",
            "Caption": "{\"values\": {\"dss\": \"КриптоПро DSS\",\"date\": \"03.10.2021 19:24:25\",\"login\": \"xmv_self2\",\"certid\": \"57165\", \"sessionid\":\"zoy8hyay\",\"optype\":\"Подпись\" },\"keys\": {\"dss\": \"Сервер ЭП\",\"date\": \"Время\",\"login\": \"Логин пользователя\",\"certid\":\"Идентификатор сертификата\",\"sessionid\":\"Идентификатор запроса\",\"optype\":\"Операция\"}}"
        },
        "ExpiresAt": 1633278865,
        "DocumentCount": 1,
        "TransactionId": "88ea6428-81bc-492c-af7a-e4dfda166a94",
        "Parameters": {
            "signatureType": "CMS",
            "isDetached": "True"
        },
        "Documents": [
            {
                "Id": "58a7b075-5366-4708-860d-175fceaa0f35",
                "DocumentInfo": "test2.txt",
                "DocumentHash": "90F4D89125915C46EBB74E9765DA2E34011AAC6101E70266F0129C90B942F5F8",
                "Snippet": "<html lang=\"ru\">\r\n  <head>\r\n    <meta charset=\"UTF-8\" />\r\n    <style>body{{font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\"; padding: 16pt;}} @media (prefers-color-scheme: dark) {  body {    background: #484848; color:white; } }</style>\r\n  </head>\r\n  <body>\r\n    <p>Имя документа:</p>\r\n    <p>test2.txt</p>\r\n  </body>\r\n</html>",
                "SnippetHash": "A8AC3F0FD4E5A4022BF1C64EA5ECD9D482B6D692375D4592A4EB7CCD930DCA39",
                "FileSize": 2431,
                "PageCount": 0,
                "IsPrintableViewAvailable": false,
                "IsSnippetViewAvailable": true,
                "IsRawViewAvailable": true,
                "DocumentPreSignedHash": null,
                "XmlEncryptedKey": null,
                "DocumentPreDataId": null
            }
        ],
        "IsClientSide": true,
        "IsFullDocRequired": true,
        "CertificateId": "57165"
    }
    

    Выработка ключа согласования

    {
      "Description": {
        "Type": "PrivateKeyAccess",
        "Caption": "Доступ к закрытому ключу. Сертификат: Cloud Test Key Access."
      },
      "ExpiresAt": 1642532230,
      "DocumentCount": 1,
      "TransactionId": "e0a27324-9e57-44a9-9f6d-a7b47f849d56",
      "Parameters": {},
      "Documents": [
        {
          "Id": "00000000-0000-0000-0000-000000000000",
          "DocumentInfo": "",
          "DocumentHash": "B9D07EE290F04547E44B7D202688A8119F2B2EE30927B58F50FA4C8E07FC149D",
          "Snippet": "<html>Not supported</html>",
          "SnippetHash": "B9D07EE290F04547E44B7D202688A8119F2B2EE30927B58F50FA4C8E07FC149D",
          "FileSize": 0,
          "PageCount": 0,
          "IsPrintableViewAvailable": false,
          "IsSnippetViewAvailable": true,
          "IsRawViewAvailable": false,
          "DocumentPreSignedHash": null,
          "XmlEncryptedKey": null,
          "DocumentPreDataId": null,
          "AgreeKeyUkm": "03C3812B75B0327E",
          "AgreeKeyPublicKey": "9AAC9998F232EFE3C6078854808AEBAA03A8D7E05F4FB6C89A0693C585CA1F1D0EA29619A59A8301409C7A29FBC689BE66D286B6FE9064C00E29EC6BBC524395",
          "AgreeKeyVkoAlgId": "0"
        }
      ],
      "IsClientSide": true,
      "IsFullDocRequired": false,
      "CertificateId": "63819"
    }
    

    Инициированная из МП подпись на установленных в МП ключах

    Подпись на ключах в МП, инициированная из МП, состоит из трех шагов

    • Отправка документов, параметров подписи на сервер для получения данных, которые требуется подписать (вызов /sign/ex)
    • Отображение пользователю данных операции и подпись данных
    • Отправка значений подписи для получения подписанных документов (вызов /confirm/ex)

    Первый шаг - Начало операции подписи

    Обработка полученных в результате вызова сведений об операции - аналогична подтверждению серверной операции (см. описание выше): В результате вызова /sign/ex SDK получает данные операции, которые обрабатываются так же как если бы это было подтверждение серверной операции.

    В результате получается структура подписи документов на клиентских ключах ApproveRequest (как для подтверждения серверной подписи)

    Второй шаг - Отправка подписанных данных

    Выпуск сертификата пользователя (API DSS)

    КриптоПро DSS предоставляет следующие основные сценарии выпуска сертификата для мобильного устройства пользователя:

    • инициированный на сервере КриптоПро DSS
    • инициированный в мобильном приложении.

    Выпуск сертификата, инициированный на сервере КриптоПро DSS

    1. Оператор через API DSS (или Веб-интерфейс DSS) создает неподписанный запрос на сертификат.
    2. Пользователь в мобильном приложении создает ключ подписи и подписывает запрос на сертификат. Подписанный запрос на сертификат передается на сервер DSS.
    3. В зависимости от настроек КриптоПро DSS, подписанный запрос на сертификат передается в Удостоверяющий Центр для обработки.
    4. Выпущенный сертификат устанавливается в DSS и мобильное приложение.

    Создание неподписанного запроса на сертификат осуществляется через конечную точку /request. Как и в случае ключей, хранимых в DSS, Оператор выбирает зарегистрированный в DSS Удостоверяющий Центр, Шаблон сертификата и заполняет имя субъекта. Для задания способа хранения ключа на мобильном устройстве в объекте CertificateRequest в поле Parameters необходимо будет указать соответствующий флаг (IsClient: true). У объекта DssCertRequest, представляющего созданный запрос на сертификат, появится дополнительный статус Status – SIGN_WAIT. Данный статус сообщает Прикладной системе, что запрос на сертификат ожидает подписания в мобильном приложении. После того как пользователь подпишет запроса на сертификат, запрос перейдет в статус PENDING. Обработка запроса в статусе PENDING зависит от типа УЦ, к которому создавался запрос на сертификат.

    Если КриптоПро DSS подключен к КриптоПро УЦ 2.0, то DSS автоматически отправит запрос в УЦ и загрузит выпущенный сертификат. Если запрос создавался для «Стороннего УЦ» (тип Out-of-Band), то подписанный запрос на сертификат должен быть выгружен Прикладной системой из DSS (конечная точка /requests). или пользователем в мобильном приложении и передан в Удостоверяющий Центр для обработки. Выпущенный сертификат может быть установлен в DSS Прикладной системой через API DSS и далее, используя API DSS SDK, установлен в мобильном приложении пользователя. Также выпущенный сертификат может быть передан непосредственно пользователю для установки его в мобильное приложение и регистрации на сервере DSS. Таким образом, сценарий похож на сценарий выпуска сертификата с ключом, хранимым на сервере DSS. Но возникает дополнительный шаг, когда прикладная система ожидает подпись запроса на сертификат в мобильном приложении. Уведомление пользователя о необходимости подписать запрос на сертификат может быть выполнено через Push-уведомление (средствами DSS), или любым другим образом доступным Прикладной системе. Пользователь может самостоятельно в мобильном приложении проверить наличие запросов, требующих подписи.

    Выпуск сертификата, инициированный в мобильном приложении

    1. Пользователь в мобильном приложении создает ключ подписи и запрос на сертификат.
    2. Запрос на сертификат тем или иным образом передается в Удостоверяющий Центра для обработки.
    3. Выпущенный сертификат устанавливается в DSS и мобильное приложение. На шаге 2 подписанный запрос на сертификат, используя API SDK, может быть загружен в DSS. Таким образом сценарий сводится к предыдущему.

    Выпуск сертификата пользователя (DSS SDK)

    Последовательность действий при выпуске сертификата:

    • Получить неподписанный запрос на сертификат
    • Создать ключ подписи и подписать запроса на сертификат
    • Отправить на сервер подписанный запрос на сертификат
    • Ожидать обработки запроса на сертификат
    • Установить в контейнер ключа и зарегистрировать его на сервере

    Получить неподписанный запрос на сертификат можно одним из следующих вариантов:

    • Получить неподписанный запрос на сертификат с сервера (т.е. запрос создан Оператором DSS через серверное API DSS)
    • Отправить запрос на создание неподписанного запроса на сертификат из МП

    Для получения списка сертификатов и запросов на сертификаты SDK должен использовать метод certificates.

    Структура, определяющая сертификат или запрос на сертификат Certificate, дополняется следующими новыми полями:

    • isClient (bool) - Тип хранения закрытого ключа сертификата: на сервере или на клиенте.

    Для запросов на сертификаты (тип объекта type=req) появляется новый статус state - sign_wait. Данный статус говорит мобильному приложению, что требуется подписать запрос на сертификат.

    Таким образом, если МП в списке объектов, полученных из конечной точки Certificate, нашло объект с type=req и state=sign_wait, то оно должно предложить пользователю продолжить создание запроса на сертификат.

    Так же МП пожет отправить запрос на создание неподписанного запроса на сертификат. Порядок и параметры создания запроса на сертификат полностью аналогичны сценарию создания запроса на сертификат с ключом, хранимым на сервере. То есть необходимо выбрать модуль УЦ и заполнить параметры запроса (имя субъекта и шаблон сертификата). Метод для создания неподписанного запроса на сертификат certificates

    Подпись запроса на сертификат состоит из следующих шагов:

    • создание ключа подписи - метод криптобиблиотеки createKey
    • подпись запроса на сертификат - метод криптобиблиотеки signCertRequest

    Пример создания ключа:

     createKey(null, 80, containerName, "")
    

    Параметры prov_name (null), prov_type (80) - имеют фиксированные значения. Имя ключевого контейнера container_name SDK задает на свое усмотрение. ПИН-код (pin) приложение задает на свое усмотрение.

    Примечание

    Приложение может сохранить следующие сведения о созданном ключе: prov_name, prov_type, container_name, uid, rid, cid, isinstalled.

    В будущем могут быть использованы другие имена и типы криптопровайдеров (prov_name, prov_type) для поддержки других алгоритмов ключей подписи (prov_type:81 и др.).

    Идентификатор запроса на сертификат (rid) и идентификатор сертификата (cid) необходимы в будущем для:

    • установки выпущенного сертификата в контейнер
    • выбор сертификата подписи для подтверждения операции

    isinstalled - флаг, был ли установлен сертификат в контейнер.

    Пример подписи запроса на сертификат:

     if(signCertRequestWrapper(null, 80, containerName, "", request, request.Length, null, ref signedRequestLenght) != 234)
     {
          // error
     }
     byte[] signedRequest = new byte[signedRequestLenght];
    
    if (signCertRequestWrapper(null, 80, containerName, "", request, request.Length, signedRequest, ref signedRequestLenght) != 0)
    {
        // error
    }
    

    Подпись запроса на сертификат происходит в два вызова. Первый вызов определяет размер буфера для сохранения подписанного запроса на сертификат. Второй вызов вернет подписанный запрос на сертификат.

    Примечание

    Первый вызов данного метода должен вернуть ошибку ERROR_MORE_DATA (234). Если метод вернул иную ошибку, то метод завершился неуспешно. Аналогичным образом ведут себя другие методы, требующие выделения памяти под возвращаемый результат.

    Полученный подписанный запрос на сертификат необходимо отправить на сервер для обработки. Для отправки используется метод certificates.

    В запросе необходимо указать:

    • идентификатор неподписанного запроса на сертификат,
    • идентификатор модуля УЦ,
    • подписанный запрос на сертификат.

    В ответ сервер вернет объект Certificate, определяющий запрос на сертификат или выпущенный сертификат. Это означает, что если тип полученного объекта type=req и статус status=pending, то запрос принят в обработку.

    Как было описано выше, SDK должен сохранить следующие значения:

    • prov_name,
    • prov_type,
    • container_name,
    • uid,
    • rid,
    • isinstalled.

    Если тип полученного объекта type=crt и статус status=active или out_of_band, то DSS обработал запрос на сертификат и получил сертификат. SDK должен установить сертификат в контейнер, используя метод криптобиблиотеки installCertificate. При этом флаг isinstalled должен иметь значение true.

    Обработанный запрос на сертификат (для которого выпущен сертификат) переходит в статус status=accepted. В объекте запроса на сертификат со стутусом status=accepted будут заполнены оба параметра rid, cid - т.е. ссылка на соответствующий объект сертификата.

    Соответственно SDK должен найти в списке объектов certificates объект сертификата и установить сертификат в контейнер.

    Пример установки сертификата

    if (0 != installCertificate("", 80, containerName, "", request.Content, request.Content.Length))
    {
        // error
    }
    
    Примечание

    Если создание запроса на сертификат (подпись запроса на сертификат, отправка запроса на сервер и т.п.) завершились с ошибкой, то рекомендуется удалить созданный ключ подписи. Это требуется для того чтобы в МП не оставались "мертвые" ключи.

    Для удаления ключевого контейнера используется метод криптобиблиотеки deleteKey.

    Перечислить ключи, которые хранит CSP, можно с помощью метода криптобиблиотеки enumKeys. Вызов данного метода должен происходить в цикле, пока метод не вернет код 1. Каждый вызов метода будет возвращать следующее имя контейнера.

    Примечание

    В качестве буфера для заполнения имени контейнера в метод enumKeys необходимо передать буфер размером 256 байт. Буфер будет заполнен NULL-терминированной строкой.

    Типовые ошибки криптобиблиотеки

    Имя Код Описание
    NTE_EXISTS 0x8009000F Ключ уже существует.
    Ключевой контейнер с данным именем уже создан.
    Ошибку может возникнуть в методе createKey.
    SCARD_W_WRONG_CHV 0x8010006b Введен неверный ПИН-код.
    Ошибку могут возвращать все методы криптобиблиотеки.
    NTE_BAD_KEYSET 0x80090016 Ключ не существует.
    Ошибку могут возвращать все методы криптобиблиотеки.
    SCARD_E_NO_SUCH_CERTIFICATE 0x8010002c В контейнере отсутствует сертификат.
    Ошибку может возникнуть в методе getCertificate.
    NTE_BAD_DATA 0x80090005 Плохие данные.
    Ошибку может возникнуть в методе installCertificate в случае если устанавливается сертификат, не соответствующий ключу в котейнере.
    NTE_PROVIDER_DLL_FAIL 0x8009001D Ошибка инициализации криптопровайдера.
    Ошибку могут возвращать все методы криптобиблиотеки.
    ERROR_INVALID_PARAMETER 0x00000057 Параметр задан неверно.
    В начало © ООО "КРИПТО-ПРО", 2000–2025