(2020-08-17 11:34:51 отредактировано Сергей Оглы)

Re: OpenVPN + rutoken + неэкспортируемая пара ГОСТ = возможно? (решено!)

Ура!!! пошла:

Mon Aug 17 16:34:34 2020 WARNING: Ignoring option 'dh' in tls-client mode, please only include this in your server configuration
Mon Aug 17 16:34:34 2020 OpenVPN 2.4.9 x86_64-w64-mingw32 [SSL (OpenSSL)] [LZO] [LZ4] [PKCS11] [AEAD] built on Aug 17 2020
Mon Aug 17 16:34:34 2020 Windows version 6.1 (Windows 7) 64bit
Mon Aug 17 16:34:34 2020 library versions: OpenSSL 1.1.0l  10 Sep 2019, LZO 2.10
Mon Aug 17 16:34:34 2020 MANAGEMENT: TCP Socket listening on [AF_INET]127.0.0.1:25341
Mon Aug 17 16:34:34 2020 Need hold release from management interface, waiting...
Mon Aug 17 16:34:34 2020 MANAGEMENT: Client connected from [AF_INET]127.0.0.1:25341
Mon Aug 17 16:34:34 2020 MANAGEMENT: CMD 'state on'
Mon Aug 17 16:34:34 2020 MANAGEMENT: CMD 'log all on'
Mon Aug 17 16:34:34 2020 MANAGEMENT: CMD 'echo all on'
Mon Aug 17 16:34:34 2020 MANAGEMENT: CMD 'bytecount 5'
Mon Aug 17 16:34:34 2020 MANAGEMENT: CMD 'hold off'
Mon Aug 17 16:34:34 2020 MANAGEMENT: CMD 'hold release'
Mon Aug 17 16:34:34 2020 PKCS#11: Adding PKCS#11 provider 'rtPKCS11ECP.dll'
Mon Aug 17 16:34:34 2020 Initializing OpenSSL support for engine 'gost'
Mon Aug 17 16:34:35 2020 Outgoing Control Channel Authentication: Using 512 bit message hash 'md_gost12_512' for HMAC authentication
Mon Aug 17 16:34:35 2020 Incoming Control Channel Authentication: Using 512 bit message hash 'md_gost12_512' for HMAC authentication
Mon Aug 17 16:34:35 2020 TCP/UDP: Preserving recently used remote address: [AF_INET]10.x.x.190:1194
Mon Aug 17 16:34:35 2020 Socket Buffers: R=[8192->8192] S=[8192->8192]
Mon Aug 17 16:34:35 2020 Attempting to establish TCP connection with [AF_INET]10.x.x.190:1194 [nonblock]
Mon Aug 17 16:34:35 2020 MANAGEMENT: >STATE:1597649675,TCP_CONNECT,,,,,,
Mon Aug 17 16:34:36 2020 TCP connection established with [AF_INET]10.x.x.190:1194
Mon Aug 17 16:34:36 2020 TCP_CLIENT link local: (not bound)
Mon Aug 17 16:34:36 2020 TCP_CLIENT link remote: [AF_INET]10.x.x.190:1194
Mon Aug 17 16:34:36 2020 MANAGEMENT: >STATE:1597649676,WAIT,,,,,,
Mon Aug 17 16:34:36 2020 MANAGEMENT: >STATE:1597649676,AUTH,,,,,,
Mon Aug 17 16:34:36 2020 TLS: Initial packet from [AF_INET]10.x.x.190:1194, sid=00b8a630 aa030866
Mon Aug 17 16:34:36 2020 VERIFY OK: depth=1, C=RU, ST=28 ....
Mon Aug 17 16:34:36 2020 VERIFY KU OK
Mon Aug 17 16:34:36 2020 Validating certificate extended key usage
Mon Aug 17 16:34:36 2020 ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
Mon Aug 17 16:34:36 2020 VERIFY EKU OK
Mon Aug 17 16:34:36 2020 VERIFY OK: depth=0, C=RU, ST=28 ...
Mon Aug 17 16:34:39 2020 MANAGEMENT: CMD 'password [...]'
Mon Aug 17 16:34:40 2020 Control Channel: TLSv1.2, cipher TLSv1.0 GOST2012-GOST8912-GOST8912
Mon Aug 17 16:34:40 2020 [З...] Peer Connection Initiated with [AF_INET]10.x.x.190:1194
Mon Aug 17 16:34:41 2020 MANAGEMENT: >STATE:1597649681,GET_CONFIG,,,,,,
Mon Aug 17 16:34:41 2020 SENT CONTROL [З...]: 'PUSH_REQUEST' (status=1)
Mon Aug 17 16:34:41 2020 PUSH: Received control message: 'PUSH_REPLY,route 10.х.х.0 255.255.248.0,route-gateway 10.x.x.1,ping 10,ping-restart 120,ifconfig 10.x.x.2 255.255.255.0,peer-id 0'
Mon Aug 17 16:34:41 2020 OPTIONS IMPORT: timers and/or timeouts modified
Mon Aug 17 16:34:41 2020 OPTIONS IMPORT: --ifconfig/up options modified
Mon Aug 17 16:34:41 2020 OPTIONS IMPORT: route options modified
Mon Aug 17 16:34:41 2020 OPTIONS IMPORT: route-related options modified
Mon Aug 17 16:34:41 2020 OPTIONS IMPORT: peer-id set
Mon Aug 17 16:34:41 2020 OPTIONS IMPORT: adjusting link_mtu to 1658
Mon Aug 17 16:34:41 2020 Outgoing Data Channel: Cipher 'grasshopper-cbc' initialized with 256 bit key
Mon Aug 17 16:34:41 2020 Outgoing Data Channel: Using 512 bit message hash 'md_gost12_512' for HMAC authentication
Mon Aug 17 16:34:41 2020 Incoming Data Channel: Cipher 'grasshopper-cbc' initialized with 256 bit key
Mon Aug 17 16:34:41 2020 Incoming Data Channel: Using 512 bit message hash 'md_gost12_512' for HMAC authentication
Mon Aug 17 16:34:41 2020 interactive service msg_channel=0
Mon Aug 17 16:34:41 2020 ROUTE_GATEWAY 10.x.x.129/255.255.255.192 I=11 HWADDR=00:1d:0f:be:fa:bb
Mon Aug 17 16:34:41 2020 open_tun
Mon Aug 17 16:34:41 2020 TAP-WIN32 device [Подключение по локальной сети 2] opened: \\.\Global\{B2D272BC-FB6A-4E7F-AEF5-3C0369CD9AB3}.tap
Mon Aug 17 16:34:41 2020 TAP-Windows Driver Version 9.21 
Mon Aug 17 16:34:41 2020 Notified TAP-Windows driver to set a DHCP IP/netmask of 10.x.x.2/255.255.255.0 on interface {B2D272BC-FB6A-4E7F-AEF5-3C0369CD9AB3} [DHCP-serv: 10.x.x.0, lease-time: 31536000]
Mon Aug 17 16:34:41 2020 Successful ARP Flush on interface [17] {B2D272BC-FB6A-4E7F-AEF5-3C0369CD9AB3}
Mon Aug 17 16:34:41 2020 MANAGEMENT: >STATE:1597649681,ASSIGN_IP,,10.x.x.2,,,,
Mon Aug 17 16:34:46 2020 TEST ROUTES: 1/1 succeeded len=1 ret=1 a=0 u/d=up
Mon Aug 17 16:34:46 2020 MANAGEMENT: >STATE:1597649686,ADD_ROUTES,,,,,,
Mon Aug 17 16:34:46 2020 C:\Windows\system32\route.exe ADD 10.x.x.0 MASK 255.255.248.0 10.x.x.1
Mon Aug 17 16:34:46 2020 ROUTE: CreateIpForwardEntry succeeded with dwForwardMetric1=20 and dwForwardType=4
Mon Aug 17 16:34:46 2020 Route addition via IPAPI succeeded [adaptive]
Mon Aug 17 16:34:46 2020 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
Mon Aug 17 16:34:46 2020 Initialization Sequence Completed
Mon Aug 17 16:34:46 2020 MANAGEMENT: >STATE:1597649686,CONNECTED,SUCCESS,10.x.x.2,10.x.x.190,1194,127.0.0.1,49281

