Re: Поддержка Рутокен ЭЦП в OpenSSL

Для решения проблемы необходимо поставить свежую библиотеку PKCS#11 https://www.rutoken.ru/support/download/pkcs/

(2014-08-07 15:45:18 отредактировано k0dg1k)

Re: Поддержка Рутокен ЭЦП в OpenSSL

Добрый день.
Есть веб-приложение. Клиент - js (rutoken plugin), сервер - php. Произвожу шифрование данных (строки) на сервере. С помощью голого openssl ( exec('openssl cms -engine gost -gost89 -encrypt -in data.txt -outform PEM -out data_enc.txt cert.crt') ) все работает отлично. Но хотелось бы сделать с помощью обертки php openssl, но та не видит гостовских алгоритмов. Может кто подскажет, как с этим бороться?
php: 5.5.x
rutoken ecp

Re: Поддержка Рутокен ЭЦП в OpenSSL

k0dg1k пишет:

Добрый день.
Есть веб-приложение. Клиент - js (rutoken plugin), сервер - php. Произвожу шифрование данных (строки) на сервере. С помощью голого openssl ( exec('openssl cms -engine gost -gost89 -encrypt -in data.txt -outform PEM -out data_enc.txt cert.crt') ) все работает отлично. Но хотелось бы сделать с помощью обертки php openssl, но та не видит гостовских алгоритмов. Может кто подскажет, как с этим бороться?
php: 5.5.x
rutoken ecp

Здравствуйте!

На Хабре была статья, где описан способ решения подобной проблемы. Может быть поможет.

Re: Поддержка Рутокен ЭЦП в OpenSSL

Будет много кода.

Смотрим функцию из враппера PHP над OpenSSL (https://github.com/php/php-src/blob/mas … /openssl.c), которая является аналогом нужной вам команды:

/* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]])
   Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */
PHP_FUNCTION(openssl_pkcs7_encrypt)
{
    zval ** zrecipcerts, * zheaders = NULL;
    STACK_OF(X509) * recipcerts = NULL;
    BIO * infile = NULL, * outfile = NULL;
    long flags = 0;
    PKCS7 * p7 = NULL;
    HashPosition hpos;
    zval ** zcertval;
    X509 * cert;
    const EVP_CIPHER *cipher = NULL;
    long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
    uint strindexlen;
    ulong intindex;
    char * strindex;
    char * infilename = NULL;    int infilename_len;
    char * outfilename = NULL;    int outfilename_len;
    
    RETVAL_FALSE;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZa!|ll", &infilename, &infilename_len,
                &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE)
        return;

    
    if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) {
        return;
    }

    infile = BIO_new_file(infilename, "r");
    if (infile == NULL) {
        goto clean_exit;
    }

    outfile = BIO_new_file(outfilename, "w");
    if (outfile == NULL) { 
        goto clean_exit;
    }

    recipcerts = sk_X509_new_null();

    /* get certs */
    if (Z_TYPE_PP(zrecipcerts) == IS_ARRAY) {
        zend_hash_internal_pointer_reset_ex(HASH_OF(*zrecipcerts), &hpos);
        while(zend_hash_get_current_data_ex(HASH_OF(*zrecipcerts), (void**)&zcertval, &hpos) == SUCCESS) {
            long certresource;

            cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC);
            if (cert == NULL) {
                goto clean_exit;
            }

            if (certresource != -1) {
                /* we shouldn't free this particular cert, as it is a resource.
                    make a copy and push that on the stack instead */
                cert = X509_dup(cert);
                if (cert == NULL) {
                    goto clean_exit;
                }
            }
            sk_X509_push(recipcerts, cert);

            zend_hash_move_forward_ex(HASH_OF(*zrecipcerts), &hpos);
        }
    } else {
        /* a single certificate */
        long certresource;

        cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource TSRMLS_CC);
        if (cert == NULL) {
            goto clean_exit;
        }

        if (certresource != -1) {
            /* we shouldn't free this particular cert, as it is a resource.
                make a copy and push that on the stack instead */
            cert = X509_dup(cert);
            if (cert == NULL) {
                goto clean_exit;
            }
        }
        sk_X509_push(recipcerts, cert);
    }

    /* sanity check the cipher */
    cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
    if (cipher == NULL) {
        /* shouldn't happen */
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher");
        goto clean_exit;
    }

    p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, flags);

    if (p7 == NULL) {
        goto clean_exit;
    }

    /* tack on extra headers */
    if (zheaders) {
        zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos);
        while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&zcertval, &hpos) == SUCCESS) {
            strindex = NULL;
            zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);

            convert_to_string_ex(zcertval);

            if (strindex) {
                BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(zcertval));
            } else {
                BIO_printf(outfile, "%s\n", Z_STRVAL_PP(zcertval));
            }

            zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
        }
    }

    (void)BIO_reset(infile);

    /* write the encrypted data */
    SMIME_write_PKCS7(outfile, p7, infile, flags);

    RETVAL_TRUE;

