Alt-Crypt-OpenSSL-PKCS12-Broadbean

 view release on metacpan or  search on metacpan

PKCS12.xs  view on Meta::CPAN

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#define NEED_newCONSTSUB
#include "ppport.h"

#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
#include <openssl/ssl.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/provider.h>
OSSL_PROVIDER *legacy   = NULL;
OSSL_PROVIDER *deflt    = NULL;
#endif
#define NOKEYS          0x1
#define NOCERTS         0x2
#define INFO            0x4
#define CLCERTS         0x8
#define CACERTS         0x10

#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define PKCS12_SAFEBAG_get0_p8inf(o) ((o)->value.keybag)
#define PKCS12_SAFEBAG_get0_attr PKCS12_get_attr
#define PKCS12_SAFEBAG_get_bag_nid M_PKCS12_cert_bag_type
#define PKCS12_SAFEBAG_get_nid M_PKCS12_bag_type
#define PKCS12_SAFEBAG_get1_cert PKCS12_certbag2x509
#define PKCS12_SAFEBAG_get0_safes(o) ((o)->value.safes)
#define PKCS12_SAFEBAG_get0_type(o) (o)->type
#define PKCS8_pkey_get0_attrs(o) ((o)->attributes)
#define PKCS12_SAFEBAG_get0_attrs(o) ((o)->attrib)
#define CONST_PKCS8_PRIV_KEY_INFO PKCS8_PRIV_KEY_INFO
#define CONST_X509_ALGOR X509_ALGOR
#define CONST_X509_SIG X509_SIG
#define CONST_ASN1_TYPE ASN1_TYPE
#define CONST_STACK_OF(o) STACK_OF(o)
#define CONST_X509_NAME X509_NAME
#define CONST_ASN1_INTEGER ASN1_INTEGER
#define CONST_ASN1_OBJECT ASN1_OBJECT
#define CONST_ASN1_OCTET_STRING ASN1_OCTET_STRING
#define CONST_VOID void
#else
#define CONST_PKCS8_PRIV_KEY_INFO const PKCS8_PRIV_KEY_INFO
#define CONST_X509_ALGOR const X509_ALGOR
#define CONST_X509_SIG const X509_SIG
#define CONST_ASN1_TYPE const ASN1_TYPE
#define CONST_STACK_OF(o) const STACK_OF(o)
#define CONST_X509_NAME const X509_NAME
#define CONST_ASN1_INTEGER const ASN1_INTEGER
#define CONST_ASN1_OBJECT const ASN1_OBJECT
#define CONST_ASN1_OCTET_STRING const ASN1_OCTET_STRING
#define CONST_VOID const void
#endif

const EVP_CIPHER *enc;
int dump_certs_pkeys_bags(pTHX_ BIO *out, CONST_STACK_OF(PKCS12_SAFEBAG) *bags,
                           const char *pass, int passlen, int options,
                           char *pempass, const EVP_CIPHER *enc, HV* hash);
static int alg_print(pTHX_ BIO *bio, CONST_X509_ALGOR *alg, HV* hash);
void print_attribute(pTHX_ BIO *out, CONST_ASN1_TYPE *av, char **value);
int print_attribs(pTHX_ BIO *out, CONST_STACK_OF(X509_ATTRIBUTE) *attrlst, const char *name, HV *hash);
void hex_prin(BIO *out, unsigned char *buf, int len);
void dump_cert_text(BIO *out, X509 *x);
SV * get_cert_subject_name(pTHX_ X509 *x);
SV * get_cert_issuer_name(pTHX_ X509 *x);

#define CHECK_OPEN_SSL(p_result) if (!(p_result)) croakSSL(__FILE__, __LINE__);
#define PACKAGE_CROAK(p_message) croak("%s", (p_message))
#define CHECK_NEW(p_var, p_size, p_type) \
   if (New(0, p_var, p_size, p_type) == NULL) \
     { PACKAGE_CROAK("unable to alloc buffer"); }

/* fake our package name */
typedef PKCS12*  Crypt__OpenSSL__PKCS12;

void croakSSL(char* p_file, int p_line) {

  const char* errorReason;

  /* Just return the top error on the stack */
  errorReason = ERR_reason_error_string(ERR_get_error());

  ERR_clear_error();

  croak("%s:%d: OpenSSL error: %s", p_file, p_line, errorReason);
}