Re: OpenVPN + rutoken + неэкспортируемая пара ГОСТ = возможно? (решено!)

Только в вашем решении, как и у меня, на рутокене храниться RSA ключик, а на сервере ГОСТовый ключ в открытом виде. Но изначальная задача: защитить ключ клиента на рутокене, идентифицировать его и получить ГОСТ шифрование.
В итоге стенд:
Сервер: CentOS 7, OpenVPN + OpenSSL 1.1.1 + PKI ГОСТ2012_512 + Engine GOST + ГОСТ сертификат.
Клиент: Win7, OpenVPN + OpenSSL 1.1.0 + Engine GOST (под OpenSSL 1.1.0) + RSA сертификат + rtPKCS11ECP.dll.
PS: OpenSSL 1.1.1 - некорректно работает с pkcs11-helper  и выдает ошибку

OpenSSL: error:141F0006:SSL routines:tls_construct_cert_verify:EVP lib

Re: OpenVPN + rutoken + неэкспортируемая пара ГОСТ = возможно? (решено!)

C:\Program Files\openvpn\bin>openssl.exe engine rtengine -c -t
(rtengine) Rutoken engine
 [gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, gost2001, gost-mac, gost2012_256, gost2012_512, gost-mac-12]
     [ available ]

C:\Program Files\openvpn\bin>openssl.exe engine gost -c -t
(gost) Reference implementation of GOST engine
 [gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, grasshopper-ecb, grasshopper-cbc, grasshopper-cfb, grasshopper-ofb, grasshopper-ctr, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, gost2001, gost-mac, gost2012_256, gost2012_512, gost-mac-12]
     [ available ]