clean_exit:
    PKCS7_free(p7);
    BIO_free(infile);
    BIO_free(outfile);
    if (recipcerts) {
        sk_X509_pop_free(recipcerts, X509_free);
    }
}

Там есть такой фрагмент

/* sanity check the cipher */
    cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
    if (cipher == NULL) {
        /* shouldn't happen */
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher");
        goto clean_exit;
    }

    p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, flags);

    if (p7 == NULL) {
        goto clean_exit;
    }

Давайте посмотрим на реализацию php_openssl_get_evp_cipher_from_algo:

static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo) { /* {{{ */
    switch (algo) {
#ifndef OPENSSL_NO_RC2
        case PHP_OPENSSL_CIPHER_RC2_40:
            return EVP_rc2_40_cbc();
            break;
        case PHP_OPENSSL_CIPHER_RC2_64:
            return EVP_rc2_64_cbc();
            break;
        case PHP_OPENSSL_CIPHER_RC2_128:
            return EVP_rc2_cbc();
            break;
#endif

#ifndef OPENSSL_NO_DES
        case PHP_OPENSSL_CIPHER_DES:
            return EVP_des_cbc();
            break;
        case PHP_OPENSSL_CIPHER_3DES:
            return EVP_des_ede3_cbc();
            break;
#endif

#ifndef OPENSSL_NO_AES
        case PHP_OPENSSL_CIPHER_AES_128_CBC:
            return EVP_aes_128_cbc();
            break;
        case PHP_OPENSSL_CIPHER_AES_192_CBC:
            return EVP_aes_192_cbc();
            break;
        case PHP_OPENSSL_CIPHER_AES_256_CBC:
            return EVP_aes_256_cbc();
            break;
#endif


        default:
            return NULL;
            break;
    }
}
/* }}} */

ГОСТа нет.

Вывод: если вам нужно шифрование в формате CMS с ГОСТами, то требуется дополнительно патчить PHP. Вы можете сделать это и осчастливить своим патчем рунет.

Re: Поддержка Рутокен ЭЦП в OpenSSL

Добрый день!
попытался разобраться с интеграцией rutoken c openssl по этой инструкции:
http://developer.rutoken.ru/pages/viewp … Id=2228251
Собрана версия openssl с поддержкой эллиптической криптографии
openssl-version выдает:
OpenSSL 1.0.1 14 Mar 2012

openssl genpkey -engine pkcs11_gost -algorithm GOST2001 -pkeyopt slot_key_id:50 -pkeyopt paramset:A -pkeyopt pin:12345678
Выдает следующее:
Error configuring OpenSSL
3073689800:error:25066067:DSO support routines:DLFCN_LOAD:could not load the shared library:dso_dlfcn.c:185:filename(/usr/lib/pkcs11-gost/libpkcs11_gost.so): /usr/lib/pkcs11-gost/libpkcs11_gost.so: undefined symbol: EC_ex_dup_data_fun
3073689800:error:25070067:DSO support routines:DSO_load:could not load the shared library:dso_lib.c:244:
3073689800:error:260B6084:engine routines:DYNAMIC_LOAD:dso not found:eng_dyn.c:450:
3073689800:error:260BC066:engine routines:INT_ENGINE_CONFIGURE:engine configuration error:eng_cnf.c:204:section=pkcs11_section, name=dynamic_path, value=/usr/lib/pkcs11-gost/libpkcs11_gost.so
3073689800:error:0E07606D:configuration file routines:MODULE_RUN:module initialization error:conf_mod.c:235:module=engines, value=engine_section, retcode=-1