EVP_PKEY* _load_pkey(char* keyString, EVP_PKEY*(*p_loader)(BIO*, EVP_PKEY**, pem_password_cb*, void*)) {

  EVP_PKEY* pkey;
  BIO* stringBIO;

  if (!strncmp(keyString, "----", 4)) {

    CHECK_OPEN_SSL(stringBIO = BIO_new_mem_buf(keyString, strlen(keyString)));

  } else {

    CHECK_OPEN_SSL(stringBIO = BIO_new_file(keyString, "r"));
  }

  pkey = p_loader(stringBIO, NULL, NULL, NULL);

  (void)BIO_set_close(stringBIO, BIO_CLOSE);
  BIO_free_all(stringBIO);

  CHECK_OPEN_SSL(pkey);
  return pkey;
}

STACK_OF(X509)* _load_cert_chain(char* keyString, STACK_OF(X509_INFO)*(*p_loader)(BIO*, STACK_OF(X509_INFO)*, pem_password_cb*, void*)) {
  int i;
  STACK_OF(X509_INFO) *xis = NULL;
  X509_INFO *xi = NULL;
  BIO* stringBIO;
  STACK_OF(X509) *stack = sk_X509_new_null();

  if (!strncmp(keyString, "----", 4)) {
    CHECK_OPEN_SSL(stringBIO = BIO_new_mem_buf(keyString, strlen(keyString)));
  } else {
    CHECK_OPEN_SSL(stringBIO = BIO_new_file(keyString, "r"));
  }

  xis = p_loader(stringBIO, NULL, NULL, NULL);
  for (i = 0; i < sk_X509_INFO_num(xis); i++) {
    xi = sk_X509_INFO_value(xis, i);
    if (xi->x509 != NULL && stack != NULL) {
      CHECK_OPEN_SSL(xi->x509);
      if (!sk_X509_push(stack, xi->x509))
        goto end;
      xi->x509 = NULL;
    }
  }

 end:
  sk_X509_INFO_pop_free(xis, X509_INFO_free);
  (void)BIO_set_close(stringBIO, BIO_CLOSE);
  BIO_free_all(stringBIO);

  return stack;
}

static void sv_bio_error(pTHX_ BIO *bio) {

  SV* sv = (SV *)BIO_get_callback_arg(bio);
  if (sv) sv_free(sv);

  BIO_free_all (bio);
}

SV* extractBioString(pTHX_ BIO* p_stringBio)
{
     SV* sv;
     char *datap;
     long datasize = 0;
 
     CHECK_OPEN_SSL(BIO_flush(p_stringBio) == 1);

     datasize = BIO_get_mem_data(p_stringBio, &datap);
     sv = newSVpv(datap, datasize);

     CHECK_OPEN_SSL(BIO_set_close(p_stringBio, BIO_CLOSE) == 1);
     BIO_free(p_stringBio);
     return sv;
}

static const char *ssl_error(pTHX) {
  BIO *bio;
  SV *sv;
  STRLEN l;

  CHECK_OPEN_SSL(bio = BIO_new(BIO_s_mem()));
  ERR_print_errors(bio);
  sv = extractBioString(aTHX_ bio);
  ERR_clear_error();
  return SvPV(sv, l);
}

