Home > Software engineering >  Failing to generate X509 CSR with OpenSSL
Failing to generate X509 CSR with OpenSSL

Time:11-04

I am trying to generate a signing request. Apparentely there is an error somewhere in the code (or UB most likelly) which leads to:

  • garbage output locally
  • returned code 139 on godbolt

#include <memory>
#include <stdexcept>
#include <string>

#include <iostream>

namespace {
std::string make_csr();
} // namespace

int main(int, char **) {
    try {
        std::cout << ::make_csr() << std::endl;
        return 0;
    }
    catch (const std::exception &e)
    {
        std::cerr << "Failed with error: " << e.what() << std::endl;
        return -1;
    }
}

#include <openssl/aes.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>

namespace ssl_utils {

template <typename T> void free(T *);

template <typename T> T *make();

template <typename T> class raii_obj {
private:
  struct _free {
    void operator()(T *ptr) const { ssl_utils::free(ptr); }
  };

public:
  using type = T;
  using ptr = std::unique_ptr<type, _free>;

  static ptr make() { return ptr(ssl_utils::make<type>()); }
};

using raii_bignum = raii_obj<BIGNUM>;
using raii_evp_pkey = raii_obj<EVP_PKEY>;
using raii_x509_req = raii_obj<X509_REQ>;

} // namespace ssl_utils

namespace {

std::string make_csr() {

  constexpr size_t privateKeyLength{2048};
  auto pBignum = ssl_utils::raii_bignum::make();
  auto pKey = ssl_utils::raii_evp_pkey::make();

  RSA *rsa = RSA_new();

  if (!(static_cast<bool>(pBignum) && //
        static_cast<bool>(rsa) &&     //
        BN_set_word(pBignum.get(), RSA_F4) &&
        RSA_generate_key_ex(rsa,              //
                                 privateKeyLength, //
                                 pBignum.get(),    //
                                 nullptr) &&
        EVP_PKEY_assign_RSA(pKey.get(), rsa))) {
    throw std::runtime_error("Failed to generate RSA key!");
  }

  auto pReq = ssl_utils::raii_x509_req::make();
  X509_REQ_set_version(pReq.get(), 0L);
  X509_REQ_set_pubkey(pReq.get(), pKey.get());

  X509_NAME *const pName = X509_REQ_get_subject_name(pReq.get());

  X509_NAME_add_entry_by_txt(
      pName, LN_pkcs9_emailAddress, MBSTRING_UTF8,
      reinterpret_cast<const unsigned char *>(
          "[email protected]"),
      -1, -1, 0);

  if (!X509_REQ_sign(pReq.get(), pKey.get(), EVP_md5()) ||
      !X509_REQ_verify(pReq.get(), pKey.get()))
    throw std::runtime_error("CSR signing failed!");

  unsigned char *request = request;
  const std::string::size_type reqSize =
      i2d_X509_REQ(pReq.get(), &request);

  std::string strReq{reinterpret_cast<char *>(request), reqSize};
  // CRYPTO_free(request); // does not compile

  return strReq;
}
}

namespace ssl_utils {

template <> BIGNUM *make() { return BN_new(); }

template <> void free(BIGNUM *ptr) { BN_free(ptr); }

template <> EVP_PKEY *make() { return EVP_PKEY_new(); }

template <> void free(EVP_PKEY *ptr) { EVP_PKEY_free(ptr); }

template <> X509_REQ *make() { return X509_REQ_new(); }

template <> void free(X509_REQ *ptr) { X509_REQ_free(ptr); }
} // namespace ssl_utils

When debugging on my local machine, I don't see any of the X509_REQ_sign, X509_REQ_verify or i2d_X509_REQ calls failing. So my first thought was that the console's encoding was to blame:

0�j0�R 0%1#0!   *�H��
    [email protected]�"0
    *�H��
 � 0�
� ��*��6�
�*x���~����V'U&�, x�c|ϊ����t�!cjBS  3��pI�?�i�弒 s�8���2�r��N�f�T�sq�>Ն�0���#��Z�\�Td_�?im$m]�Ts�RW"�M���Xr��Z r�J���Q$���,ɏ4��)8`R�����)ދAn��B�Di:W�������}
x(ʴ���v�&%�X����k}�P��U�7��ƀ{��l��O!<A��"I���R
�%�o!v׸�k�̳ � 0
    *�H��
 � J�V��K����Pj�u"���nX&yK��e�p��������/V��A��r����Р�c�s�ֶ�>�T4�7���5j������BF�s0D��f�-j�w�c �[:���5cf����0�u�`����&$�"�� ���?��dR��2��w ��v���}5޴qA�8�Rw� ���J��B��Y�n����Z�ۭ]gLt���gۿY��Q3B#k����bш�VWjN�5P�
���B��Ţ���۪��   ��

But then I checked the code at godbold and found out that it crashes at some point there.
I am using OpenSSL for MinGW-w64 from Msys2 package (version "1.1.1q").

CodePudding user response:

The root issue is here, the typo:

unsigned char *request = request;

It is initialized with an unspecified value and should be

unsigned char *request = nullptr;

Otherwise i2d_X509_REQ tries to realloc the invalid pointer.

CRYPTO_free(request); // does not compile

should be

OPENSSL_free(request);

https://godbolt.org/z/ofnc3M9h1

  • Related