Re: Поддержка Рутокен ЭЦП в OpenSSL

А у вас программная engine gost работает?
Очень похоже на то, что что-то таки не дособралось с эллиптическими кривыми

Re: Поддержка Рутокен ЭЦП в OpenSSL

как это проверить?
вообще возникло ощущение что не подхватываются символы из libp11.so.2, хотя она лежит в /usr/lib

Re: Поддержка Рутокен ЭЦП в OpenSSL

С этим разобрался, прописал пути к библиотеке  libp11.so.2, в LD_LIBRARY_PATH
Теперь выдает вот такое сообщение:
./openssl genpkey -engine pkcs11_gost -algorithm GOST2001 -pkeyopt slot_key_id:0 -pkeyopt paramset:A -pkeyopt pin:12345678
engine "pkcs11_gost" set.
Error writing key
3073783432:error:0D0A30A7:asn1 encoding routines:i2d_PrivateKey:unsupported public key type:i2d_pr.c:77:
3073783432:error:0906900D:PEM routines:PEM_ASN1_write_bio:ASN1 lib:pem_lib.c:357:
Причем ключ на токене по всей видимости создаётся, т к при повторной генерации получаю:
./openssl genpkey -engine pkcs11_gost -algorithm GOST2001 -pkeyopt slot_key_id:0 -pkeyopt paramset:A -pkeyopt pin:12345678
engine "pkcs11_gost" set.
key with such ID allready exists on token
Error generating key
3074176648:error:8107808D:lib(129):PKEY_GOST01CP_KEYGEN:key allready exists on token:gost_sign_pkcs11.c:152:

Re: Поддержка Рутокен ЭЦП в OpenSSL

Это нормальное поведение. Особенности тулзы openssl. Ключ на токене создался.

Re: Поддержка Рутокен ЭЦП в OpenSSL

openssl req -engine pkcs11_gost -new -key 0 -keyform engine -out req.csr
unable to load module (null)
can't use that engine
3073870024:error:80001401:Vendor defined:PKCS11_CTX_load:Unable to load PKCS#11 module:p11_load.c:69:
3073870024:error:8108D097:lib(129):PKCS11_INIT:fail load libp11:engine_pkcs11.c:180:
3073870024:error:260B806D:engine routines:ENGINE_TABLE_REGISTER:init failed:eng_table.c:174:
no engine specified
unable to load Private Key
*** glibc detected *** openssl: double free or corruption (!prev): 0x0889cef8 ***
Ошибка сегментирования (core dumped)

Re: Поддержка Рутокен ЭЦП в OpenSSL

engine не смогла загрузить библиотеку librtpkcs11ecp.so

Нужно смотреть как путь к ней у вас прописан в конфиге и есть ли она по этому пути

Re: Поддержка Рутокен ЭЦП в OpenSSL

Получилось!
но при подписании выдает
*** glibc detected *** openssl: double free or corruption (!prev): 0x0824dc80 ***
Ошибка сегментирования (core dumped)
Хотя всё подписывает, и подпись получается верная
И еще вопрос: как избавиться от запроса ПИНа при подписании? Планируется что подпись должна происходить без участия человека

Re: Поддержка Рутокен ЭЦП в OpenSSL

с ПИН-ом тоже разобрался:
https://github.com/OpenSC/OpenSC/wiki/E … quickstart
PIN can be passed only in the [pkcs11_section] of the openssl.conf (see above).

Re: Поддержка Рутокен ЭЦП в OpenSSL

А как у вас выглядит конфиг openssl?
Это на тему double free

Re: Поддержка Рутокен ЭЦП в OpenSSL

И если не секрет, то что за проект (можно в личку)