/* these are trimmed from their openssl/apps/pkcs12.c counterparts */
int dump_certs_pkeys_bag (pTHX_ BIO *bio, PKCS12_SAFEBAG *bag, const char *pass, int passlen, int options, char *pempass, const EVP_CIPHER *enc, HV * bag_hv) {

  EVP_PKEY *pkey;
  X509 *x509;
  CONST_PKCS8_PRIV_KEY_INFO *p8 = NULL;
  CONST_STACK_OF(X509_ATTRIBUTE) *bag_attrs;
  CONST_STACK_OF(X509_ATTRIBUTE) *key_attrs;

  bag_attrs = PKCS12_SAFEBAG_get0_attrs(bag);

/* FIXME: Change NULL to default_enc and allow the encryption to ne overridden like no-des */
#ifndef OPENSSL_NO_DES
  EVP_CIPHER *default_enc = (EVP_CIPHER *)EVP_des_ede3_cbc();
  enc = NULL; /*default_enc; */
#else
  EVP_CIPHER *default_enc = (EVP_CIPHER *)EVP_aes_256_cbc();
  enc = default_enc;
#endif

  switch (PKCS12_SAFEBAG_get_nid(bag)) {

    case NID_keyBag: ;

      if (options & NOKEYS) return 1;

      p8 = PKCS12_SAFEBAG_get0_p8inf(bag);

      if (!(pkey = EVP_PKCS82PKEY (p8))) return 0;

      if (options & INFO) {
        key_attrs = PKCS8_pkey_get0_attrs(p8);
        if (bag_hv) {
          SV * value = newSVpvn("key_bag", strlen("key_bag"));
          if((hv_store(bag_hv, "type", strlen("type"), value, 0)) == NULL)
            croak("unable to add certificate_bag to the bag_hv");

          /* Assign the output to a temporary BIO and free after it is saved to key_sv */
          BIO *keybio;
          CHECK_OPEN_SSL(keybio = BIO_new(BIO_s_mem()));
          PEM_write_bio_PrivateKey (keybio, pkey, enc, NULL, 0, NULL, pempass);
          SV * key_sv = extractBioString(aTHX_ keybio);

          if((hv_store(bag_hv, "key", strlen("key"), key_sv, 0)) == NULL)
            croak("unable to add certificate_bag to the bag_hv");
          print_attribs(aTHX_ bio, bag_attrs, "bag_attributes", bag_hv);
          print_attribs(aTHX_ bio, key_attrs, "key_attributes", bag_hv);
        } else {
          BIO_printf(bio, "Key bag\n");
          print_attribs(aTHX_ bio, bag_attrs, "Bag Attributes", NULL);
          print_attribs(aTHX_ bio, key_attrs, "Key Attributes", NULL);
          PEM_write_bio_PrivateKey (bio, pkey, enc, NULL, 0, NULL, pempass);
        }
      } else {
        PEM_write_bio_PrivateKey (bio, pkey, enc, NULL, 0, NULL, pempass);
      }

      EVP_PKEY_free(pkey);

      break;

    case NID_pkcs8ShroudedKeyBag: ;

      if (options & NOKEYS) return 1;

      if ((p8 = PKCS12_decrypt_skey(bag, pass, passlen)) == NULL)
        return 0;
      if ((pkey = EVP_PKCS82PKEY (p8)) == NULL) {
        PKCS8_PRIV_KEY_INFO_free((PKCS8_PRIV_KEY_INFO *) p8);
        return 0;
      }
      if (options & INFO) {
        CONST_X509_ALGOR *tp8alg;
#if OPENSSL_VERSION_NUMBER > 0x10100000L
        CONST_X509_SIG *tp8;

        tp8 = PKCS12_SAFEBAG_get0_pkcs8(bag);
        X509_SIG_get0(tp8, &tp8alg, NULL);
#else
        tp8alg = bag->value.shkeybag->algor;
#endif
        key_attrs = PKCS8_pkey_get0_attrs(p8);
        if (bag_hv) {
          SV *str_bag = newSVpvn("shrouded_bag", strlen("shrouded_bag"));
          HV * parameters_hv = newHV();;
          if((hv_store(bag_hv, "type", strlen("type"), str_bag, 0)) == NULL)
            croak("unable to add type to the bag_hv");
#if OPENSSL_VERSION_NUMBER > 0x10000000L
          alg_print(aTHX_ bio, tp8alg, parameters_hv);
#endif
          print_attribs(aTHX_ bio, bag_attrs, "bag_attributes", bag_hv);
          if((hv_store(bag_hv, "parameters", strlen("parameters"), newRV_inc((SV *) parameters_hv), 0)) == NULL)
            croak("unable to add bag_attributes to the bag_hv");

          SV *str_key_bag = newSVpvn("shrouded_keybag", strlen("shrouded_keybag"));
          if((hv_store(bag_hv, "type", strlen("type"), str_key_bag, 0)) == NULL)
            croak("unable to add type to the bag_hv");

          print_attribs(aTHX_ bio, key_attrs, "key_attributes", bag_hv);

          /* Assign the output to a temporary BIO and free after it is saved to key_sv */
          BIO *keybio;
          CHECK_OPEN_SSL(keybio = BIO_new(BIO_s_mem()));
          PEM_write_bio_PrivateKey (keybio, pkey, enc, NULL, 0, NULL, pempass);
          SV * key_sv = extractBioString(aTHX_ keybio);

          if((hv_store(bag_hv, "key", strlen("key"), key_sv, 0)) == NULL)
            croak("unable to add certificate_bag to the bag_hv");
        } else {
          BIO_printf(bio, "Shrouded Keybag: ");
          alg_print(aTHX_ bio, tp8alg, NULL);
          print_attribs(aTHX_ bio, bag_attrs, "Bag Attributes", NULL);

          print_attribs(aTHX_ bio, key_attrs, "Key Attributes", NULL);
          PEM_write_bio_PrivateKey (bio, pkey, enc, NULL, 0, NULL, pempass);
        }
      } else {
        PEM_write_bio_PrivateKey (bio, pkey, enc, NULL, 0, NULL, pempass);
      }

      PKCS8_PRIV_KEY_INFO_free((PKCS8_PRIV_KEY_INFO *)p8);

      EVP_PKEY_free(pkey);

      break;

PKCS12.xs  view on Meta::CPAN

            croak("unable to add issuer to the bag_hv");
          PEM_write_bio_X509 (keybio, x509);
          SV * key_sv = extractBioString(aTHX_ keybio);
          if((hv_store(bag_hv, "cert", strlen("cert"), key_sv, 0)) == NULL)
            croak("unable to add certificate_bag to the bag_hv");
        } else
        {
          BIO_printf(bio, "Certificate bag\n");
          print_attribs(aTHX_ bio, bag_attrs, "Bag Attributes", NULL);
          dump_cert_text(bio, x509);
          PEM_write_bio_X509 (bio, x509);
        }
      } else {
        PEM_write_bio_X509 (bio, x509);
      }

      X509_free(x509);

      break;

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
    case NID_secretBag:
        /*FIXME: Not sure how to test this */
        if (options & INFO) {
          char * attribute_value = NULL;
          if (bag_hv){
            SV * value = newSVpvn("secret_bag", strlen("secret_bag"));
            if((hv_store(bag_hv, "type", strlen("type"), value, 0)) == NULL)
              croak("unable to add type to the bag_hv");
            print_attribs(aTHX_ bio, bag_attrs, "bag_attributes", bag_hv);
            Newx(attribute_value, 0, char);
            print_attribute(aTHX_ bio, PKCS12_SAFEBAG_get0_bag_obj(bag), &attribute_value);
            if(hv_store(bag_hv, "bag_value", strlen("bag_value"), newSVpvn(attribute_value, strlen(attribute_value)), 0) == NULL)
              croak("unable to add MAC to the bag_hv");
          }
          else {
            BIO_printf(bio, "Secret bag\n");
            print_attribs(aTHX_ bio, bag_attrs, "Bag Attributes", NULL);
            BIO_printf(bio, "Bag Type: ");
            i2a_ASN1_OBJECT(bio, PKCS12_SAFEBAG_get0_bag_type(bag));
            BIO_printf(bio, "\nBag Value: ");
            print_attribute(aTHX_ bio, PKCS12_SAFEBAG_get0_bag_obj(bag), &attribute_value);
          }
          Safefree(attribute_value);
        } else {
          if (!bag_hv)
            i2a_ASN1_OBJECT(bio, PKCS12_SAFEBAG_get0_bag_type(bag));
        }
        break;