И еще вопрос, когда в rtengine появиться кузнечик?

(2020-08-27 13:46:13 отредактировано Сергей Оглы)

Re: OpenVPN + rutoken + неэкспортируемая пара ГОСТ = возможно? (решено!)

Доброго.
Вышел каменный цветок, OpenVPN + GOST2012 + рутокет эцп 2.0!!!
Дело в нескольких десятках строк в pkcs11-helper, помог реверс-инжиниринг и понимание куда копать.
Для использования openssl v1.1.1 пришлось обновить pkcs-helper до v1.26.0 и немного модифицировать pkcs11h-openssl.c:
в начало

#include <openssl/engine.h>

добавляем функцию __pkcs11h_openssl_gost_do_sign

#define NSSCK_VENDOR_PKCS11_RU_TEAM 0xD4321000 /* 0x80000000 | 0x54321000 */
#define CK_VENDOR_PKCS11_RU_TEAM_TC26 NSSCK_VENDOR_PKCS11_RU_TEAM 
#define CKM_GOSTR3410 0x00001201
#define CKM_GOSTR3410_256 CKM_GOSTR3410
#define CKM_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TC26 |0x006)

static int* __pkcs11h_openssl_gost_do_sign(EVP_PKEY_CTX *ctx, unsigned __int8 *sig, size_t *sig_len, const unsigned __int8 *key, size_t key_len)
{
    ENGINE *engine_gost = NULL;
    pkcs11h_certificate_t *certificate;
    CK_RV rv = CKR_FUNCTION_FAILED;
    const char *rv_error;
    engine_gost = ENGINE_by_id("gost");
    if ( !engine_gost ){
        _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: Cannot get engine by id gost");
        goto label_exit_gost_sig;
    }
    certificate = (pkcs11h_certificate_t *)ENGINE_get_ex_data(engine_gost,0);
    if ( pkcs11h_certificate_lockSession(certificate) ) {
        _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: pkcs11h_certificate_lockSession - error");
        ENGINE_free(engine_gost);
        goto label_exit_gost_sig;
    }
    rv = pkcs11h_certificate_signAny(certificate, ENGINE_get_ex_data(engine_gost,1), key, key_len, sig, sig_len);
    if ( rv != CKR_OK ){
        rv_error = pkcs11h_getMessage(rv);
        _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: GOST Cannot perform signature %ld:'%s'", rv, rv_error);
        pkcs11h_certificate_releaseSession(certificate);
        ENGINE_free(engine_gost);
label_exit_gost_sig:
        _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: __pkcs11h_openssl_gost_do_sign - return sig=%p", sig);
        return -1;
    }
    ENGINE_free(engine_gost);
    pkcs11h_certificate_releaseSession(certificate);
    return sig;
}

и модифицируем функцию pkcs11h_openssl_session_getEVP

