Учитывая функцию C, которая заполняет беззнаковый символ **, как заполнить std :: vector данными без промежуточной копии - PullRequest
1 голос
/ 14 апреля 2019

Функции C:

int i2d_PrivateKey(EVP_PKEY *a, unsigned char **pp);

int i2d_X509(X509 *a, unsigned char **ppout);

И я написал такой код для копирования в std :: vector:

// populate PrivateKey
std::vector<uint8_t> PrivateKey;
EVP_PKEY *privatekey = NULL;
int size = i2d_PrivateKey(privatekey, NULL);
if (size > 0)
{
    PrivateKey.reserve(size);
    uint8_t* ptr = &PrivateKey[0];
    i2d_PrivateKey(privatekey, &ptr);
std::cout << "PrivateKey size=" << PrivateKey.size() << '\n';
}

PrivateKey.size () возвращает ноль, поэтому я знаю, что вектор не был заполнен. Однако я знаю, что размер является положительным целым числом, поэтому выполняется код внутри блока if (size.

Если ptr является адресом начала массива PrivateKey, тогда этот код не должен работать?

Хотя этот код использует openssl, я думаю, что это более общий вопрос с указателями. Если я создаю временный массив uint8_t, он работает, но я бы предпочел скопировать его непосредственно в вектор и сохранить накладные расходы на временную копию.

Вот код:

#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/ossl_typ.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>

#include <iostream>
#include <vector>
#include <string>
#include <cstdint>


int main()
{
    std::vector<uint8_t> input;  // contains pkcs12 data
    std::string Password = "password";
    std::vector<uint8_t> Certificate;
    std::vector<uint8_t> PrivateKey;
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_ciphers();
    OpenSSL_add_all_digests();
    ERR_load_crypto_strings();

    PKCS12* p12_cert = NULL;
    const uint8_t* p1 = &input[0];
    if(d2i_PKCS12(&p12_cert, &p1, input.size()) != NULL) {
        EVP_PKEY *privatekey = NULL;
        X509 *x509_cert = NULL;
        // additional certs, last arg is CA which we don't care about
        if (PKCS12_parse(p12_cert, Password.c_str(), &privatekey, &x509_cert, NULL))
        {
            // populate m_privateKey
            int size = i2d_PrivateKey(privatekey, NULL);
    std::cout << "privatekey size=" << size << '\n';
            if (size > 0)
            {
                PrivateKey.reserve(size);
                uint8_t* ptr = &PrivateKey[0];
                i2d_PrivateKey(privatekey, &ptr);
        std::cout << "PrivateKey size=" << PrivateKey.size() << '\n';
            }
            // populate certificate
            size = i2d_X509(x509_cert, NULL);
            std::cout << "certificate size=" << size << '\n';
            if(size > 0)
            {
                Certificate.reserve(size);
                uint8_t* ptr = &Certificate[0];
                int ret = i2d_X509(x509_cert, &ptr);
        std::cout << "ret=" << ret <<'\n';
        std::cout << "cert size=" << Certificate.size() << '\n';
            }
        }
        PKCS12_free(p12_cert);
    }
}

ОБНОВЛЕНИЕ, можете использовать код ниже, чтобы включить исправление Аррона:

#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/ossl_typ.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>

#include <iostream>
#include <fstream>
#include <iterator>
#include <iomanip>
#include <vector>
#include <string>
#include <cstdint>

using namespace std;

std::vector<uint8_t>& File2Buffer(const std::string path,
                            std::vector<uint8_t>& buffer) {
    fstream fs(path, ios::in | ios::binary);
    if (fs) {
        // Don't skip new lines
        fs.unsetf(ios::skipws);

        fs.seekg(0, ios::end);
        size_t size = static_cast<size_t>(fs.tellg());
        fs.seekg(0, ios::beg);

        buffer.reserve(size);
        buffer.insert(buffer.begin(),
            istream_iterator<uint8_t>(fs),
            istream_iterator<uint8_t>());
    }
    return buffer;
}


int main(int argc, char* argv[])
{
    if (argc != 3) {
        cout << "Usage: " << argv[0] << " <pkcs12 file> " << "<password>\n";
        exit(0);
    }

    std::vector<uint8_t> input;
    File2Buffer(argv[1], input);
    std::string Password = argv[2];
    std::vector<uint8_t> Certificate;
    std::vector<uint8_t> PrivateKey;
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_ciphers();
    OpenSSL_add_all_digests();
    ERR_load_crypto_strings();

    PKCS12* p12_cert = NULL;
    const uint8_t* p1 = &input[0];
    if(d2i_PKCS12(&p12_cert, &p1, input.size()) != NULL) {
        EVP_PKEY *privatekey = NULL;
        X509 *x509_cert = NULL;
        // additional certs, last arg is CA which we don't care about
        if (PKCS12_parse(p12_cert, Password.c_str(), &privatekey, &x509_cert, NULL))
        {
            // populate m_privateKey
            int size = i2d_PrivateKey(privatekey, NULL);
    std::cout << "privatekey size=" << size << '\n';
            if (size > 0)
            {
                PrivateKey.resize(size);
                uint8_t* ptr = &PrivateKey[0];
                i2d_PrivateKey(privatekey, &ptr);
        std::cout << "PrivateKey size=" << PrivateKey.size() << '\n';
            }
            // populate certificate
            size = i2d_X509(x509_cert, NULL);
            std::cout << "certificate size=" << size << '\n';
            if(size > 0)
            {
                Certificate.resize(size);
                uint8_t* ptr = &Certificate[0];
                int ret = i2d_X509(x509_cert, &ptr);
        std::cout << "ret=" << ret <<'\n';
        std::cout << "cert size=" << Certificate.size() << '\n';
            }
        }
        PKCS12_free(p12_cert);
    }

    // test it out:
    if (Certificate.size() > 0) {
        cout << "Certificate size=" << Certificate.size() << '\n';
        for (auto& ch : Certificate) {
            cout << hex << ch << " ";
        }
    }
}

1 Ответ

1 голос
/ 14 апреля 2019

Используйте изменение размера вместо резерва.Проблема с резервом заключается в том, что если вы выполните присваивание (например, PrivateKey [5] = 5) и вызовете PrivateKey.size (), размер все равно останется равным 0. (На практике резерв можно использовать в паре с back_inserter в std ::копировать), но в вашем случае вы должны сделать изменение размера.

...