#endif
    case NID_safeContentsBag:
        if (options & INFO) {
          if(bag_hv) {
            SV * value = newSVpvn("safe_contents_bag", strlen("safe_contents_bag"));
            if((hv_store(bag_hv, "type", strlen("type"), value, 0)) == NULL)
              croak("unable to add type to the bag_hv");

            print_attribs(aTHX_ bio, bag_attrs, "bag_attributes", bag_hv);

            dump_certs_pkeys_bags(aTHX_ bio, PKCS12_SAFEBAG_get0_safes(bag),
                                    pass, passlen, options, pempass, enc, bag_hv);
          } else {
            BIO_printf(bio, "Safe Contents bag\n");
            print_attribs(aTHX_ bio, bag_attrs, "Bag Attributes", NULL);
            dump_certs_pkeys_bags(aTHX_ bio, PKCS12_SAFEBAG_get0_safes(bag),
                              pass, passlen, options, pempass, enc, NULL);
          }
        }
        break;
     default:
       if(bag_hv)
          return 1;
        BIO_printf(bio, "Warning unsupported bag type: ");
        i2a_ASN1_OBJECT(bio, PKCS12_SAFEBAG_get0_type(bag));
        BIO_printf(bio, "\n");
  }

  return 1;
}

int dump_certs_pkeys_bags(pTHX_ BIO *bio, CONST_STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass, int passlen, int options, char *pempass, const EVP_CIPHER *enc, HV * hash) {

  int i;

  AV * bags_av = NULL;
  HV * bag_hv = NULL;
  if(hash) {
    bags_av = newAV();
  }
  for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
    if(hash)
      bag_hv = newHV();
    if (!dump_certs_pkeys_bag (aTHX_ bio, sk_PKCS12_SAFEBAG_value (bags, i), pass, passlen, options, pempass, enc, bag_hv)) {
      return 0;
    }
    if(hash)
      av_push(bags_av, newRV_inc((SV *) bag_hv));
  }

  if (hash) {
    SV **svp = NULL;
    char *type;
    if (hv_exists(bag_hv, "type", strlen("type"))) {
      svp = hv_fetch(bag_hv, "type", strlen("type"), 0);
      if (svp != NULL)
          type = SvPVbyte_nolen(*svp);
    }
    if (svp != NULL) {
      if (strcmp(type, "safe_contents_bag") == 0 ) {
        if((hv_store(hash, "safe_contents_bag", strlen("safe_contents_bag"), newRV_inc((SV *) bags_av), 0)) == NULL)
          croak("unable to add bags to the hash");
      } else {
        if((hv_store(hash, "bags", strlen("bags"), newRV_inc((SV *) bags_av), 0)) == NULL)
          croak("unable to add bags to the hash");
      }
    }
  }
  return 1;
}

