The C functions are:
int i2d_PrivateKey(EVP_PKEY *a, unsigned char **pp);
int i2d_X509(X509 *a, unsigned char **ppout);
And I have written code like this to copy into the 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() returns zero so I know that the vector has not been populated. However, I know that size is a positive integer so the code inside the if (size block is executed.
If ptr is the address of the start of the PrivateKey array then shouldn't this code work?
Although this code uses openssl, it is more a general pointers question I think. If I create a temporary uint8_t array then it works but I would rather copy directly into the vector and save the overhead of the temporary copy.
Here is the code:
#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);
}
}
UPDATE, can use code below to incorporate Arron's fix:
#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 << " ";
}
}
}
Use resize instead of reserve. The problem with reserve is that if you do an assign(like PrivateKey[5] = 5), and call PrivateKey.size(), size will still remain 0. (Reserve in practice can be used in pair with back_inserter in std::copy), but in your case, you should do an resize.