EVP_PKEY *
pkcs11h_openssl_session_getEVP (
    IN const pkcs11h_openssl_session_t openssl_session
) {
    X509 *x509 = NULL;
    EVP_PKEY *evp = NULL;
    EVP_PKEY *ret = NULL;

    _PKCS11H_ASSERT (openssl_session!=NULL);

    _PKCS11H_DEBUG (
        PKCS11H_LOG_DEBUG2,
        "PKCS#11: pkcs11h_openssl_session_getEVP - entry openssl_session=%p",
        (void *)openssl_session
    );

    /*
     * Dup x509 so RSA will not hold session x509
     */
    if ((x509 = pkcs11h_openssl_session_getX509 (openssl_session)) == NULL) {
        _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: Cannot get certificate object");
        goto cleanup;
    }

    if ((evp = X509_get_pubkey (x509)) == NULL) {
        _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: Cannot get public key");
        goto cleanup;
    }

    if (0) {
    }
#ifndef OPENSSL_NO_RSA
    else if (EVP_PKEY_id (evp) == EVP_PKEY_RSA) {
        if (!__pkcs11h_openssl_session_setRSA(openssl_session, evp)) {
            goto cleanup;
        }
    }
#endif
#ifndef OPENSSL_NO_RSA
    else if (EVP_PKEY_id (evp) == EVP_PKEY_DSA) {
        if (!__pkcs11h_openssl_session_setDSA(openssl_session, evp)) {
            goto cleanup;
        }
    }
#endif
#ifdef __ENABLE_EC
    else if (EVP_PKEY_id(evp) == EVP_PKEY_EC) {
        if (!__pkcs11h_openssl_session_setECDSA(openssl_session, evp)) {
            goto cleanup;
        }
    }
#endif

добавляем:

    else if (EVP_PKEY_id(evp) == NID_id_GostR3410_2012_512|| EVP_PKEY_id(evp) == NID_id_GostR3410_2012_256 || EVP_PKEY_id(evp) == NID_id_GostR3410_2001 ) {
        ENGINE *engine_gost = NULL;
        engine_gost = ENGINE_by_id("gost");
        if ( !engine_gost ){
            _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: Cannot get engine by id gost");
            goto cleanup;
        }
        if ( ENGINE_get_pkey_meth(engine_gost, EVP_PKEY_id(evp)) == NULL){
            _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: Cannot get public key mech");
        }else{
            _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: public key mech");
            EVP_PKEY_meth_set_sign(ENGINE_get_pkey_meth(engine_gost, EVP_PKEY_id(evp)),0,__pkcs11h_openssl_gost_do_sign);
            ENGINE_set_ex_data(engine_gost,0,openssl_session->certificate);
            if (EVP_PKEY_id(evp) == NID_id_GostR3410_2012_512){
            ENGINE_set_ex_data(engine_gost,1,CKM_GOSTR3410_512);
            }else{
            ENGINE_set_ex_data(engine_gost,1,CKM_GOSTR3410_256);
            }
            ENGINE_free(engine_gost);
        }
    }

и остаток:

    else {
        _PKCS11H_LOG (PKCS11H_LOG_WARN, "PKCS#11: Invalid public key algorithm %d", EVP_PKEY_id (evp));
        goto cleanup;
    }

#if defined(ENABLE_PKCS11H_THREADING)
    _pkcs11h_threading_mutexLock(&openssl_session->reference_count_lock);
#endif
    openssl_session->reference_count++;
#if defined(ENABLE_PKCS11H_THREADING)
    _pkcs11h_threading_mutexRelease(&openssl_session->reference_count_lock);
#endif

    ret = evp;
    evp = NULL;

cleanup:

    /*
     * openssl objects have reference
     * count, so release them
     */
    if (evp != NULL) {
        EVP_PKEY_free (evp);
        evp = NULL;
    }

    if (x509 != NULL) {
        X509_free (x509);
        x509 = NULL;
    }

    _PKCS11H_DEBUG (
        PKCS11H_LOG_DEBUG2,
        "PKCS#11: pkcs11h_openssl_session_getEVP - return ret=%p",
        (void *)ret
    );

    return ret;
}

собираем и радуемся!!!
В отличии от RutokenVPN (который работает с сертификатами RSA и ГОСТ2001 - который устарел) получаем поддержку сертификатов ГОСТ2012 (256 и 512) .
PS: набор исходников состоит из OpenVPN 2.4.9 + OpenSSL 1.1.1g + Pkcs11-helper 1.26.0 и отдельно собирал ENGINE gost v1.1.0.3
Отдельное спасибо разработчикам за реверс и не предоставленные исходники(ради нескольких строк)!
"И удачи в продажах enterprise за 50k".

(2020-08-29 02:49:50 отредактировано Сергей Оглы)

Re: OpenVPN + rutoken + неэкспортируемая пара ГОСТ = возможно? (решено!)

В итоге собрал библиотеки gost.dll и libpkcs11-helper-1.dll для x86_x32/64, выложил на github + небольшая инструкция по установке и скрипт для самостоятельной сборки gost.dll под Linux'ом (использовал CentOS 8).
Результат: Безопасное хранение ключевой пары у клиента в формате ГОСТ на рутокене и идентификация по ГОСТ сертификату на сервере.