int dump_certs_keys_p12(pTHX_ BIO *bio, PKCS12 *p12, char *pass, int passlen, int options, char *pempass, HV * orig_hash) {

  STACK_OF(PKCS7) *asafes;
  STACK_OF(PKCS12_SAFEBAG) *bags;

  int i, bagnid;
  PKCS7 *p7;
  HV * bag_hv = NULL;
  HV * parameters_hv = NULL;
  AV * pkcs7_bags_av = newAV();
  AV * pkcs7_enc_bags_av = newAV();

  if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL) {
    croak("Unable to PKCS12_unpack_authsafes");
    return 0;
  }

  for (i = 0; i < sk_PKCS7_num(asafes); i++) {

  if (orig_hash) {
    parameters_hv = newHV();
    bag_hv = newHV();
  }

    p7 = sk_PKCS7_value(asafes, i);

    bagnid = OBJ_obj2nid(p7->type);

    if (bagnid == NID_pkcs7_data) {

      if (options & INFO) {
        if (orig_hash) {
          bag_hv = newHV();
          av_push(pkcs7_bags_av, newRV_inc((SV *) bag_hv));
        }
        else
          BIO_printf(bio, "PKCS7 Data\n");
      }
      bags = PKCS12_unpack_p7data(p7);

    } else if (bagnid == NID_pkcs7_encrypted) {
      if (options & INFO) {
        if (orig_hash) {
          bag_hv = newHV();

          if (p7->d.encrypted != NULL)
            alg_print(aTHX_ bio, p7->d.encrypted->enc_data->algorithm, parameters_hv);
          if((hv_store(bag_hv, "parameters", strlen("parameters"), newRV_inc((SV *) parameters_hv), 0)) == NULL)
            croak("unable to add parameters to the hash");
          if((hv_store(orig_hash, "pkcs7_encrypted_data", strlen("pkcs7_encrypted_data"), newRV_inc((SV *) bag_hv), 0)) == NULL)
            croak("unable to add pkcs7_encrypted_data to the orig_hash");
          av_push(pkcs7_enc_bags_av, newRV_inc((SV *) bag_hv));
        } else {
          BIO_printf(bio, "PKCS7 Encrypted data: ");
          if (p7->d.encrypted == NULL) {
            BIO_printf(bio, "<no data>\n");
          } else {
            alg_print(aTHX_ bio, p7->d.encrypted->enc_data->algorithm, NULL);
          }
        }
      }
      bags = PKCS12_unpack_p7encdata(p7, pass, passlen);

    } else {
      continue;
    }

    if (bags == NULL) return 0;

    if (!dump_certs_pkeys_bags(aTHX_ bio, bags, pass, passlen, options, pempass, enc, bag_hv)) {
      sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
      return 0;
    }

  if (orig_hash) {
    if((hv_store(orig_hash, "pkcs7_data", strlen("pkcs7_data"), newRV_inc((SV *) pkcs7_bags_av), 0)) == NULL)
      croak("unable to add bags to the hash");
    if((hv_store(orig_hash, "pkcs7_encrypted_data", strlen("pkcs7_encrypted_data"), newRV_inc((SV *) pkcs7_enc_bags_av), 0)) == NULL)
      croak("unable to add bags to the hash");
  }
    sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
  }

  sk_PKCS7_pop_free(asafes, PKCS7_free);

  return 1;
}
#ifdef TIM
# define B_FORMAT_TEXT   0x8000
# define FORMAT_TEXT    (1 | B_FORMAT_TEXT)     /* Generic text */
int FMT_istext(int format)
{
  return (format & B_FORMAT_TEXT) == B_FORMAT_TEXT;
}

BIO *dup_bio_err(int format)
{
  BIO *b = BIO_new_fp(stderr,
                      BIO_NOCLOSE | (FMT_istext(format) ? BIO_FP_TEXT : 0));

  return b;
}
#endif
static unsigned long nmflag = 0;
static char nmflag_set = 0;
# define XN_FLAG_SPC_EQ          (1 << 23)/* Put spaces round '=' */

#define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)

#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
                   ASN1_STRFLGS_ESC_QUOTE | \
                   ASN1_STRFLGS_ESC_CTRL | \
                   ASN1_STRFLGS_ESC_MSB)

unsigned long get_nameopt(void)
{
  return
      nmflag_set ? nmflag : ESC_FLAGS | XN_FLAG_SEP_CPLUS_SPC | ASN1_STRFLGS_UTF8_CONVERT | XN_FLAG_SPC_EQ;
}

void print_name(BIO *out, const char *title, CONST_X509_NAME *nm)
{
  char *buf;
  char mline = 0;
  int indent = 0;
  unsigned long lflags = get_nameopt();

  if (out == NULL)
    return;
  if (title != NULL)

PKCS12.xs  view on Meta::CPAN

Crypt::OpenSSL::PKCS12
new_from_string(class, string)
  SV  *class
  SV  *string

  ALIAS:
  new_from_file = 1

  PREINIT:
  BIO *bio;
  STRLEN str_len;
  char *str_ptr;
  CODE:

  SvGETMAGIC(string);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
  /* FIXME: There should likely be an option for whether to load the legacy provider */
  legacy = OSSL_PROVIDER_load(NULL, "legacy");
  if (legacy == NULL) {
      warn("Failed to load Legacy provider\n");
  }
  deflt = OSSL_PROVIDER_load(NULL, "default");
  if (deflt == NULL) {
      OSSL_PROVIDER_unload(legacy);
      croak("Failed to load Default provider\n");
  }
#endif

  if (SvPOKp(string) || SvNOKp(string) || SvIOKp(string)) {
    if (ix == 1) {
      /* We are not looking up the SV's UTF8 bit because BIO_new_file() accepts
       * filename like syscall fopen() which mainly may accept octet sequences
       * for UTF-8 in C char*. That's what we get from using SvPV(). Also,
       * using SvPV() is not a bug if ASCII input is only allowed. */
      str_ptr = SvPV(string, str_len);
    } else {
      /* To avoid encoding mess, caller is not allowed to provide octets from
       * UTF-8 encoded strings. BIO_new_mem_buf() needs octet input only. */
      if (SvUTF8(string)) {
        croak("PKCS12_new_from: Source string must not be UTF-8 encoded (please use octets)");
      }
      str_ptr = SvPV(string, str_len);
    }
  } else {
    croak("PKCS12_new_from: Invalid Perl type for string or file was passed (0x%x).", (unsigned int)SvFLAGS(string));
  }

  if (!str_ptr || !str_len) croak("PKCS12_new_from: No string or file was passed.");

  if (ix == 1) {
    bio = BIO_new_file(str_ptr, "rb");
  } else {
    bio = BIO_new_mem_buf(str_ptr, str_len);
  }

  if (!bio) croak("Failed to create BIO");

  /* this can come in any number of ways */
  if ((RETVAL = d2i_PKCS12_bio(bio, 0)) == NULL) {
    BIO_free_all(bio);
    croak("%" SVf ": Couldn't create PKCS12 from d2i_PKCS12_BIO(): %s", SVfARG(class), ssl_error(aTHX));
  }

  BIO_free_all(bio);

  OUTPUT:
  RETVAL

# This is called at per-object destruction time.
void
DESTROY(pkcs12)
  Crypt::OpenSSL::PKCS12 pkcs12;

  CODE:
  if (pkcs12) {
    PKCS12_free(pkcs12);
  }

# This is called via an END block in the Perl module to clean up initialization that happened in BOOT.
void
__PKCS12_cleanup(void)
  CODE:

  CRYPTO_cleanup_all_ex_data();
  ERR_free_strings();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
  ERR_remove_state(0);
#endif
  EVP_cleanup();

SV*
as_string(pkcs12)
  Crypt::OpenSSL::PKCS12 pkcs12;

  PREINIT:
  BIO *bio;

  CODE:

  CHECK_OPEN_SSL(bio = BIO_new(BIO_s_mem()));

  if (!(i2d_PKCS12_bio(bio, pkcs12))) {
    sv_bio_error(aTHX_ bio);
    croak("i2d_PKCS12_bio: %s", ssl_error(aTHX));
  }

  RETVAL = extractBioString(aTHX_ bio);

  OUTPUT:
  RETVAL

SV*
mac_ok(pkcs12, pwd = "")
  Crypt::OpenSSL::PKCS12 pkcs12
  char *pwd

  CODE:

  if (!(PKCS12_verify_mac(pkcs12, pwd, strlen(pwd)))) {
    croak("PKCS12_verify_mac: \n%s", ssl_error(aTHX));
  }

  RETVAL = (PKCS12_verify_mac(pkcs12, pwd, strlen(pwd))) ? &PL_sv_yes : &PL_sv_no;

  OUTPUT:
  RETVAL

SV*
changepass(pkcs12, oldpwd = "", newpwd = "")
  Crypt::OpenSSL::PKCS12 pkcs12
  char *oldpwd
  char *newpwd

  CODE:

  if (!(PKCS12_newpass(pkcs12, oldpwd, newpwd))) {
    warn("PKCS12_newpass: %s %s\n%s", oldpwd, newpwd, ssl_error(aTHX));
    RETVAL = &PL_sv_no;
  } else {
    RETVAL = &PL_sv_yes;
  }

  OUTPUT:
  RETVAL

SV*
create(pkcs12, cert_chain_pem = "", pk = "", pass = 0, file = 0, name = "PKCS12 Certificate")
  char *cert_chain_pem
  char *pk
  char *pass
  char *file
  char *name

  PREINIT:
  FILE *fp;
  EVP_PKEY* pkey;
  PKCS12 *p12;
  STACK_OF(X509) *cert_chain = NULL;

  CODE:

  pkey       = _load_pkey(pk, PEM_read_bio_PrivateKey);
  cert_chain = _load_cert_chain(cert_chain_pem, PEM_X509_INFO_read_bio);
  p12        = PKCS12_create(pass, name, pkey, sk_X509_shift(cert_chain), cert_chain, 0, 0, 0, 0, 0);

  if (!p12) {
    ERR_print_errors_fp(stderr);
    croak("Error creating PKCS#12 structure\n");
  }

  if (!(fp = fopen(file, "wb"))) {
    ERR_print_errors_fp(stderr);
    croak("Error opening file %s\n", file);
  }

  i2d_PKCS12_fp(fp, p12);
  PKCS12_free(p12);
  fclose(fp);

  RETVAL = &PL_sv_yes;

  OUTPUT:
  RETVAL


SV*
create_as_string(pkcs12, cert_chain_pem = "", pk = "", pass = 0, name = "PKCS12 Certificate")
  char *cert_chain_pem
  char *pk
  char *pass
  char *name

  PREINIT:
  BIO *bio;
  EVP_PKEY* pkey;
  PKCS12 *p12;
  STACK_OF(X509) *cert_chain = NULL;



( run in 0.443 second using v1.01-cache-2.11-cpan-acf6aa7dc9e )