We construct a local cryptographic TLS proxy with an HTTP electronic signature API



    Electronic bidding, government services, corporate electronic document management systems, remote banking services and other similar systems are in the sphere of interests of regulators and are forced to use Russian cryptocurrencies to ensure confidentiality and legal significance.

    The main means of interaction between the user and the information system is slowly but surely becoming a browser.
    If you carefully consider the integration of popular browsers and Russian cryptocurrencies, the following problems emerge:
    • Browsers use completely different cryptographic libraries (MS Crypto API, NSS, openssl). There is no universal crypto tool that “adds” GOSTs to all these libraries
    • The mechanisms for embedding many crypto tools in the operating system and in the browser are “tied” to the OS version. With the release of the OS update, the performance of the crypto facility may end
    • Google Chrome refuses to support plugins that work through NPAPI. And many Russian cryptocurrency vendors developed plugins using this particular mechanism
    • On mobile platforms, browsers do not support plugins


    In this situation, the most universal solution seems to be to implement the TLS-GOST and EDS functions in a separate network application that accepts requests from the browser on localhost, allows you to “tunnel” connections between the browser and remote web servers (real proxy), and also provides an HTTP API for digital signature functionality, work with certificates, tokens, etc.
    I will not say that the idea is new, but let's try to make some constructor for its implementation.

    The constructor details will be:


    Fans of modular architectural experimental solutions are invited to cat.



    The main idea of ​​the solution is that part of the HTTP requests to the web information system is processed by the remote web server, and part by the local one.
    In order to make this possible, you should run a web server on localhost, which is configured in such a way that requests containing the URL "local", it sends to the local web application, and requests containing the URL "remote", reverses according to your configuration file. The local web application listens at the local / login and local / api addresses.

    Requests to the local / api address are a set of specified POST requests that provide an HTTP API for:
    • work with connected Rootoken EDS
    • work with digital certificates X.509 of the public key GOST R 34.10-2001 stored on the Rootoken EDS
    • work with key pairs GOST R 34.10-2001 stored at Rutoken EDS
    • generating signed and encrypted CMS messages
    • formation of a "raw" signature in accordance with GOST R 34.10-2001
    • generating certificate requests in PKCS # 10 format


    The request for the local / login address is processed by the local web application, which provides a web interface for authorization of the Flash digital signature on Rutoken, since a token is required to establish a TLS connection with the remote server. After authorization on the device, the local web application “lifts” the TLS tunnel to the remote server. From a remote server, the browser receives web pages that can use the described cryptographic HTTP API, sending special requests to the local / api address.

    The schematic diagram of the solution is shown in the picture:


    Let us consider the components of the solution in more detail.

    NGINX Web Server

    This web-server runs quite successfully on localhost without installation, from the FLASH-memory of Rootoken EDS Flash.

    The configuration file looks like this:
    worker_processes  1;
    events 
    {
    worker_connections  1024;
    }
    http 
    {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    client_max_body_size 0;
    access_log nul;
    error_log logs/error.log warn;
    log_not_found off;
    keepalive_timeout 5;
    affect the upstream waiting time.
    gzip on;
    include headers.conf;
    include firewall.conf;
    include upstream.conf;
    server 
    {
    listen 8000;
    root www;
    index index.php;
    location / 
    {
    expires -1;
    try_files $uri $uri/ /index.php;
    }
    location /local/login 
    {
    root www;
    try_files $uri /login.php;
    }
    error_page   500  502  503  504  /50x.html;
    location  =  /50x.html 
    {
    root         html;
    }
    location ~ \.php$ 
    {
    root                 www;
    try_files           $uri =404;
    include            fastcgi_pass.conf;
    fastcgi_index   index.php;
    include            fastcgi.conf;
    #limit_req        zone=req_limit_per_ip  burst=10  nodelay;  #A bug found on Windows 8.
    }
    location /remote/ 
    {
    proxy_pass http://localhost:1443;
    }
    location ~* /rewrite+\. 
    {
    deny  all;
    }
    }
    include  vhosts/*.conf;
    }
    


    Section
    location /remote/ 
    {
    proxy_pass http://localhost:1443;
    } 
    


    Enables request forwarding mode on localhost: 1443.
    And on localhost: 1443, in turn, they are received by the local TLS proxy, which, using the Rutoken EDS Flash controller, establishes the TLS-GOST connection in accordance with its configuration file, authenticates the user with a digital certificate under the TLS protocol and forwards requests to the remote server and vice versa - through NGINX to the user's browser.

    sTunnel


    When entering the local / login URL in case of successful authorization of the user on the Rutoken EDS Flash, the local web application, using php-cgi, starts the sTunnel process, passing it the token PIN as one of the command line parameters.
    In addition, the local WEB application can configure sTunnel to use a specific certificate for client authentication within TLS.

    STunnel, listening on localhost, waits for a reversed request by the NGINX from the browser. Upon receipt of the first request, it establishes a TLS connection with the remote server in accordance with its configuration file and then transmits requests and responses over the established connection.



    Example configuration file:

    ; проверка сертификата сервера обязательна
    verify=1
    ; режим работы: клиент TLS-прокси
    client=yes
    ; корневой сертификат
    CAFile=ca2001_A-root.crt
    ; версия протокола TLS
    sslVersion=TLSv1
    ; не создаем иконку в трее
    taskbar=no
    ; уровень ведения логирования
    DEBUG=7
    output = log
    ; использовать engine PKCS11_GOST для работы с Рутокен ЭЦП
    engine=pkcs11_gost
    ; использовать библиотеку PKCS#11 из директории запуска процесс
    engineCtrl=MODULE_PATH:rtpkcs11ecp.dll
    ;за ГОСТ-ы отвечает engine
    engineDefault=ALL
    [remote system]
    ; используем для загрузки ключа engine PKCS11_GOST
    engineNum = 1
    ; загружаем клиентский сертификат из файла
    cert=client.crt
    ; ID ключевой пары на Рутокен ЭЦП                                                                  
    key = a0:59:d9:62:0d:09:69:2f:b6:ba:2d:9b:da:5b:2b:4d:fe:75:05:19
    ; прокси принимает подключения на данном порту
    accept = localhost:1443
    ; удаленный сервер
    connect = x.x.x.x:443
    sni = localhost
    ; используем российский шифрсьют для TLS
    ciphers = GOST2001-GOST89-GOST89
    ; for IE
    TIMEOUTclose = 0
    


    This configuration gives you this:
    • The connection is established using the encryption code GOST2001-GOST89-GOST89
    • The production of the approval key according to the VKO GOST scheme 34.10-2001 occurs on the controller Rutoken EDS using a non-removable authentication key


    HTTP API module


    The HTTP API module is implemented by a local web application written in PHP using PHP-CGI. It should be noted that PHP also successfully runs on localhost from the FLASH-memory of Rootoken EDS Flash.

    Briefly about the implementation. At / local / api, the web application listens for incoming POST requests in json format.
    Each request must have func and prm fields. Func is the name of the required method, prm is an array with parameters for the method. Something like {func: 'someFunc', prm: ['param1', 'param2', ..., 'paramN']}

    In response, the web application returns a standard object containing the fields of the return / error code and, possibly, the result - {res: resultObject, retcode: int}

    To implement the methods, we need to get access to the functions of the Rootoken EDS from PHP. For this purpose we will use the C ++ library, which contains both the functionality for working with tokens and support for digital certificates, certificate requests and CMS messages.
    Using the swig project, we managed to make a PHP module (“PHP-PKI-Core”) from this library, whose functions are called using the PHP-CGI mechanism.

    The following is a script that implements the cryptographic HTTP API:

    prm = $request["prm"];
        // название функции
        $result->func = $request["func"];
        // враппер для функций криптобиблиотеки, automatically generated by SWIG (http://www.swig.org)
        include_once("pkiWrapper.php");
        $pkicore = new _CryptoCore();
        try {
            // всегда делаем енумерэйт, ибо хэндлер не сохраняется между запросами
            $pkicore->enumerateDevices();
            // если это логин - сохраняем PIN-код и  ID токена в сессии
            if ($result->func == 'login') {
                call_user_func_array(array($pkicore, $result->func), $result->prm);
                $_SESSION['pin'] = $result->prm[1];
                $_SESSION['tokenid'] = $result->prm[0];
                $result->retcode = 1;
            } else if (isset($_SESSION["pin"]) && isset($_SESSION['tokenid'])) {
                // логин на токен
                $pkicore->login($_SESSION['tokenid'], $_SESSION['pin']);
                // вызов функции с параметрами
                $result->res = call_user_func_array(array($pkicore, $result->func), $result->prm);
                $result->retcode = 1;
            } else {
                // не залогинены на токен, выдаем
                $result->retcode = 1000;
                $result->error = "Токен не залогинен";
            }
        } catch (Exception $ex) {
            $result->retcode = 500;
            $result->error = $ex->getMessage();
            unset($_SESSION['pin']);
            unset($_SESSION['tokenid']);
        }
    } else {
        $result->retcode = 500;
        $result->error = 'No function / bad name';
        $result->text = $request["func"];
    }
    // отдаем результат. 
    echo json_encode($result);
    ?>
    

    The main nuance here is that when processing each HTTP request, it is required to re-produce a login for the token. Which, in turn, requires storing the token ID and its PIN code in the session. From the point of view of security requirements, the session must be stored in RAM, and not in a temporary file.

    This is what the request to the sign HTTP API function looks like :
    POST /local/api HTTP/1.1
    Host: localhost:8080
    Connection: keep-alive
    Content-Length: 884
    Accept: application/json, text/plain, */*
    Origin: http://localhost:1443
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36
    Content-Type: application/json;charset=UTF-8
    Referer: http://localhost:1443/
    Accept-Encoding: gzip, deflate
    Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
    Cookie: PHPSESSID=uq7jdu714nb9f4jec3plc6lpp2; .ASPXAUTH=F33760B6909CE6628C96863CF87B3B1477B161EAB8F603594B4FCB22F182AE083869BEFDD62EF5DB54FA9E3005D3BB2FB7F1400D8BE40C74B522ACD64C427A7D6976AA65E98EB413F2F706F619186D2CF8D6D976961EF09D5742DC6812F15AA5F5ABB68516B0214DED7774C1664FAB7C
    {"func":"sign","prm":{"0":0,"1":"dc:48:b8:f2:cf:a8:36:0d:bd:37:33:61:f7:32:ff:1b:a7:65:db:ce","2":"Платежное поручение2245Сумма71269Дата11.12.2014ПолучательФГУП СервисКонтактИнн7707083893КПП 775003035Назначение платежаЗа телематические услугиБанк получателя>ЮгБанкБИК044525225Номер счета получателя30101810400000000225ПлательщикЗАО \"Актив-софт\"Банк плательщикаБанк ВТБ (открытое акционерное общество)БИК044525187Номер счета плательщика30101810700000000187","3":false,"4":{"detached":true,"addUserCertificate":true,"useHardwareHash":true}}}
    


    Answer:
    HTTP/1.1 200 OK
    Server: nginx/1.4.4
    Date: Thu, 26 Feb 2015 10:19:53 GMT
    Content-Type: application/json; charset=UTF-8
    Transfer-Encoding: chunked
    Connection: keep-alive
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Access-Control-Allow-Origin: http://localhost:1443
    Access-Control-Allow-Credentials: true
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
    Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
    {"prm":[0,"dc:48:b8:f2:cf:a8:36:0d:bd:37:33:61:f7:32:ff:1b:a7:65:db:ce","\u041f\u043b\u0430\u0442\u0435\u0436\u043d\u043e\u0435 \u043f\u043e\u0440\u0443\u0447\u0435\u043d\u0438\u04352245\u0421\u0443\u043c\u043c\u043071269\u0414\u0430\u0442\u043011.12.2014\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c\u0424\u0413\u0423\u041f \u0421\u0435\u0440\u0432\u0438\u0441\u041a\u043e\u043d\u0442\u0430\u043a\u0442\u0418\u043d\u043d7707083893\u041a\u041f\u041f 775003035\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043b\u0430\u0442\u0435\u0436\u0430\u0417\u0430 \u0442\u0435\u043b\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0443\u0441\u043b\u0443\u0433\u0438\u0411\u0430\u043d\u043a \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f\u0421\u0431\u0435\u0440\u0431\u0430\u043d\u043a\u0411\u0418\u041a044525225\u041d\u043e\u043c\u0435\u0440 \u0441\u0447\u0435\u0442\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f30101810400000000225\u041f\u043b\u0430\u0442\u0435\u043b\u044c\u0449\u0438\u043a\u0417\u0410\u041e \"\u0410\u043a\u0442\u0438\u0432-\u0441\u043e\u0444\u0442\"\u0411\u0430\u043d\u043a \u043f\u043b\u0430\u0442\u0435\u043b\u044c\u0449\u0438\u043a\u0430\u0411\u0430\u043d\u043a \u0412\u0422\u0411 (\u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0435 \u0430\u043a\u0446\u0438\u043e\u043d\u0435\u0440\u043d\u043e\u0435 \u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e)\u0411\u0418\u041a044525187\u041d\u043e\u043c\u0435\u0440 \u0441\u0447\u0435\u0442\u0430 \u043f\u043b\u0430\u0442\u0435\u043b\u044c\u0449\u0438\u043a\u043030101810700000000187",false,{"detached":true,"addUserCertificate":true,"useHardwareHash":true}],"func":"sign","res":"MIIDmQYJKoZIhvcNAQcCoIIDijCCA4YCAQExDDAKBgYqhQMCAgkFADALBgkqhkiG 9w0BBwGgggGnMIIBozCCAVCgAwIBAgIBATAKBgYqhQMCAgMFADBUMQswCQYDVQQG EwJSVTEPMA0GA1UEBxMGTW9zY293MSIwIAYDVQQKFBlPT08gIkdhcmFudC1QYXJr LVRlbGVjb20iMRAwDgYDVQQDEwdUZXN0IENBMB4XDTE1MDIyNjEzMzU1MloXDTE2 MDIyNjEzMzU1MlowGTEXMBUGA1UEAx4OBDIEQwRGBDIEQwRGBDIwYzAcBgYqhQMC AhMwEgYHKoUDAgIjAQYHKoUDAgIeAQNDAARAwXoeizbUWzwA1mkNfWpSQ8eslAIZ dpvMv7qM0n9KTehYyEj1+vkAmMZ9UOT3cE1C6E4fUjWJgXHJL6Ttu5rw86NEMEIw JQYDVR0lBB4wHAYIKwYBBQUHAwIGCCsGAQUFBwMEBgYpAQEBAQIwCwYDVR0PBAQD AgKkMAwGA1UdEwEB/wQCMAAwCgYGKoUDAgIDBQADQQBH8ihBKIWIILO3JJu4j4Bk NN5lQ4n5Y0HUozpHwMfCvR98rLHMmjGFwjdQHHXSrW6eCHryVD8oeV47+hO/U5HM MYIBuTCCAbUCAQEwWTBUMQswCQYDVQQGEwJSVTEPMA0GA1UEBxMGTW9zY293MSIw IAYDVQQKFBlPT08gIkdhcmFudC1QYXJrLVRlbGVjb20iMRAwDgYDVQQDEwdUZXN0 IENBAgEBMAoGBiqFAwICCQUAoIH6MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw HAYJKoZIhvcNAQkFMQ8XDTE1MDIyNjEzMzYxNVowLwYJKoZIhvcNAQkEMSIEIIF3 veehPcgZGmj4rvJQtNwSYf7VzDZNraAiDR89eoysMIGOBgkqhkiG9w0BCQ8xgYAw fjALBglghkgBZQMEASowCAYGKoUDAgIJMAgGBiqFAwICFTALBglghkgBZQMEARYw CwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMCAgIAgDANBggqhkiG 9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDAKBgYqhQMCAhMFAARAZGY6 ZjA6MzU6ZDY6N2M6MjI6ZjA6MDc6ZDc6OGU6NDY6Nzk6YzU6YzU6NmU6MmQ6YzA6 ODg6ZWU6MDU6NjU6NA==y}
    


    In the res field is a signature in CMS detached format from the transmitted data.

    It should be noted that using the PHP-PKI-Core module, a developer can make a cryptographic HTTP API in a way that suits him.

    Conclusion


    The design, for all its apparent cumbersomeness, represents 4 running applications - nginx, sTunnel, php, php-cgi.
    These applications, as I already noted, do not require installation, are stored on the FLASH-memory and run from it.
    It is not difficult to write a control application that gently starts them and “extinguishes” the processes at the end of the work or in case of detaching the token.

    Also popular now: