]> granicus.if.org Git - php/commitdiff
Added some preliminary x509 cert and csr creation/signing functions.
authorWez Furlong <wez@php.net>
Mon, 10 Sep 2001 23:57:10 +0000 (23:57 +0000)
committerWez Furlong <wez@php.net>
Mon, 10 Sep 2001 23:57:10 +0000 (23:57 +0000)
Also, some problems with CGI initialization should be eliminated.
Renamed some pkey functions to be more consistent.
# Added aliases for older names; not sure if we should keep those.

ext/openssl/openssl.c

index 52d7071dc9d2b6525ce16dce78ad3a02922e9b0e..efaadf00ddd884060e2dbc18b8e3276f9b7d0b2f 100644 (file)
 #include <openssl/crypto.h>
 #include <openssl/pem.h>
 #include <openssl/err.h>
+#include <openssl/conf.h>
+#include <openssl/e_os.h>
+#include <openssl/rand.h>
+
+#define DEFAULT_KEY_LENGTH     512
+#define MIN_KEY_LENGTH         384
+
 
 #define DEBUG_SMIME    0
 
@@ -48,23 +55,46 @@ static unsigned char arg2of4_force_ref[] =
 static unsigned char arg2and3of4_force_ref[] =
                        { 4, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_NONE };
 
+enum php_openssl_key_type      {
+       OPENSSL_KEYTYPE_RSA,
+       OPENSSL_KEYTYPE_DSA,
+       OPENSSL_KEYTYPE_DH,
+       OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA
+};
+
 /* {{{ openssl_functions[]
  */
 function_entry openssl_functions[] = {
-       PHP_FE(openssl_get_privatekey,     NULL)
-       PHP_FE(openssl_get_publickey,      NULL)
-       PHP_FE(openssl_free_key,           NULL)
-
-       PHP_FE(openssl_x509_read,                       NULL)
+/* public/private key functions */
+       PHP_FE(openssl_pkey_free,                       NULL)
+       PHP_FE(openssl_pkey_new,                        NULL)
+       PHP_FE(openssl_pkey_export,                     second_arg_force_ref)
+       PHP_FE(openssl_pkey_get_private,        NULL)
+       PHP_FE(openssl_pkey_get_public,         NULL)
+
+       PHP_FALIAS(openssl_free_key,            openssl_pkey_free,                      NULL)
+       PHP_FALIAS(openssl_get_privatekey,      openssl_pkey_get_private,       NULL)
+       PHP_FALIAS(openssl_get_publickey,       openssl_pkey_get_public,        NULL)
+
+/* x.509 cert funcs */
+       PHP_FE(openssl_x509_read,                               NULL)
        PHP_FE(openssl_x509_free,                       NULL)
-
-       PHP_FE(openssl_x509_parse,                        NULL)
+       PHP_FE(openssl_x509_parse,                              NULL)
        PHP_FE(openssl_x509_checkpurpose,               NULL)
+       PHP_FE(openssl_x509_check_private_key,  NULL)
+       PHP_FE(openssl_x509_export,                             second_arg_force_ref)
 
+/* CSR funcs */
+       PHP_FE(openssl_csr_new,                         second_arg_force_ref)
+       PHP_FE(openssl_csr_export,                      second_arg_force_ref)
+       PHP_FE(openssl_csr_sign,                        NULL)
+
+       
        PHP_FE(openssl_sign,               arg2of3_force_ref)
        PHP_FE(openssl_verify,             NULL)
        PHP_FE(openssl_seal,               arg2and3of4_force_ref)
        PHP_FE(openssl_open,               arg2of4_force_ref)
+
 /* for S/MIME handling */
        PHP_FE(openssl_pkcs7_verify,              NULL)
        PHP_FE(openssl_pkcs7_decrypt,             NULL)
@@ -99,29 +129,394 @@ zend_module_entry openssl_module_entry = {
 ZEND_GET_MODULE(openssl)
 #endif
 
-static void _php_pkey_free(zend_rsrc_list_entry *rsrc TSRMLS_DC);
 static int le_key;
-
-static void _php_x509_free(zend_rsrc_list_entry *rsrc TSRMLS_DC);
 static int le_x509;
+static int le_csr;
+
+/* {{{ resource destructors */
+static void php_pkey_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+       EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr;
+       EVP_PKEY_free(pkey);
+}
+
+static void php_x509_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+       X509 *x509 = (X509 *)rsrc->ptr;
+       X509_free(x509);
+}
+
+static void php_csr_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+       X509_REQ * csr = (X509_REQ*)rsrc->ptr;
+       X509_REQ_free(csr);
+}
+/* }}} */
+
+/* {{{ openssl -> PHP "bridging" */
+/* true global; readonly after module startup */
+static char default_ssl_conf_filename[PATH_MAX];
+
+struct php_x509_request {
+       LHASH * global_config;  /* Global SSL config */
+       LHASH * req_config;             /* SSL config for this request */
+       const EVP_MD * md_alg;
+       const EVP_MD * digest;
+       char    * section_name,
+                       * config_filename,
+                       * digest_name,
+                       * extensions_section,
+                       * request_extensions_section;
+       int priv_key_bits;
+       int priv_key_type;
+
+       int priv_key_encrypt;
+
+       EVP_PKEY * priv_key;
+};
+
 
 static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC);
 static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC);
 static X509_STORE        * setup_verify(zval * calist TSRMLS_DC);
 static STACK_OF(X509) * load_all_certs_from_file(char *certfile);
+static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC);
+static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC);
+
+
+static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname)
+{
+       zval * subitem;
+       int i;
+       char * sn, * ln;
+       int nid;
+       X509_NAME_ENTRY * ne;
+       ASN1_STRING * str;
+       ASN1_OBJECT * obj;
+
+       MAKE_STD_ZVAL(subitem);
+       array_init(subitem);
+
+       for (i = 0; i < X509_NAME_entry_count(name); i++)       {
+               ne      = X509_NAME_get_entry(name, i);
+               obj = X509_NAME_ENTRY_get_object(ne);
+               str = X509_NAME_ENTRY_get_data(ne);
+               nid = OBJ_obj2nid(obj);
+               if (shortname)  {
+                       sn = (char*)OBJ_nid2sn(nid);
+                       add_assoc_stringl(subitem, sn, str->data, str->length, 1);
+               }
+               else    {
+                       ln = (char*)OBJ_nid2ln(nid);
+                       add_assoc_stringl(subitem, ln, str->data, str->length, 1);
+               }
+       }
+       zend_hash_update(HASH_OF(val), key, strlen(key) + 1, (void *)&subitem, sizeof(subitem), NULL);
+}
+
+static void add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str)
+{
+       add_assoc_stringl(val, key, str->data, str->length, 1);
+}
+
+static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC)
+{
+/*
+       This is how the time string is formatted:
+
+   sprintf(p,"%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
+      ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
+*/
+
+       time_t ret;
+       struct tm thetime;
+       char * strbuf;
+       char * thestr;
+       long gmadjust = 0;
+
+       if (timestr->length < 13)       {
+               zend_error(E_WARNING, "%s(): extension author too lazy to parse %s correctly", get_active_function_name(TSRMLS_C), timestr->data);
+               return (time_t)-1;
+       }
+
+       strbuf = estrdup(timestr->data);
+
+
+       memset(&thetime, 0, sizeof(thetime));
+
+       /* we work backwards so that we can use atoi more easily */
+
+       thestr = strbuf + timestr->length - 3;
+
+       thetime.tm_sec = atoi(thestr);
+       *thestr = '\0';
+       thestr -= 2;
+       thetime.tm_min = atoi(thestr);
+       *thestr = '\0';
+       thestr -= 2;
+       thetime.tm_hour = atoi(thestr);
+       *thestr = '\0';
+       thestr -= 2;
+       thetime.tm_mday = atoi(thestr);
+       *thestr = '\0';
+       thestr -= 2;
+       thetime.tm_mon = atoi(thestr)-1;
+       *thestr = '\0';
+       thestr -= 2;
+       thetime.tm_year = atoi(thestr);
+
+       if (thetime.tm_year < 68)
+               thetime.tm_year += 100;
+
+       thetime.tm_isdst = -1;
+       ret = mktime(&thetime);
+
+#if HAVE_TM_GMTOFF
+       gmadjust = thetime.tm_gmtoff;
+#else
+       /*
+       ** If correcting for daylight savings time, we set the adjustment to
+       ** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and
+       ** set the adjustment to the main timezone + 3600 seconds.
+       */
+       gmadjust = -(thetime.tm_isdst ? timezone - 3600 : timezone + 3600);
+#endif
+       ret += gmadjust;
+
+       efree(strbuf);
+
+       return ret;
+}
+
+static inline int php_openssl_config_check_syntax(
+               const char * section_label,
+               const char * config_filename,
+               const char * section,
+               LHASH * config TSRMLS_DC)
+{
+       X509V3_CTX ctx;
+       
+       X509V3_set_ctx_test(&ctx);
+       X509V3_set_conf_lhash(&ctx, config);
+       if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL))  {
+               zend_error(E_WARNING, "%s(): Error loading %s section %s of %s",
+                               get_active_function_name(TSRMLS_C),
+                               section_label,
+                               section,
+                               config_filename);
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static int add_oid_section(struct php_x509_request * req TSRMLS_DC)
+{
+       char * str;
+       STACK_OF(CONF_VALUE) * sktmp;
+       CONF_VALUE * cnf;
+       int i;
+
+       str = CONF_get_string(req->req_config, NULL, "oid_section");
+       if (str == NULL)
+               return SUCCESS;
+       
+       sktmp = CONF_get_section(req->req_config, str);
+       if (sktmp == NULL)      {
+               zend_error(E_WARNING, "%s(): problem loading oid section %s",
+                               get_active_function_name(TSRMLS_C), str);
+               return FAILURE;
+       }
+       for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++)  {
+               cnf = sk_CONF_VALUE_value(sktmp, i);
+               if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef)  {
+                       zend_error(E_WARNING, "%s(): problem creating object %s=%s",
+                                       get_active_function_name(TSRMLS_C),
+                                       cnf->name, cnf->value);
+                       return FAILURE;
+               }
+       }
+       return SUCCESS;
+}
+
+#define PHP_SSL_REQ_INIT(req)          memset(req, 0, sizeof(*req))
+#define PHP_SSL_REQ_DISPOSE(req)       php_openssl_dispose_config(req TSRMLS_C)
+#define PHP_SSL_REQ_PARSE(req, zval)   php_openssl_parse_config(req, zval TSRMLS_C)
+
+#define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \
+                       req->config_filename, req->var, req->req_config TSRMLS_C) == FAILURE) return FAILURE
+
+#define SET_OPTIONAL_STRING_ARG(key, varname, defval)  \
+       if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \
+               varname = Z_STRVAL_PP(item); \
+       else \
+               varname = defval
+
+#define SET_OPTIONAL_LONG_ARG(key, varname, defval)    \
+       if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \
+               varname = Z_LVAL_PP(item); \
+       else \
+               varname = defval
+
+
+
+static int php_openssl_parse_config(
+               struct php_x509_request * req,
+               zval * optional_args
+               TSRMLS_DC
+               )
+{
+       char * str;
+       zval ** item;
+
+       SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
+       SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
+       req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
+       req->req_config = CONF_load(NULL, req->config_filename, NULL);
+               
+       if (req->req_config == NULL)
+               return FAILURE;
+       
+
+       /* read in the oids */
+       str = CONF_get_string(req->req_config, NULL, "oid_file");
+       if (str)        {
+               BIO * oid_bio = BIO_new_file(str, "r");
+               if (oid_bio)    {
+                       OBJ_create_objects(oid_bio);
+                       BIO_free(oid_bio);
+               }
+       }
+       if (add_oid_section(req TSRMLS_C) == FAILURE)
+               return FAILURE;
+
+       SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name,
+                       CONF_get_string(req->req_config, req->section_name, "default_md"));     
+       SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section,
+                       CONF_get_string(req->req_config, req->section_name, "x509_extensions"));
+       SET_OPTIONAL_STRING_ARG("req_extensions", req->extensions_section,
+                       CONF_get_string(req->req_config, req->request_extensions_section, "req_extensions"));
+       SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits,
+                       CONF_get_number(req->req_config, req->section_name, "default_bits"));
+
+       SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT);
+
+       if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), "encrypt_key", sizeof("encrypt_key"), (void**)&item) == SUCCESS) {
+               req->priv_key_encrypt = Z_BVAL_PP(item);
+       }
+       else    {
+               str = CONF_get_string(req->req_config, req->section_name, "encrypt_rsa_key");
+               if (str == NULL)
+                       str = CONF_get_string(req->req_config, req->section_name, "encrypt_key");
+               if (str && strcmp(str, "no") == 0)
+                       req->priv_key_encrypt = 0;
+               else
+                       req->priv_key_encrypt = 1;
+       }
+
+       
+       /* digest alg */
+       if (req->digest_name == NULL)
+               req->digest_name = CONF_get_string(req->req_config, req->section_name, "default_md");
+       if (req->digest_name)
+               req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
+       if (req->md_alg == NULL)
+               req->md_alg = req->digest = EVP_md5();
+
+       PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section);
+
+       /* set the string mask */
+       str = CONF_get_string(req->req_config, req->section_name, "string_mask");
+       if (str && !ASN1_STRING_set_default_mask_asc(str))      {
+               zend_error(E_WARNING, "%s(): Invalid global string mask setting %s",
+                               get_active_function_name(TSRMLS_C), str);
+               return FAILURE;
+       }
+
+       if (req->request_extensions_section == NULL)
+               req->request_extensions_section = CONF_get_string(req->req_config, req->section_name, "req_extensions");
+
+       PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section);
+       
+       return SUCCESS;
+}
+
+static void php_openssl_dispose_config(struct php_x509_request * req TSRMLS_DC)
+{
+       if (req->priv_key)      {
+               EVP_PKEY_free(req->priv_key);
+               req->priv_key = NULL;
+       }
+       if (req->global_config) {
+               CONF_free(req->global_config);
+               req->global_config = NULL;
+       }
+       if (req->req_config)    {
+               CONF_free(req->req_config);
+               req->req_config = NULL;
+       }
+}
+
+static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded)
+{
+       char buffer[PATH_MAX];
+
+       *egdsocket = 0;
+       *seeded = 0;
+       
+#ifdef WINDOWS
+       RAND_screen();
+#endif
+       if (file == NULL)
+               file = RAND_file_name(buffer, sizeof(buffer));
+       else if (RAND_egd(file) > 0)    {
+               /* if the given filename is an EGD socket, don't
+                * write anything back to it */
+               *egdsocket = 1;
+               return SUCCESS;
+       }
+       if (file == NULL || !RAND_load_file(file, -1))  {
+               if (RAND_status() == 0) {
+                       zend_error(E_WARNING, "unable to load random state; not enough random data!");
+                       return FAILURE;
+               }
+               return FAILURE;
+       }
+       *seeded = 1;
+       return SUCCESS;
+}
+
+static int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded)
+{
+       char buffer[PATH_MAX];
+       if (egdsocket || !seeded)       {
+               /* if we did not manage to read the seed file, we should not write
+                * a low-entropy seed file back */
+               return FAILURE;
+       }
+       if (file == NULL)
+               file = RAND_file_name(buffer, sizeof(buffer));
+       if (file == NULL || !RAND_write_file(file))     {
+               zend_error(E_WARNING, "unable to write random state");
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+/* }}} */
 
 /* {{{ PHP_MINIT_FUNCTION
  */
 PHP_MINIT_FUNCTION(openssl)
 {
-       le_key = zend_register_list_destructors_ex(_php_pkey_free, NULL, "OpenSSL key", module_number);
-       le_x509 = zend_register_list_destructors_ex(_php_x509_free, NULL, "OpenSSL X.509", module_number);
+       char * config_filename;
+       
+       le_key = zend_register_list_destructors_ex(php_pkey_free, NULL, "OpenSSL key", module_number);
+       le_x509 = zend_register_list_destructors_ex(php_x509_free, NULL, "OpenSSL X.509", module_number);
+       le_csr = zend_register_list_destructors_ex(php_csr_free, NULL, "OpenSSL X.509 CSR", module_number);
 
        OpenSSL_add_all_ciphers();
+       OpenSSL_add_all_digests();
+       OpenSSL_add_all_algorithms();
 
-/*
-       SSL_load_error_strings();
-*/
        ERR_load_ERR_strings();
        ERR_load_crypto_strings();
        ERR_load_EVP_strings();
@@ -159,6 +554,28 @@ PHP_MINIT_FUNCTION(openssl)
                               RSA_PKCS1_OAEP_PADDING,
                               CONST_CS|CONST_PERSISTENT);
 
+       /* Values for key types */
+       REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA",
+                       OPENSSL_KEYTYPE_RSA, CONST_CS | CONST_PERSISTENT);
+#ifndef NO_DSA
+       REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA",
+                       OPENSSL_KEYTYPE_DSA, CONST_CS | CONST_PERSISTENT);
+#endif
+       REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH",
+                       OPENSSL_KEYTYPE_DH, CONST_CS | CONST_PERSISTENT);
+       
+       /* Determine default SSL configuration file */
+       config_filename = getenv("OPENSSL_CONF");
+       if (config_filename == NULL)
+               config_filename = getenv("SSLEAY_CONF");
+       if (config_filename == NULL)    {
+               snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s",
+                               X509_get_default_cert_area(),
+                               OPENSSL_CONF);
+       }
+       else
+               strncpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
+
        return SUCCESS;
 }
 /* }}} */
@@ -183,6 +600,8 @@ PHP_MSHUTDOWN_FUNCTION(openssl)
 }
 /* }}} */
 
+/* {{{ x509 cert functions */
+
 /* {{{ php_openssl_x509_from_zval
        Given a zval, coerce it into an X509 object.
        The zval can be:
@@ -251,702 +670,1101 @@ static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * r
        }
        return cert;
 }
+
 /* }}} */
 
-/* {{{ php_openssl_evp_from_zval
-   Given a zval, coerce it into a EVP_PKEY object.
-       It can be:
-               1. private key resource from openssl_get_privatekey()
-               2. X509 resource -> public key will be extracted from it
-               3. if it starts with file:// interpreted as path to key file
-               4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
-               5. an array(0 => [items 2..4], 1 => passphrase)
-       NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
-       empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
-       the Apache error log!
-*/
-static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC)
+/* {{{ proto openssl_x509_export(mixed x509, string &out, bool tofile[, bool notext = true])
+       Export a cert to file or a var */
+PHP_FUNCTION(openssl_x509_export)
 {
-       EVP_PKEY * key = NULL;
-       X509 * cert = NULL;
-       int free_cert = 0;
-       long cert_res = -1;
-       char * filename = NULL;
-       
-       if (resourceval)
-               *resourceval = -1;
+       X509 * cert;
+       zval * zcert = NULL, *zout=NULL;
+       zend_bool tofile = 0, notext = 1;
+       BIO * bio_out;
+       long certresource;
 
-       if (Z_TYPE_PP(val) == IS_ARRAY) {
-               zval ** zphrase;
-               
-               /* get passphrase */
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rzb|b", &zcert, &zout, &tofile, &notext) == FAILURE)
+               return;
 
-               if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE)       {
-                       zend_error(E_WARNING, "%s(): key array must be of the form array(0 => key, 1 => phrase)", get_active_function_name(TSRMLS_C));
-                       return NULL;
-               }
-               convert_to_string_ex(zphrase);
-               passphrase = Z_STRVAL_PP(zphrase);
+       RETVAL_FALSE;
 
-               /* now set val to be the key param and continue */
-               if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE)   {
-                       zend_error(E_WARNING, "%s(): key array must be of the form array(0 => key, 1 => phrase)", get_active_function_name(TSRMLS_C));
-                       return NULL;
+       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
+       if (cert == NULL)       {
+               zend_error(E_WARNING, "cannot get cert from parameter 1");
+               return;
+       }
+
+       if (tofile)     {
+               /* TODO: export to named file */
+               bio_out = BIO_new_file(Z_STRVAL_P(zout), "w");
+               if (bio_out)    {
+                       if (!notext)
+                               X509_print(bio_out, cert);
+                       PEM_write_bio_X509(bio_out, cert);
+                       
+                       RETVAL_TRUE;
                }
+               else
+                       zend_error(E_WARNING, "error opening file %s", Z_STRVAL_P(zout));
        }
+       else    {
+               /* export to a var */
+               char * bio_mem_ptr;
+               long bio_mem_len;
 
-       if (Z_TYPE_PP(val) == IS_RESOURCE)      {
-               void * what;
-               int type;
+               bio_out = BIO_new(BIO_s_mem());
+               if (!notext)
+                       X509_print(bio_out, cert);
+               PEM_write_bio_X509(bio_out, cert);
 
-               what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key);
-               if (!what)
-                       return NULL;
+               bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
+               ZVAL_STRINGL(zout, bio_mem_ptr, bio_mem_len, 1);
 
-               if (resourceval)
-                       *resourceval = Z_LVAL_PP(val);
+               RETVAL_TRUE;
+       }
+       
+       if (certresource == -1 && cert)
+               X509_free(cert);
+       
+       BIO_free(bio_out);
 
-               if (type == le_x509)    {
-                       /* extract key from cert, depending on public_key param */
-                       cert = (X509*)what;
-                       free_cert = 0;
-               }
-               else if (type == le_key)        {
-                       /* got the key - return it */
-                       return (EVP_PKEY*)what;
-               }
+}
+/* }}} */
 
-               /* other types could be used here - eg: file pointers and read in the data from them */
+/* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key)
+       Checks if a private key corresponds to a cert */
+PHP_FUNCTION(openssl_x509_check_private_key)
+{
+       zval * zcert, *zkey;
+       X509 * cert = NULL;
+       EVP_PKEY * key = NULL;
+       long certresource = -1, keyresource = -1;
 
-               return NULL;
+       RETVAL_FALSE;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &zcert, &zkey) == FAILURE)
+               return;
+
+       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
+       if (cert == NULL)
+               RETURN_FALSE;
+       
+       key = php_openssl_evp_from_zval(&zkey, 0, "", 1, &keyresource TSRMLS_CC);
+       if (key)        {
+               RETVAL_BOOL(X509_check_private_key(cert, key));
        }
-       else    {
-               /* force it to be a string and check if it refers to a file */
-               convert_to_string_ex(val);
 
-               if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", 7) == 0)
-                       filename = Z_STRVAL_PP(val) + 7;
+       if (keyresource == -1 && key)
+               EVP_PKEY_free(key);
+       if (certresource == -1 && cert)
+               X509_free(cert);
+}
+/* }}} */
 
-               /* it's an X509 file/cert of some kind, and we need to extract the data from that */
-               if (public_key) {
-                       cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
-                       free_cert = (cert_res == -1);
-                       /* actual extraction done later */
-               }
-               else    {
-                       /* we want the private key */
-                       if (filename)   {
-                               BIO *in = BIO_new_file(filename, "r");
-                               if (in == NULL)
-                                       return NULL;
-                               key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);
-                               BIO_free(in);
-                       }
-                       else    {
-                               BIO *   b = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
-                               if (b == NULL)
-                                       return NULL;
+/* {{{ proto array openssl_x509_parse(mixed x509[, bool shortnames=true])
+       returns an array of the fields/values of the cert */
+PHP_FUNCTION(openssl_x509_parse)
+{
+       zval * zcert;
+       X509 * cert = NULL;
+       long certresource = -1;
+       int i;
+       zend_bool useshortnames = 1;
+       char * tmpstr;
+       zval * subitem;
 
-                               key = (EVP_PKEY *) PEM_ASN1_read_bio((char *(*)())d2i_PrivateKey,
-                                             PEM_STRING_EVP_PKEY, b,
-                                             NULL, NULL, passphrase);
-                               BIO_free(b);
-                       }
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &zcert, &useshortnames) == FAILURE)
+               return;
+       
+       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
+       if (cert == NULL)
+               RETURN_FALSE;
+
+       array_init(return_value);
+
+       if (cert->name)
+               add_assoc_string(return_value, "name", cert->name, 1);
+/*     add_assoc_bool(return_value, "valid", cert->valid); */
+
+       add_assoc_name_entry(return_value, "subject",           X509_get_subject_name(cert), useshortnames);
+       add_assoc_name_entry(return_value, "issuer",            X509_get_issuer_name(cert), useshortnames);
+       add_assoc_long(return_value, "version",                         X509_get_version(cert));
+       add_assoc_long(return_value, "serialNumber",            ASN1_INTEGER_get(X509_get_serialNumber(cert)));
+
+       add_assoc_asn1_string(return_value, "validFrom",        X509_get_notBefore(cert));
+       add_assoc_asn1_string(return_value, "validTo",          X509_get_notAfter(cert));
+
+       add_assoc_long(return_value, "validFrom_time_t",        asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC));
+       add_assoc_long(return_value, "validTo_time_t",          asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC));
+
+       tmpstr = X509_alias_get0(cert, NULL);
+       if (tmpstr)
+               add_assoc_string(return_value, "alias", tmpstr, 1);
+
+/*
+       add_assoc_long(return_value, "signaturetypeLONG", X509_get_signature_type(cert));
+       add_assoc_string(return_value, "signaturetype", OBJ_nid2sn(X509_get_signature_type(cert)), 1);
+       add_assoc_string(return_value, "signaturetypeLN", OBJ_nid2ln(X509_get_signature_type(cert)), 1);
+*/
+       MAKE_STD_ZVAL(subitem);
+       array_init(subitem);
+
+       /* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
+          in x509v3.h */
+       for (i = 0; i < X509_PURPOSE_get_count(); i++)  {
+               int id, purpset;
+               char * pname;
+               X509_PURPOSE * purp;
+               zval * subsub;
+
+               MAKE_STD_ZVAL(subsub);
+               array_init(subsub);
+
+               purp = X509_PURPOSE_get0(i);
+               id = X509_PURPOSE_get_id(purp);
+
+               purpset = X509_check_purpose(cert, id, 0);
+               add_index_bool(subsub, 0, purpset);
+
+               purpset = X509_check_purpose(cert, id, 1);
+               add_index_bool(subsub, 1, purpset);
+
+               pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
+               add_index_string(subsub, 2, pname, 1);
+
+               /* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
+
+               add_index_zval(subitem, id, subsub);
+       }
+       add_assoc_zval(return_value, "purposes", subitem);
+
+       if (certresource == -1 && cert)
+               X509_free(cert);
+
+}
+/* }}} */
+
+/* {{{ load_all_certs_from_file */
+static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
+{
+   STACK_OF(X509_INFO) *sk=NULL;
+   STACK_OF(X509) *stack=NULL, *ret=NULL;
+   BIO *in=NULL;
+   X509_INFO *xi;
+   TSRMLS_FETCH();
+
+       if(!(stack = sk_X509_new_null())) {
+               zend_error(E_ERROR, "%s(): memory allocation failure", get_active_function_name(TSRMLS_C));
+               goto end;
+       }
+
+       if(!(in=BIO_new_file(certfile, "r"))) {
+               zend_error(E_WARNING, "%s(): error opening the file, %s", get_active_function_name(TSRMLS_C), certfile);
+               goto end;
+       }
+
+       /* This loads from a file, a stack of x509/crl/pkey sets */
+       if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
+               zend_error(E_WARNING, "%s(): error reading the file, %s", get_active_function_name(TSRMLS_C), certfile);
+               goto end;
+       }
+
+       /* scan over it and pull out the certs */
+       while (sk_X509_INFO_num(sk))
+       {
+               xi=sk_X509_INFO_shift(sk);
+               if (xi->x509 != NULL)
+               {
+                       sk_X509_push(stack,xi->x509);
+                       xi->x509=NULL;
                }
+               X509_INFO_free(xi);
        }
+       if(!sk_X509_num(stack)) {
+               zend_error(E_WARNING, "%s(): no certificates in file, %s", get_active_function_name(TSRMLS_C), certfile);
+               sk_X509_free(stack);
+               goto end;
+       }
+       ret=stack;
+end:
+       BIO_free(in);
+       sk_X509_INFO_free(sk);
 
-       if (public_key && cert && key == NULL)  {
-               /* extract public key from X509 cert */
-               key = (EVP_PKEY *) X509_get_pubkey(cert);
+       return ret;
+}
+/* }}} */
+
+/* {{{ check_cert */
+static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
+{
+       int ret=0;
+       X509_STORE_CTX *csc;
+       TSRMLS_FETCH();
+
+       csc = X509_STORE_CTX_new();
+       if (csc == NULL)
+       {
+               zend_error(E_ERROR, "%s(): memory allocation failure", get_active_function_name(TSRMLS_C));
+               return 0;
        }
+       X509_STORE_CTX_init(csc, ctx, x, untrustedchain);
 
-       if (free_cert && cert)
+       if(purpose >= 0)
+               X509_STORE_CTX_set_purpose(csc, purpose);
+
+       ret = X509_verify_cert(csc);
+       X509_STORE_CTX_free(csc);
+
+       return ret;
+}
+/* }}} */
+
+/* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo[, string untrustedfile])
+       check the cert to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */
+PHP_FUNCTION(openssl_x509_checkpurpose)
+{
+       zval * zcert, * zcainfo;
+       X509_STORE * cainfo = NULL;
+       X509 * cert = NULL;
+       long certresource = -1;
+       STACK_OF(X509) * untrustedchain = NULL;
+       long purpose;
+       char * untrusted = NULL;
+       long untrusted_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zla|s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len)
+                       == FAILURE)
+               return;
+
+       RETVAL_LONG(-1);
+
+       if (untrusted)  {
+               untrustedchain = load_all_certs_from_file(untrusted);
+               if (untrustedchain == NULL)
+                       goto clean_exit;
+       }
+
+       cainfo = setup_verify(zcainfo TSRMLS_CC);
+       if (cainfo == NULL)
+               goto clean_exit;
+
+       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
+       if (cert == NULL)
+               goto clean_exit;
+
+       RETVAL_BOOL(check_cert(cainfo, cert, untrustedchain, purpose));
+
+clean_exit:
+       if (certresource == 1 && cert)
                X509_free(cert);
+       if (cainfo)
+               X509_STORE_free(cainfo);
+       if (untrustedchain)
+               sk_X509_pop_free(untrustedchain, X509_free);
+}
+/* }}} */
 
-       if (key && makeresource && resourceval) {
-               *resourceval = zend_list_insert(key, le_key);
+/* {{{ setup_verify
+ * calist is an array containing file and directory names.  create a
+ * certificate store and add those certs to it for use in verification.
+*/
+static X509_STORE * setup_verify(zval * calist TSRMLS_DC)
+{
+       X509_STORE *store;
+       X509_LOOKUP * dir_lookup, * file_lookup;
+       HashPosition pos;
+       int ndirs = 0, nfiles = 0;
+
+       store = X509_STORE_new();
+
+       if (store == NULL)
+               return NULL;
+
+       if (calist && (Z_TYPE_P(calist) == IS_ARRAY))   {
+               zend_hash_internal_pointer_reset_ex(HASH_OF(calist), &pos);
+               for (;; zend_hash_move_forward_ex(HASH_OF(calist), &pos))       {
+                       zval ** item;
+                       struct stat sb;
+
+                       if (zend_hash_get_current_data_ex(HASH_OF(calist), (void**)&item, &pos) == FAILURE)
+                               break;
+
+                       convert_to_string_ex(item);
+
+                       if (VCWD_STAT(Z_STRVAL_PP(item), &sb) == -1)    {
+                               zend_error(E_WARNING, "%s() unable to stat %s", get_active_function_name(TSRMLS_C), Z_STRVAL_PP(item));
+                               continue;
+                       }
+
+                       if ((sb.st_mode & S_IFREG) == S_IFREG)  {
+                               file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+                               if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM))
+                                       zend_error(E_WARNING, "%s() error loading file %s", get_active_function_name(TSRMLS_C), Z_STRVAL_PP(item));
+                               else
+                                       nfiles++;
+                               file_lookup = NULL;
+                       }
+                       else    {
+                               dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+                               if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM))
+                                       zend_error(E_WARNING, "%s() error loading directory %s", get_active_function_name(TSRMLS_C), Z_STRVAL_PP(item));
+                               else
+                                       ndirs++;
+                               dir_lookup = NULL;
+                       }
+               }
+       }
+       if (nfiles == 0)        {
+               file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+               if (file_lookup)
+                       X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT);
+       }
+       if (ndirs == 0) {
+               dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+               if (dir_lookup)
+                       X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT);
        }
-       return key;
+       return store;
 }
 /* }}} */
 
-/* {{{ proto bool openssl_private_encrypt(string data, string crypted, mixed key [, int padding])
-   Encrypt data with private key */
-PHP_FUNCTION(openssl_private_encrypt)
+/* {{{ proto resource openssl_x509_read(mixed cert)
+   Read X.509 certificate */
+PHP_FUNCTION(openssl_x509_read)
 {
-       zval *key, *crypted;
-       EVP_PKEY *pkey;
-       int cryptedlen;
-       unsigned char *cryptedbuf = NULL;
-       int successful = 0;
-       long keyresource = -1;
-       char * data;
-       long data_len, padding = RSA_PKCS1_PADDING;
+       zval *cert;
+       X509 *x509;
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &cert) == FAILURE)
                return;
 
-       RETVAL_FALSE;
-
-       pkey = php_openssl_evp_from_zval(&key, 0, "", 0, &keyresource TSRMLS_CC);
+       Z_TYPE_P(return_value) = IS_RESOURCE;
+       x509 = php_openssl_x509_from_zval(&cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
 
-       if (pkey == NULL)       {
-               zend_error(E_WARNING, "%s(): key param is not a valid private key",
-                               get_active_function_name(TSRMLS_C));
+       if (x509 == NULL) {
+               zend_error(E_WARNING, "%s() supplied parameter cannot be coerced into an X509 certificate!", get_active_function_name(TSRMLS_C));
                RETURN_FALSE;
        }
-       
-       cryptedlen = EVP_PKEY_size(pkey);
-       cryptedbuf = emalloc(cryptedlen + 1);
-
-       switch (pkey->type) {
-               case EVP_PKEY_RSA:
-               case EVP_PKEY_RSA2:
-                       successful =  (RSA_private_encrypt(data_len, 
-                                               data, 
-                                               cryptedbuf, 
-                                               pkey->pkey.rsa, 
-                                               padding) == cryptedlen);
-                       break;
-               default:
-                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
-       }
-
-       if (successful) {
-               zval_dtor(crypted);
-               cryptedbuf[cryptedlen] = '\0';
-               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
-               cryptedbuf = NULL;
-               RETVAL_TRUE;
-       }
-       if (cryptedbuf)
-               efree(cryptedbuf);
-       if (keyresource == -1)
-               EVP_PKEY_free(pkey);
 }
 /* }}} */
 
-/* {{{ proto bool openssl_private_decrypt(string data, string crypted, mixed key [, int padding])
-   Decrypt data with private key */
-PHP_FUNCTION(openssl_private_decrypt)
+/* {{{ proto void openssl_free_x509(resource x509)
+   Free X.509 certificate */
+PHP_FUNCTION(openssl_x509_free)
 {
-       zval *key, *crypted;
-       EVP_PKEY *pkey;
-       int cryptedlen;
-       unsigned char *cryptedbuf;
-       unsigned char *crypttemp;
-       int successful = 0;
-       long padding = RSA_PKCS1_PADDING;
-       long keyresource = -1;
-       char * data;
-       long data_len;
-       
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
-               return;
-
-       RETVAL_FALSE;
-       
-       pkey = php_openssl_evp_from_zval(&key, 0, "", 0, &keyresource TSRMLS_CC);
-       if (pkey == NULL)       {
-               zend_error(E_WARNING, "%s(): key parameter is not a valid private key", get_active_function_name(TSRMLS_C));
-               RETURN_FALSE;
-       }
-       
-       cryptedlen = EVP_PKEY_size(pkey);
-       crypttemp = emalloc(cryptedlen + 1);
-
-       switch (pkey->type) {
-               case EVP_PKEY_RSA:
-               case EVP_PKEY_RSA2:
-                       cryptedlen = RSA_private_decrypt(data_len, 
-                                       data, 
-                                       crypttemp, 
-                                       pkey->pkey.rsa, 
-                                       padding);
-                       if (cryptedlen != -1) {
-                               cryptedbuf = emalloc(cryptedlen + 1);
-                               memcpy(cryptedbuf, crypttemp, cryptedlen);
-                               successful = 1;
-                       }
-                       break;
-               default:
-                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
-       }
-
-       efree(crypttemp);
+       zval *x509;
+       X509 *cert;
 
-       if (successful) {
-               zval_dtor(crypted);
-               cryptedbuf[cryptedlen] = '\0';
-               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
-               cryptedbuf = NULL;
-               RETVAL_TRUE;
-       }
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE)
+               return;
 
-       if (keyresource == -1)
-               EVP_PKEY_free(pkey);
-       if (cryptedbuf)
-               efree(cryptedbuf);
+       ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509);
+       zend_list_delete(Z_LVAL_P(x509));
 }
 /* }}} */
 
-/* {{{ proto bool openssl_public_encrypt(string data, string crypted, mixed key [, int padding])
-   Encrypt data with public key */
-PHP_FUNCTION(openssl_public_encrypt)
+/* }}} */
+
+/* {{{ x509 CSR functions */
+
+/* {{{ php_openssl_make_REQ */
+static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs)
 {
-       zval *key, *crypted;
-       EVP_PKEY *pkey;
-       int cryptedlen;
-       unsigned char *cryptedbuf;
-       int successful = 0;
-       long keyresource = -1;
-       long padding = RSA_PKCS1_PADDING;
-       char * data;
-       long data_len;
-       
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
-               return;
+       STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
+       char * str, *dn_sect, *attr_sect;
+
+       dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
+       if (dn_sect == NULL)
+               return FAILURE;
+       dn_sk = CONF_get_section(req->req_config, dn_sect);
+       if (dn_sk == NULL)
+               return FAILURE;
+       attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes");
+       if (attr_sect == NULL)
+               attr_sk = NULL;
+       else    {
+               attr_sk = CONF_get_section(req->req_config, attr_sect);
+               if (attr_sk == NULL)    
+                       return FAILURE;
+       }
+       /* setup the version number: version 1 */
+       if (X509_REQ_set_version(csr, 0L))      {
+               int i, nid;
+               char * type;
+               CONF_VALUE * v;
+               X509_NAME * subj;
+               HashPosition hpos;
+               zval ** item;
+               
+               subj = X509_REQ_get_subject_name(csr);
+               /* apply values from the dn hash */
+               zend_hash_internal_pointer_reset_ex(HASH_OF(dn), &hpos);
+               while(zend_hash_get_current_data_ex(HASH_OF(dn), (void**)&item, &hpos) == SUCCESS)      {
+                       char * strindex; int strindexlen;
+                       long intindex;
+                       
+                       zend_hash_get_current_key_ex(HASH_OF(dn), &strindex, &strindexlen, &intindex, 0, &hpos);
 
-       RETVAL_FALSE;
-       
-       pkey = php_openssl_evp_from_zval(&key, 1, NULL, 0, &keyresource TSRMLS_CC);
-       if (pkey == NULL)       {
-               zend_error(E_WARNING, "%s(): key parameter is not a valid public key", get_active_function_name(TSRMLS_C));
-               RETURN_FALSE;
-       }
+                       convert_to_string_ex(item);
 
-       cryptedlen = EVP_PKEY_size(pkey);
-       cryptedbuf = emalloc(cryptedlen + 1);
+                       if (strindex)   {
+                               int nid;
+
+                               nid = OBJ_txt2nid(strindex);
+                               if (nid != NID_undef)   {
+                                       if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC,
+                                                               (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0))
+                                       {
+                                               zend_error(E_WARNING, "dn: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item));
+                                               return FAILURE;
+                                       }
+                               }
+                               else    zend_error(E_WARNING, "dn: %s is not a recognized name", strindex);
+                       }
+                       zend_hash_move_forward_ex(HASH_OF(dn), &hpos);
+               }
 
-       switch (pkey->type) {
-               case EVP_PKEY_RSA:
-               case EVP_PKEY_RSA2:
-                       successful = (RSA_public_encrypt(data_len, 
-                                               data, 
-                                               cryptedbuf, 
-                                               pkey->pkey.rsa, 
-                                               padding) == cryptedlen);
-                       break;
-               default:
-                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
+               /* Finally apply defaults from config file */
+               for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++)   {
+                       int len;
+                       char buffer[200];
+                       
+                       v = sk_CONF_VALUE_value(dn_sk, i);
+                       type = v->name;
+                       
+                       len = strlen(type);
+                       if (len < sizeof("_default"))
+                               continue;
+                       len -= sizeof("_default") - 1;
+                       if (strcmp("_default", type + len) != 0)        {
+                               continue;
+                       }
+                       
+                       memcpy(buffer, type, len);
+                       buffer[len] = '\0';
+                       type = buffer;
+               
+                       /* Skip past any leading X. X: X, etc to allow for multiple
+                        * instances */
+                       for (str = type; *str; str++)   {
+                               if (*str == ':' || *str == ',' || *str == '.')  {
+                                       str++;
+                                       if (*str)
+                                               type = str;
+                                       break;
+                               }
+                       }
+                       /* if it is already set, skip this */
+                       nid = OBJ_txt2nid(type);
+                       if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0)
+                               continue;
+                       zend_printf("%s not already set; using default of %s\n", type, v->value);
+                       if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_ASC, (unsigned char*)v->value, -1, -1, 0))
+                       {
+                               zend_error(E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
+                               return FAILURE;
+                       }
+                       if (!X509_NAME_entry_count(subj))       {
+                               zend_error(E_WARNING, "no objects specified in config file");
+                               return FAILURE;
+                       }
+               }
+               if (attribs)    {
+                       zend_hash_internal_pointer_reset_ex(HASH_OF(attribs), &hpos);
+                       while(zend_hash_get_current_data_ex(HASH_OF(attribs), (void**)&item, &hpos) == SUCCESS) {
+                               char * strindex; int strindexlen;
+                               long intindex;
+
+                               zend_hash_get_current_key_ex(HASH_OF(attribs), &strindex, &strindexlen, &intindex, 0, &hpos);
+                               convert_to_string_ex(item);
+
+                               if (strindex)   {
+                                       int nid;
+
+                                       nid = OBJ_txt2nid(strindex);
+                                       if (nid != NID_undef)   {
+                                               if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC,
+                                                                       (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0))
+                                               {
+                                                       zend_error(E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item));
+                                                       return FAILURE;
+                                               }
+                                       }
+                                       else    zend_error(E_WARNING, "dn: %s is not a recognized name", strindex);
+                               }
+                               zend_hash_move_forward_ex(HASH_OF(attribs), &hpos);
+                       }
+                       for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++)        {
+                               v = sk_CONF_VALUE_value(attr_sk, i);
+                               /* if it is already set, skip this */
+                               nid = OBJ_txt2nid(v->name);
+                               if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0)
+                                       continue;
+
+                               if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_ASC,
+                                                       (unsigned char*)v->value, -1))
+                               {
+                                       zend_error(E_WARNING, "add1_attr_by_txt %s -> %s (failed)", v->name, v->value);
+                                       return FAILURE;
+                               }
+                       }
 
+               }
        }
 
-       if (successful) {
-               zval_dtor(crypted);
-               cryptedbuf[cryptedlen] = '\0';
-               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
-               cryptedbuf = NULL;
-               RETVAL_TRUE;
-       }
-       if (keyresource == -1)
-               EVP_PKEY_free(pkey);
-       if (cryptedbuf)
-               efree(cryptedbuf);
+       X509_REQ_set_pubkey(csr, req->priv_key);
+       return SUCCESS;
 }
 /* }}} */
 
-/* {{{ proto bool openssl_public_decrypt(string data, string crypted, resource key [, int padding])
-   Decrypt data with public key */
-PHP_FUNCTION(openssl_public_decrypt)
+/* {{{ php_openssl_csr_from_zval */
+static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC)
 {
-       zval *key, *crypted;
-       EVP_PKEY *pkey;
-       int cryptedlen;
-       unsigned char *cryptedbuf;
-       unsigned char *crypttemp;
-       int successful = 0;
-       long keyresource = -1;
-       long padding = RSA_PKCS1_PADDING;
-       char * data;
-       long data_len;
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
-               return;
-
-       RETVAL_FALSE;
+       X509_REQ * csr = NULL;
+       char * filename;
+       BIO * in;
        
-       pkey = php_openssl_evp_from_zval(&key, 1, NULL, 0, &keyresource TSRMLS_CC);
-       if (pkey == NULL)       {
-               zend_error(E_WARNING, "%s(): key parameter is not a valid public key", get_active_function_name(TSRMLS_C));
-               RETURN_FALSE;
-       }
+       if (resourceval)
+               *resourceval = -1;
 
-       cryptedlen = EVP_PKEY_size(pkey);
-       crypttemp = emalloc(cryptedlen + 1);
+       if (Z_TYPE_PP(val) == IS_RESOURCE)      {
+               void * what;
+               int type;
 
-       switch (pkey->type) {
-               case EVP_PKEY_RSA:
-               case EVP_PKEY_RSA2:
-                       cryptedlen = RSA_public_decrypt(data_len, 
-                                       data, 
-                                       crypttemp, 
-                                       pkey->pkey.rsa, 
-                                       padding);
-                       if (cryptedlen != -1) {
-                               cryptedbuf = emalloc(cryptedlen + 1);
-                               memcpy(cryptedbuf, crypttemp, cryptedlen);
-                               successful = 1;
-                       }
-                       break;
-                       
-               default:
-                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
-                
+               what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509 CSR", &type, 1, le_csr);
+               if (what)       {
+                       if (resourceval)
+                               *resourceval = Z_LVAL_PP(val);
+                       return (X509_REQ*)what;
+               }
+               return NULL;
        }
+       convert_to_string_ex(val);
 
-       efree(crypttemp);
+       if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", 7) == 0)
+               filename = Z_STRVAL_PP(val) + 7;
 
-       if (successful) {
-               zval_dtor(crypted);
-               cryptedbuf[cryptedlen] = '\0';
-               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
-               cryptedbuf = NULL;
-               RETVAL_TRUE;
-       }
+       if (filename)
+               in = BIO_new_file(filename, "r");
+       else
+               in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
+       
+       csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
+       BIO_free(in);
 
-       if (cryptedbuf)
-               efree(cryptedbuf);
-       if (keyresource == -1)
-               EVP_PKEY_free(pkey);
+       return csr;
 }
 /* }}} */
 
-/* {{{ proto int openssl_get_privatekey(string key [, string passphrase])
-   Get private key */
-PHP_FUNCTION(openssl_get_privatekey)
+/* {{{ proto openssl_csr_export(resource csr, string &out, bool tofile, [bool notext=true])
+       Exports a CSR to file or a var */
+PHP_FUNCTION(openssl_csr_export)
 {
-       EVP_PKEY *pkey;
-       zval * key;
-       char * passphrase = "";
-       int passphrase_len;
+       X509_REQ * csr;
+       zval * zcsr = NULL, *zout=NULL;
+       zend_bool tofile = 0, notext = 1;
+       BIO * bio_out;
+       long csr_resource;
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &key, &passphrase, &passphrase_len) == FAILURE)
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rzb|b", &zcsr, &zout, &tofile, &notext) == FAILURE)
                return;
 
-       return_value->type = IS_RESOURCE;
-       pkey = php_openssl_evp_from_zval(&key, 0, passphrase, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
+       RETVAL_FALSE;
 
-       if (pkey == NULL) {
-               zend_error(E_WARNING, "%s(): unable to coerce arg to a private key", get_active_function_name(TSRMLS_C));
-               RETURN_FALSE;
+       csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
+       if (csr == NULL)        {
+               zend_error(E_WARNING, "cannot get CSR from parameter 1");
+               return;
        }
-}
-/* }}} */
 
-/* {{{ openssl -> PHP "bridging" */
+       if (tofile)     {
+               /* TODO: export to named file */
+               bio_out = BIO_new_file(Z_STRVAL_P(zout), "w");
+               if (bio_out)    {
+                       if (!notext)
+                               X509_REQ_print(bio_out, csr);
+                       PEM_write_bio_X509_REQ(bio_out, csr);
+                       RETVAL_TRUE;
+               }
+               else
+                       zend_error(E_WARNING, "error opening file %s", Z_STRVAL_P(zout));
+       }
+       else    {
+               /* export to a var */
+               char * bio_mem_ptr;
+               long bio_mem_len;
 
-static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname)
-{
-       zval * subitem;
-       int i;
-       char * sn, * ln;
-       int nid;
-       X509_NAME_ENTRY * ne;
-       ASN1_STRING * str;
-       ASN1_OBJECT * obj;
+               bio_out = BIO_new(BIO_s_mem());
+               if (!notext)
+                       X509_REQ_print(bio_out, csr);
+               PEM_write_bio_X509_REQ(bio_out, csr);
 
-       MAKE_STD_ZVAL(subitem);
-       array_init(subitem);
+               bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
+               ZVAL_STRINGL(zout, bio_mem_ptr, bio_mem_len, 1);
 
-       for (i = 0; i < X509_NAME_entry_count(name); i++)       {
-               ne      = X509_NAME_get_entry(name, i);
-               obj = X509_NAME_ENTRY_get_object(ne);
-               str = X509_NAME_ENTRY_get_data(ne);
-               nid = OBJ_obj2nid(obj);
-               if (shortname)  {
-                       sn = (char*)OBJ_nid2sn(nid);
-                       add_assoc_stringl(subitem, sn, str->data, str->length, 1);
-               }
-               else    {
-                       ln = (char*)OBJ_nid2ln(nid);
-                       add_assoc_stringl(subitem, ln, str->data, str->length, 1);
-               }
+               RETVAL_TRUE;
        }
-       zend_hash_update(HASH_OF(val), key, strlen(key) + 1, (void *)&subitem, sizeof(subitem), NULL);
-}
-
-static void add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str)
-{
-       add_assoc_stringl(val, key, str->data, str->length, 1);
+       
+       if (csr_resource == -1 && csr)
+               X509_REQ_free(csr);
+       
+       BIO_free(bio_out);
 }
+/* }}} */
 
-static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC)
+/* {{{ proto openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days)
+       Sign a cert with another cert */
+PHP_FUNCTION(openssl_csr_sign)
 {
-/*
-       This is how the time string is formatted:
+       zval * zcert = NULL, *zcsr, *zpkey, *args = NULL;
+       long num_days;
+       X509 * cert = NULL, *new_cert = NULL;
+       X509_REQ * csr;
+       EVP_PKEY * key, *priv_key;
+       long csr_resource, certresource, keyresource;
+       int i;
+       struct php_x509_request req;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz!zl|a!", &zcsr, &zcert, &zpkey, &num_days, &args) == FAILURE)
+               return;
 
-   sprintf(p,"%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
-      ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
-*/
+       RETVAL_FALSE;
+       PHP_SSL_REQ_INIT(&req);
+       
+       csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
+       if (csr == NULL)        {
+               zend_error(E_WARNING, "cannot get CSR from parameter 1");
+               return;
+       }
+       if (zcert)      {
+               cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
+               if (cert == NULL)       {
+                       zend_error(E_WARNING, "cannot get cert from parameter 2");
+                       goto cleanup;
+               }
+       }
+       priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC);
+       if (priv_key == NULL)   {
+               zend_error(E_WARNING, "cannot get private key from parameter 3");
+               goto cleanup;
+       }
+       if (cert && !X509_check_private_key(cert, key)) {
+               zend_error(E_WARNING, "private key does not correspond to signing cert");
+               goto cleanup;
+       }
+       
+       if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE)
+               goto cleanup;
 
-       time_t ret;
-       struct tm thetime;
-       char * strbuf;
-       char * thestr;
-       long gmadjust = 0;
+       /* Check that the request matches the signature */
+       key = X509_REQ_get_pubkey(csr);
+       if (key == NULL)        {
+               zend_error(E_WARNING, "error unpacking public key");
+               goto cleanup;
+       }
+       i = X509_REQ_verify(csr, key);
 
-       if (timestr->length < 13)       {
-               zend_error(E_WARNING, "%s(): extension author too lazy to parse %s correctly", get_active_function_name(TSRMLS_C), timestr->data);
-               return (time_t)-1;
+       if (i < 0)      {
+               zend_error(E_WARNING, "Signature verification problems");
+               goto cleanup;
+       }
+       else if (i==0)  {
+               zend_error(E_WARNING, "Signature did not match the certificate request");
+               goto cleanup;
        }
+       
+       /* Now we can get on with it */
+       
+       new_cert = X509_new();
+       if (new_cert == NULL)   {
+               zend_error(E_WARNING, "No memory");
+               goto cleanup;
+       }
+       /* Version 3 cert */
+       if (!X509_set_version(new_cert, 3))
+               goto cleanup;
 
-       strbuf = estrdup(timestr->data);
+       /* TODO: Allow specifying */
+       ASN1_INTEGER_set(X509_get_serialNumber(new_cert), 0L);
+       
+       X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
 
+       if (cert == NULL)
+               cert = new_cert;
 
-       memset(&thetime, 0, sizeof(thetime));
+       if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert)))
+               goto cleanup;
 
-       /* we work backwards so that we can use atoi more easily */
+       X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
+       X509_gmtime_adj(X509_get_notAfter(new_cert), (long)60*60*24*num_days);
+       i = X509_set_pubkey(new_cert, key);
+       if (!i)
+               goto cleanup;
 
-       thestr = strbuf + timestr->length - 3;
+       if (req.request_extensions_section)     {
+               X509V3_CTX ctx;
+               
+               X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
+               X509V3_set_conf_lhash(&ctx, req.req_config);
+               if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.request_extensions_section, new_cert))
+                       goto cleanup;
+       }
 
-       thetime.tm_sec = atoi(thestr);
-       *thestr = '\0';
-       thestr -= 2;
-       thetime.tm_min = atoi(thestr);
-       *thestr = '\0';
-       thestr -= 2;
-       thetime.tm_hour = atoi(thestr);
-       *thestr = '\0';
-       thestr -= 2;
-       thetime.tm_mday = atoi(thestr);
-       *thestr = '\0';
-       thestr -= 2;
-       thetime.tm_mon = atoi(thestr)-1;
-       *thestr = '\0';
-       thestr -= 2;
-       thetime.tm_year = atoi(thestr);
+       /* Now sign it */
+       if (!X509_sign(new_cert, priv_key, req.digest)) {
+               zend_error(E_WARNING, "failed to sign it");
+               goto cleanup;
+       }
+       
+       /* Succeeded; lets return the cert */
+       RETVAL_RESOURCE(zend_list_insert(new_cert, le_x509));
+       new_cert = NULL;
+       
+cleanup:
 
-       if (thetime.tm_year < 68)
-               thetime.tm_year += 100;
+       if (cert == new_cert)
+               cert = NULL;
+       
+       PHP_SSL_REQ_DISPOSE(&req);
 
-       thetime.tm_isdst = -1;
-       ret = mktime(&thetime);
+       if (keyresource == -1 && priv_key)
+               EVP_PKEY_free(priv_key);
+       if (key)
+               EVP_PKEY_free(key);
+       if (csr_resource == -1 && csr)
+               X509_REQ_free(csr);
+       if (certresource == -1 && cert)
+               X509_free(cert);
+       if (new_cert)
+               X509_free(new_cert);
+}
+/* }}} */
 
-#if HAVE_TM_GMTOFF
-       gmadjust = thetime.tm_gmtoff;
-#else
-       /*
-       ** If correcting for daylight savings time, we set the adjustment to
-       ** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and
-       ** set the adjustment to the main timezone + 3600 seconds.
-       */
-       gmadjust = -(thetime.tm_isdst ? timezone - 3600 : timezone + 3600);
-#endif
-       ret += gmadjust;
+/* {{{ proto bool openssl_csr_new(array dn, resource &privkey, [array extraattribs, array configargs])
+   Generate a privkey + CSR */
+PHP_FUNCTION(openssl_csr_new)
+{
+       struct php_x509_request req;
+       zval * args = NULL, * dn, *attribs = NULL;
+       zval * out_pkey;
+       X509_REQ * csr = NULL;
+       int we_made_the_key = 1;
+       long key_resource;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE)
+               return;
 
-       efree(strbuf);
+       RETVAL_FALSE;
+       
+       PHP_SSL_REQ_INIT(&req);
 
-       return ret;
+       if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS)
+       {
+               /* Generate or use a private key */
+               if (Z_TYPE_P(out_pkey) != IS_NULL)      {
+                       req.priv_key = php_openssl_evp_from_zval(&out_pkey, 0, NULL, 0, &key_resource TSRMLS_CC);
+                       if (req.priv_key != NULL)
+                               we_made_the_key = 0;
+               }
+               if (req.priv_key == NULL)       
+                       php_openssl_generate_private_key(&req TSRMLS_CC);
+               if (req.priv_key == NULL)       
+                       zend_error(E_WARNING, "Unable to generate a private key");
+               else    {
+                       csr = X509_REQ_new();
+                       if (csr)        {
+                               if (php_openssl_make_REQ(&req, csr, dn, attribs) == SUCCESS)    {
+                                       X509V3_CTX ext_ctx;
+
+                                       X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
+                                       X509V3_set_conf_lhash(&ext_ctx, req.req_config);
+
+                                       /* Add extensions */
+                                       if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
+                                                               &ext_ctx, req.request_extensions_section, csr))
+                                       {
+                                               zend_error(E_WARNING, "Error loading extension section %s", req.request_extensions_section);
+                                       }
+                                       else    {
+                                               RETVAL_TRUE;
+                                               
+                                               if (X509_REQ_sign(csr, req.priv_key, req.digest))       {
+
+                                                       RETVAL_RESOURCE(zend_list_insert(csr, le_csr));
+                                                       csr = NULL;                     
+                                               }
+                                               else
+                                                       zend_error(E_WARNING, "Error signing request");
+
+                                               if (we_made_the_key)    {
+                                                       /* and a resource for the private key */
+                                                       ZVAL_RESOURCE(out_pkey, zend_list_insert(req.priv_key, le_key));
+                                               }
+                                               else if (key_resource != -1)    
+                                                       req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
+                                       }
+                               }
+                       }
+               }
+       }
+       if (csr)
+               X509_REQ_free(csr);
+       PHP_SSL_REQ_DISPOSE(&req);
 }
 /* }}} */
 
-/* {{{ proto array openssl_x509_parse(mixed x509[, bool shortnames=true])
-       returns an array of the fields/values of the cert */
-PHP_FUNCTION(openssl_x509_parse)
+/* }}} */
+
+/* {{{ EVP Public/Private key functions */
+
+/* {{{ php_openssl_evp_from_zval
+   Given a zval, coerce it into a EVP_PKEY object.
+       It can be:
+               1. private key resource from openssl_get_privatekey()
+               2. X509 resource -> public key will be extracted from it
+               3. if it starts with file:// interpreted as path to key file
+               4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
+               5. an array(0 => [items 2..4], 1 => passphrase)
+       NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
+       empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
+       the Apache error log!
+*/
+static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC)
 {
-       zval * zcert;
+       EVP_PKEY * key = NULL;
        X509 * cert = NULL;
-       long certresource = -1;
-       int i;
-       zend_bool useshortnames = 1;
-       char * tmpstr;
-       zval * subitem;
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &zcert, &useshortnames))
-               return;
+       int free_cert = 0;
+       long cert_res = -1;
+       char * filename = NULL;
        
-       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
-       if (cert == NULL)
-               RETURN_FALSE;
-
-       array_init(return_value);
-
-       if (cert->name)
-               add_assoc_string(return_value, "name", cert->name, 1);
-/*     add_assoc_bool(return_value, "valid", cert->valid); */
+       if (resourceval)
+               *resourceval = -1;
 
-       add_assoc_name_entry(return_value, "subject",           X509_get_subject_name(cert), useshortnames);
-       add_assoc_name_entry(return_value, "issuer",            X509_get_issuer_name(cert), useshortnames);
-       add_assoc_long(return_value, "version",                         X509_get_version(cert));
-       add_assoc_long(return_value, "serialNumber",            ASN1_INTEGER_get(X509_get_serialNumber(cert)));
+       if (Z_TYPE_PP(val) == IS_ARRAY) {
+               zval ** zphrase;
+               
+               /* get passphrase */
 
-       add_assoc_asn1_string(return_value, "validFrom",        X509_get_notBefore(cert));
-       add_assoc_asn1_string(return_value, "validTo",          X509_get_notAfter(cert));
+               if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE)       {
+                       zend_error(E_WARNING, "%s(): key array must be of the form array(0 => key, 1 => phrase)", get_active_function_name(TSRMLS_C));
+                       return NULL;
+               }
+               convert_to_string_ex(zphrase);
+               passphrase = Z_STRVAL_PP(zphrase);
 
-       add_assoc_long(return_value, "validFrom_time_t",        asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC));
-       add_assoc_long(return_value, "validTo_time_t",          asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC));
+               /* now set val to be the key param and continue */
+               if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE)   {
+                       zend_error(E_WARNING, "%s(): key array must be of the form array(0 => key, 1 => phrase)", get_active_function_name(TSRMLS_C));
+                       return NULL;
+               }
+       }
 
-       tmpstr = X509_alias_get0(cert, NULL);
-       if (tmpstr)
-               add_assoc_string(return_value, "alias", tmpstr, 1);
+       if (Z_TYPE_PP(val) == IS_RESOURCE)      {
+               void * what;
+               int type;
 
-/*
-       add_assoc_long(return_value, "signaturetypeLONG", X509_get_signature_type(cert));
-       add_assoc_string(return_value, "signaturetype", OBJ_nid2sn(X509_get_signature_type(cert)), 1);
-       add_assoc_string(return_value, "signaturetypeLN", OBJ_nid2ln(X509_get_signature_type(cert)), 1);
-*/
-       MAKE_STD_ZVAL(subitem);
-       array_init(subitem);
+               what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key);
+               if (!what)
+                       return NULL;
 
-       /* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
-          in x509v3.h */
-       for (i = 0; i < X509_PURPOSE_get_count(); i++)  {
-               int id, purpset;
-               char * pname;
-               X509_PURPOSE * purp;
-               zval * subsub;
+               if (resourceval)
+                       *resourceval = Z_LVAL_PP(val);
 
-               MAKE_STD_ZVAL(subsub);
-               array_init(subsub);
+               if (type == le_x509)    {
+                       /* extract key from cert, depending on public_key param */
+                       cert = (X509*)what;
+                       free_cert = 0;
+               }
+               else if (type == le_key)        {
+                       /* got the key - return it */
+                       return (EVP_PKEY*)what;
+               }
 
-               purp = X509_PURPOSE_get0(i);
-               id = X509_PURPOSE_get_id(purp);
+               /* other types could be used here - eg: file pointers and read in the data from them */
 
-               purpset = X509_check_purpose(cert, id, 0);
-               add_index_bool(subsub, 0, purpset);
+               return NULL;
+       }
+       else    {
+               /* force it to be a string and check if it refers to a file */
+               convert_to_string_ex(val);
 
-               purpset = X509_check_purpose(cert, id, 1);
-               add_index_bool(subsub, 1, purpset);
+               if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", 7) == 0)
+                       filename = Z_STRVAL_PP(val) + 7;
 
-               pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
-               add_index_string(subsub, 2, pname, 1);
+               /* it's an X509 file/cert of some kind, and we need to extract the data from that */
+               if (public_key) {
+                       cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
+                       free_cert = (cert_res == -1);
+                       /* actual extraction done later */
+               }
+               else    {
+                       /* we want the private key */
+                       if (filename)   {
+                               BIO *in = BIO_new_file(filename, "r");
+                               if (in == NULL)
+                                       return NULL;
+                               key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);
+                               BIO_free(in);
+                       }
+                       else    {
+                               BIO *   b = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
+                               if (b == NULL)
+                                       return NULL;
 
-               /* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
+                               key = (EVP_PKEY *) PEM_ASN1_read_bio((char *(*)())d2i_PrivateKey,
+                                             PEM_STRING_EVP_PKEY, b,
+                                             NULL, NULL, passphrase);
+                               BIO_free(b);
+                       }
+               }
+       }
 
-               add_index_zval(subitem, id, subsub);
+       if (public_key && cert && key == NULL)  {
+               /* extract public key from X509 cert */
+               key = (EVP_PKEY *) X509_get_pubkey(cert);
        }
-       add_assoc_zval(return_value, "purposes", subitem);
 
-       if (certresource == -1 && cert)
+       if (free_cert && cert)
                X509_free(cert);
 
+       if (key && makeresource && resourceval) {
+               *resourceval = zend_list_insert(key, le_key);
+       }
+       return key;
 }
 /* }}} */
 
-/* {{{ load_all_certs_from_file */
-static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
+/* {{{ php_openssl_generate_private_key */
+static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC)
 {
-   STACK_OF(X509_INFO) *sk=NULL;
-   STACK_OF(X509) *stack=NULL, *ret=NULL;
-   BIO *in=NULL;
-   X509_INFO *xi;
-   TSRMLS_FETCH();
-
-       if(!(stack = sk_X509_new_null())) {
-               zend_error(E_ERROR, "%s(): memory allocation failure", get_active_function_name(TSRMLS_C));
-               goto end;
-       }
-
-       if(!(in=BIO_new_file(certfile, "r"))) {
-               zend_error(E_WARNING, "%s(): error opening the file, %s", get_active_function_name(TSRMLS_C), certfile);
-               goto end;
-       }
-
-       /* This loads from a file, a stack of x509/crl/pkey sets */
-       if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
-               zend_error(E_WARNING, "%s(): error reading the file, %s", get_active_function_name(TSRMLS_C), certfile);
-               goto end;
+       char * randfile = NULL;
+       int egdsocket, seeded;
+       EVP_PKEY * return_val = NULL;
+       
+       if (req->priv_key_bits < MIN_KEY_LENGTH)        {
+               zend_error(E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d",
+                               MIN_KEY_LENGTH, req->priv_key_bits);
+               return NULL;
        }
 
-       /* scan over it and pull out the certs */
-       while (sk_X509_INFO_num(sk))
-       {
-               xi=sk_X509_INFO_shift(sk);
-               if (xi->x509 != NULL)
-               {
-                       sk_X509_push(stack,xi->x509);
-                       xi->x509=NULL;
+       randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE");
+       php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
+       
+       if ((req->priv_key = EVP_PKEY_new()) != NULL)   {
+               switch(req->priv_key_type)      {
+                       case OPENSSL_KEYTYPE_RSA:
+                               if (EVP_PKEY_assign_RSA(req->priv_key, RSA_generate_key(req->priv_key_bits, 0x10001, NULL, NULL)))
+                                       return_val = req->priv_key;
+                               break;
+                       default:
+                               zend_error(E_WARNING, "Unsupported private key type");
                }
-               X509_INFO_free(xi);
-       }
-       if(!sk_X509_num(stack)) {
-               zend_error(E_WARNING, "%s(): no certificates in file, %s", get_active_function_name(TSRMLS_C), certfile);
-               sk_X509_free(stack);
-               goto end;
        }
-       ret=stack;
-end:
-       BIO_free(in);
-       sk_X509_INFO_free(sk);
 
-       return ret;
+       php_openssl_write_rand_file(randfile, egdsocket, seeded);
+       
+       if (return_val == NULL) {
+               EVP_PKEY_free(req->priv_key);
+               req->priv_key = NULL;
+               return NULL;
+       }
+       
+       return return_val;
 }
 /* }}} */
 
-/* {{{ check_cert */
-static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
+/* {{{ proto resource openssl_pkey_new([array configargs])
+       Generate a new private key */
+PHP_FUNCTION(openssl_pkey_new)
 {
-       int ret=0;
-       X509_STORE_CTX *csc;
-       TSRMLS_FETCH();
+       struct php_x509_request req;
+       zval * args = NULL;
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &args) == FAILURE)
+               return;
 
-       csc = X509_STORE_CTX_new();
-       if (csc == NULL)
+       RETVAL_FALSE;
+       
+       PHP_SSL_REQ_INIT(&req);
+
+       if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS)
        {
-               zend_error(E_ERROR, "%s(): memory allocation failure", get_active_function_name(TSRMLS_C));
-               return 0;
+               if (php_openssl_generate_private_key(&req TSRMLS_C))    {
+                       /* pass back a key resource */
+                       RETVAL_RESOURCE(zend_list_insert(req.priv_key, le_key));
+                       /* make sure the cleanup code doesn't zap it! */
+                       req.priv_key = NULL;
+               }
        }
-       X509_STORE_CTX_init(csc, ctx, x, untrustedchain);
-
-       if(purpose >= 0)
-               X509_STORE_CTX_set_purpose(csc, purpose);
-
-       ret = X509_verify_cert(csc);
-       X509_STORE_CTX_free(csc);
-
-       return ret;
+       PHP_SSL_REQ_DISPOSE(&req);
 }
 /* }}} */
 
-/* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo[, string untrustedfile])
-       check the cert to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */
-PHP_FUNCTION(openssl_x509_checkpurpose)
+/* {{{ proto bool openssl_pkey_export(mixed key, &mixed out, bool tofile[, string passphrase, array config_args)
+   Get an exportable representation of a key into a string or file */
+PHP_FUNCTION(openssl_pkey_export)
 {
-       zval * zcert, * zcainfo;
-       X509_STORE * cainfo = NULL;
-       X509 * cert = NULL;
-       long certresource = -1;
-       STACK_OF(X509) * untrustedchain = NULL;
-       long purpose;
-       char * untrusted = NULL;
-       long untrusted_len;
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zla|s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len)
-                       == FAILURE)
+       struct php_x509_request req;
+       zval * zpkey, * args = NULL, *out;
+       char * passphrase = NULL; long passphrase_len = 0;
+       long key_resource = -1;
+       zend_bool tofile = 0;
+       EVP_PKEY * key;
+       BIO * bio_out = NULL;
+       EVP_CIPHER * cipher;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzb|s!a!", &zpkey, &out, &tofile, &passphrase, &passphrase_len, &args) == FAILURE)
                return;
 
-       RETVAL_LONG(-1);
-
-       if (untrusted)  {
-               untrustedchain = load_all_certs_from_file(untrusted);
-               if (untrustedchain == NULL)
-                       goto clean_exit;
-       }
+       RETVAL_FALSE;
 
-       cainfo = setup_verify(zcainfo TSRMLS_CC);
-       if (cainfo == NULL)
-               goto clean_exit;
+       key = php_openssl_evp_from_zval(&zpkey, 0, passphrase, 0, &key_resource TSRMLS_C);
 
-       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
-       if (cert == NULL)
-               goto clean_exit;
+       if (key == NULL)        {
+               zend_error(E_WARNING, "cannot get key from parameter 1");
+               RETURN_FALSE;
+       }
+       
+       PHP_SSL_REQ_INIT(&req);
 
-       RETVAL_BOOL(check_cert(cainfo, cert, untrustedchain, purpose));
+       if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS)
+       {
+               if (tofile)
+                       bio_out = BIO_new_file(Z_STRVAL_P(out), "w");
+               else
+                       bio_out = BIO_new(BIO_s_mem());
+
+               if (passphrase && req.priv_key_encrypt)
+                       cipher = EVP_des_ede3_cbc();
+               else
+                       cipher = NULL;
+               
+               if (PEM_write_bio_PrivateKey(bio_out, key, cipher, passphrase, passphrase_len, NULL, NULL))     {
+                       /* Success!
+                        * If returning the output as a string, do so now */
+                       RETVAL_TRUE;
+
+                       if (!tofile)    {
+                               char * bio_mem_ptr;
+                               long bio_mem_len;
+                               
+                               bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
+                               ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len, 1);
+                       }
+               }
+       }
+       PHP_SSL_REQ_DISPOSE(&req);
 
-clean_exit:
-       if (certresource == 1 && cert)
-               X509_free(cert);
-       if (cainfo)
-               X509_STORE_free(cainfo);
-       if (untrustedchain)
-               sk_X509_pop_free(untrustedchain, X509_free);
+       if (key_resource == -1 && key)  {
+               EVP_PKEY_free(key);
+       }
+       if (bio_out)
+               BIO_free(bio_out);
 }
 /* }}} */
 
-/* {{{ proto int openssl_get_publickey(mixed cert)
+/* {{{ proto int openssl_pkey_get_public(mixed cert)
    Get public key from X.509 certificate */
-PHP_FUNCTION(openssl_get_publickey)
+PHP_FUNCTION(openssl_pkey_get_public)
 {
        zval *cert;
        EVP_PKEY *pkey;
@@ -954,7 +1772,7 @@ PHP_FUNCTION(openssl_get_publickey)
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &cert) == FAILURE)
                return;
 
-       return_value->type = IS_RESOURCE;
+       Z_TYPE_P(return_value) = IS_RESOURCE;
        pkey = php_openssl_evp_from_zval(&cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
 
        if (pkey == NULL) {
@@ -963,9 +1781,9 @@ PHP_FUNCTION(openssl_get_publickey)
 }
 /* }}} */
 
-/* {{{ proto void openssl_free_key(int key)
+/* {{{ proto void openssl_pkey_free(int key)
    Free key */
-PHP_FUNCTION(openssl_free_key)
+PHP_FUNCTION(openssl_pkey_free)
 {
        zval *key;
        EVP_PKEY *pkey;
@@ -978,6 +1796,10 @@ PHP_FUNCTION(openssl_free_key)
 }
 /* }}} */
 
+/* {{{ proto int openssl_pkey_get_private(string key [, string passphrase])
+   Get private key */
+PHP_FUNCTION(openssl_pkey_get_private)
+=======
 /* {{{ proto resource openssl_x509_read(mixed cert)
    Read X.509 certificate */
 PHP_FUNCTION(openssl_x509_read)
@@ -989,117 +1811,59 @@ PHP_FUNCTION(openssl_x509_read)
                return;
 
        return_value->type = IS_RESOURCE;
-       x509 = php_openssl_x509_from_zval(&cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
-
-       if (x509 == NULL) {
-               zend_error(E_WARNING, "%s() supplied parameter cannot be coerced into an X509 certificate!", get_active_function_name(TSRMLS_C));
-               RETURN_FALSE;
-       }
-}
-/* }}} */
-
-/* {{{ proto void openssl_free_x509(resource x509)
-   Free X.509 certificate */
-PHP_FUNCTION(openssl_x509_free)
-{
-       zval *x509;
-       X509 *cert;
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE)
-               return;
-
-       ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509);
-       zend_list_delete(Z_LVAL_P(x509));
-}
-/* }}} */
-
-/* {{{ setup_verify
- * calist is an array containing file and directory names.  create a
- * certificate store and add those certs to it for use in verification.
-*/
-static X509_STORE * setup_verify(zval * calist TSRMLS_DC)
-{
-       X509_STORE *store;
-       X509_LOOKUP * dir_lookup, * file_lookup;
-       HashPosition pos;
-       int ndirs = 0, nfiles = 0;
-
-       store = X509_STORE_new();
-
-       if (store == NULL)
-               return NULL;
-
-       if (calist && (calist->type == IS_ARRAY))       {
-               zend_hash_internal_pointer_reset_ex(HASH_OF(calist), &pos);
-               for (;; zend_hash_move_forward_ex(HASH_OF(calist), &pos))       {
-                       zval ** item;
-                       struct stat sb;
-
-                       if (zend_hash_get_current_data_ex(HASH_OF(calist), (void**)&item, &pos) == FAILURE)
-                               break;
-
-                       convert_to_string_ex(item);
-
-                       if (VCWD_STAT(Z_STRVAL_PP(item), &sb) == -1)    {
-                               zend_error(E_WARNING, "%s() unable to stat %s", get_active_function_name(TSRMLS_C), Z_STRVAL_PP(item));
-                               continue;
-                       }
-
-                       if ((sb.st_mode & S_IFREG) == S_IFREG)  {
-                               file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
-                               if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM))
-                                       zend_error(E_WARNING, "%s() error loading file %s", get_active_function_name(TSRMLS_C), Z_STRVAL_PP(item));
-                               else
-                                       nfiles++;
-                               file_lookup = NULL;
-                       }
-                       else    {
-                               dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
-                               if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM))
-                                       zend_error(E_WARNING, "%s() error loading directory %s", get_active_function_name(TSRMLS_C), Z_STRVAL_PP(item));
-                               else
-                                       ndirs++;
-                               dir_lookup = NULL;
-                       }
-               }
-       }
-       if (nfiles == 0)        {
-               file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
-               if (file_lookup)
-                       X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT);
-       }
-       if (ndirs == 0) {
-               dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
-               if (dir_lookup)
-                       X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT);
+       x509 = php_openssl_x509_from_zval(&cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
+
+       if (x509 == NULL) {
+               zend_error(E_WARNING, "%s() supplied parameter cannot be coerced into an X509 certificate!", get_active_function_name(TSRMLS_C));
+               RETURN_FALSE;
        }
-       return store;
 }
 /* }}} */
 
-/* {{{ proto mixed openssl_error_string()
-       returns a description of the last error, and alters the index of the error messages. returns false when the are no more messages. */
-PHP_FUNCTION(openssl_error_string)
+/* {{{ proto void openssl_free_x509(resource x509)
+   Free X.509 certificate */
+PHP_FUNCTION(openssl_x509_free)
 {
-       char buf[512];
-       unsigned long val;
+       zval *x509;
+       X509 *cert;
 
-       if (ZEND_NUM_ARGS() != 0)       {
-               WRONG_PARAM_COUNT;
-       }
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE)
+               return;
 
-       val = ERR_get_error();
-       if (val)
-       {
-               RETURN_STRING(ERR_error_string(val, buf), 1);
-       }
-       else
-       {
+       ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509);
+       zend_list_delete(Z_LVAL_P(x509));
+}
+/* }}} */
+
+/* {{{ setup_verify
+ * calist is an array containing file and directory names.  create a
+ * certificate store and add those certs to it for use in verification.
+*/
+static X509_STORE * setup_verify(zval * calist TSRMLS_DC)
+{
+{
+       EVP_PKEY *pkey;
+       zval * key;
+       char * passphrase = "";
+       int passphrase_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &key, &passphrase, &passphrase_len) == FAILURE)
+               return;
+
+       Z_TYPE_P(return_value) = IS_RESOURCE;
+       pkey = php_openssl_evp_from_zval(&key, 0, passphrase, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
+
+       if (pkey == NULL) {
+               zend_error(E_WARNING, "%s(): unable to coerce arg to a private key", get_active_function_name(TSRMLS_C));
                RETURN_FALSE;
        }
 }
 /* }}} */
 
+/* }}} */
+
+/* {{{ PKCS7 S/MIME functions */
+
 /* {{{ proto bool openssl_pkcs7_verify(string filename, long flags[, string signerscerts][, array cainfo][, string extracerts])
        verify that the data block is intact, the signer is who they say they are, and return the certs of the signers
 */
@@ -1328,7 +2092,7 @@ PHP_FUNCTION(openssl_pkcs7_sign)
        char * extracertsfilename = NULL; long extracertsfilename_len;
 
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sszza!|ls",
-                               &infilename, &infilename_len, &outfilename, &outfilename_len,
+                               &infilename, &infilename_len, *outfilename, &outfilename_len,
                                &zcert, &zprivkey, &zheaders, &flags, &extracertsfilename,
                                &extracertsfilename_len) == FAILURE)
                return;
@@ -1348,124 +2112,391 @@ PHP_FUNCTION(openssl_pkcs7_sign)
                goto clean_exit;
        }
 
-       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
-       if (cert == NULL)       {
-               zend_error(E_WARNING, "%s(): error getting cert", get_active_function_name(TSRMLS_C));
-               goto clean_exit;
+       cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
+       if (cert == NULL)       {
+               zend_error(E_WARNING, "%s(): error getting cert", get_active_function_name(TSRMLS_C));
+               goto clean_exit;
+       }
+
+       infile = BIO_new_file(infilename, "r");
+       if (infile == NULL)     {
+               zend_error(E_WARNING, "%s(): error opening input file %s!", get_active_function_name(TSRMLS_C), infilename);
+               goto clean_exit;
+       }
+
+       outfile = BIO_new_file(outfilename, "w");
+       if (outfile == NULL)    {
+               zend_error(E_WARNING, "%s(): error opening output file %s!", get_active_function_name(TSRMLS_C), outfilename);
+               goto clean_exit;
+       }
+
+       p7 = PKCS7_sign(cert, privkey, others, infile, flags);
+       if (p7 == NULL) {
+               zend_error(E_WARNING, "%s(): error creating PKCS7 structure!", get_active_function_name(TSRMLS_C));
+               goto clean_exit;
+       }
+
+       BIO_reset(infile);
+
+       /* 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**)&hval, &hpos) == SUCCESS)        {
+                       zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
+
+                       convert_to_string_ex(hval);
+
+                       if (strindex)
+                               BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(hval));
+                       else
+                               BIO_printf(outfile, "%s\n", Z_STRVAL_PP(hval));
+
+                       zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
+               }
+       }
+       /* write the signed data */
+       SMIME_write_PKCS7(outfile, p7, infile, flags);
+
+       RETVAL_TRUE;
+
+clean_exit:
+       PKCS7_free(p7);
+       BIO_free(infile);
+       BIO_free(outfile);
+       if (others)
+               sk_X509_pop_free(others, X509_free);
+       if (privkey && keyresource == -1)
+               EVP_PKEY_free(privkey);
+       if (cert && certresource == -1)
+               X509_free(cert);
+}
+/* }}} */
+
+/* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert[, mixed recipkey])
+       decrypt the S/MIME message in the file name infilename and output the results to the file name outfilename.  recipcert is a cert for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key */
+
+PHP_FUNCTION(openssl_pkcs7_decrypt)
+{
+       zval * recipcert, * recipkey = NULL;
+       X509 * cert = NULL;
+       EVP_PKEY * key = NULL;
+       long certresval, keyresval;
+       BIO * in = NULL, * out = NULL, * datain = NULL;
+       PKCS7 * p7 = NULL;
+       char * infilename;      long infilename_len;
+       char * outfilename;     long outfilename_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|z", &infilename, &infilename_len,
+                               &outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE)
+               return;
+
+       RETVAL_FALSE;
+
+       cert = php_openssl_x509_from_zval(&recipcert, 0, &certresval TSRMLS_CC);
+       if (cert == NULL)       {
+               zend_error(E_WARNING, "%s(): unable to coerce parameter 3 to x509 cert", get_active_function_name(TSRMLS_C));
+               goto clean_exit;
+       }
+
+       key = php_openssl_evp_from_zval(recipkey ? &recipkey : &recipcert, 0, "", 0, &keyresval TSRMLS_CC);
+       if (key == NULL)        {
+               zend_error(E_WARNING, "%s(): unable to get private key", get_active_function_name(TSRMLS_C));
+               goto clean_exit;
+       }
+
+       in = BIO_new_file(infilename, "r");
+       if (in == NULL) {
+               goto clean_exit;
+       }
+       out = BIO_new_file(outfilename, "w");
+       if (out == NULL)        {
+               goto clean_exit;
+       }
+
+       p7 = SMIME_read_PKCS7(in, &datain);
+
+       if (p7 == NULL)
+               goto clean_exit;
+
+       if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED))
+               RETVAL_TRUE;
+
+clean_exit:
+       PKCS7_free(p7);
+       BIO_free(datain);
+       BIO_free(in);
+       BIO_free(out);
+       if (cert && certresval == -1)
+               X509_free(cert);
+       if (key && keyresval == -1)
+               EVP_PKEY_free(key);
+}
+/* }}} */
+
+/* }}} */
+
+/* {{{ proto bool openssl_private_encrypt(string data, string crypted, mixed key [, int padding])
+   Encrypt data with private key */
+PHP_FUNCTION(openssl_private_encrypt)
+{
+       zval *key, *crypted;
+       EVP_PKEY *pkey;
+       int cryptedlen;
+       unsigned char *cryptedbuf = NULL;
+       int successful = 0;
+       long keyresource = -1;
+       char * data;
+       long data_len, padding = RSA_PKCS1_PADDING;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
+               return;
+
+       RETVAL_FALSE;
+
+       pkey = php_openssl_evp_from_zval(&key, 0, "", 0, &keyresource TSRMLS_CC);
+
+       if (pkey == NULL)       {
+               zend_error(E_WARNING, "%s(): key param is not a valid private key",
+                               get_active_function_name(TSRMLS_C));
+               RETURN_FALSE;
+       }
+       
+       cryptedlen = EVP_PKEY_size(pkey);
+       cryptedbuf = emalloc(cryptedlen + 1);
+
+       switch (pkey->type) {
+               case EVP_PKEY_RSA:
+               case EVP_PKEY_RSA2:
+                       successful =  (RSA_private_encrypt(data_len, 
+                                               data, 
+                                               cryptedbuf, 
+                                               pkey->pkey.rsa, 
+                                               padding) == cryptedlen);
+                       break;
+               default:
+                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
+       }
+
+       if (successful) {
+               zval_dtor(crypted);
+               cryptedbuf[cryptedlen] = '\0';
+               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
+               cryptedbuf = NULL;
+               RETVAL_TRUE;
+       }
+       if (cryptedbuf)
+               efree(cryptedbuf);
+       if (keyresource == -1)
+               EVP_PKEY_free(pkey);
+}
+/* }}} */
+
+/* {{{ proto bool openssl_private_decrypt(string data, string crypted, mixed key [, int padding])
+   Decrypt data with private key */
+PHP_FUNCTION(openssl_private_decrypt)
+{
+       zval *key, *crypted;
+       EVP_PKEY *pkey;
+       int cryptedlen;
+       unsigned char *cryptedbuf;
+       unsigned char *crypttemp;
+       int successful = 0;
+       long padding = RSA_PKCS1_PADDING;
+       long keyresource = -1;
+       char * data;
+       long data_len;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
+               return;
+
+       RETVAL_FALSE;
+       
+       pkey = php_openssl_evp_from_zval(&key, 0, "", 0, &keyresource TSRMLS_CC);
+       if (pkey == NULL)       {
+               zend_error(E_WARNING, "%s(): key parameter is not a valid private key", get_active_function_name(TSRMLS_C));
+               RETURN_FALSE;
+       }
+       
+       cryptedlen = EVP_PKEY_size(pkey);
+       crypttemp = emalloc(cryptedlen + 1);
+
+       switch (pkey->type) {
+               case EVP_PKEY_RSA:
+               case EVP_PKEY_RSA2:
+                       cryptedlen = RSA_private_decrypt(data_len, 
+                                       data, 
+                                       crypttemp, 
+                                       pkey->pkey.rsa, 
+                                       padding);
+                       if (cryptedlen != -1) {
+                               cryptedbuf = emalloc(cryptedlen + 1);
+                               memcpy(cryptedbuf, crypttemp, cryptedlen);
+                               successful = 1;
+                       }
+                       break;
+               default:
+                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
        }
 
-       infile = BIO_new_file(infilename, "r");
-       if (infile == NULL)     {
-               zend_error(E_WARNING, "%s(): error opening input file %s!", get_active_function_name(TSRMLS_C), infilename);
-               goto clean_exit;
-       }
+       efree(crypttemp);
 
-       outfile = BIO_new_file(outfilename, "w");
-       if (outfile == NULL)    {
-               zend_error(E_WARNING, "%s(): error opening output file %s!", get_active_function_name(TSRMLS_C), outfilename);
-               goto clean_exit;
+       if (successful) {
+               zval_dtor(crypted);
+               cryptedbuf[cryptedlen] = '\0';
+               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
+               cryptedbuf = NULL;
+               RETVAL_TRUE;
        }
 
-       p7 = PKCS7_sign(cert, privkey, others, infile, flags);
-       if (p7 == NULL) {
-               zend_error(E_WARNING, "%s(): error creating PKCS7 structure!", get_active_function_name(TSRMLS_C));
-               goto clean_exit;
-       }
+       if (keyresource == -1)
+               EVP_PKEY_free(pkey);
+       if (cryptedbuf)
+               efree(cryptedbuf);
+}
+/* }}} */
 
-       BIO_reset(infile);
+/* {{{ proto bool openssl_public_encrypt(string data, string crypted, mixed key [, int padding])
+   Encrypt data with public key */
+PHP_FUNCTION(openssl_public_encrypt)
+{
+       zval *key, *crypted;
+       EVP_PKEY *pkey;
+       int cryptedlen;
+       unsigned char *cryptedbuf;
+       int successful = 0;
+       long keyresource = -1;
+       long padding = RSA_PKCS1_PADDING;
+       char * data;
+       long data_len;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
+               return;
 
-       /* 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**)&hval, &hpos) == SUCCESS)        {
-                       zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
+       RETVAL_FALSE;
+       
+       pkey = php_openssl_evp_from_zval(&key, 1, NULL, 0, &keyresource TSRMLS_CC);
+       if (pkey == NULL)       {
+               zend_error(E_WARNING, "%s(): key parameter is not a valid public key", get_active_function_name(TSRMLS_C));
+               RETURN_FALSE;
+       }
 
-                       convert_to_string_ex(hval);
+       cryptedlen = EVP_PKEY_size(pkey);
+       cryptedbuf = emalloc(cryptedlen + 1);
 
-                       if (strindex)
-                               BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(hval));
-                       else
-                               BIO_printf(outfile, "%s\n", Z_STRVAL_PP(hval));
+       switch (pkey->type) {
+               case EVP_PKEY_RSA:
+               case EVP_PKEY_RSA2:
+                       successful = (RSA_public_encrypt(data_len, 
+                                               data, 
+                                               cryptedbuf, 
+                                               pkey->pkey.rsa, 
+                                               padding) == cryptedlen);
+                       break;
+               default:
+                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
 
-                       zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
-               }
        }
-       /* write the signed data */
-       SMIME_write_PKCS7(outfile, p7, infile, flags);
-
-       RETVAL_TRUE;
 
-clean_exit:
-       PKCS7_free(p7);
-       BIO_free(infile);
-       BIO_free(outfile);
-       if (others)
-               sk_X509_pop_free(others, X509_free);
-       if (privkey && keyresource == -1)
-               EVP_PKEY_free(privkey);
-       if (cert && certresource == -1)
-               X509_free(cert);
+       if (successful) {
+               zval_dtor(crypted);
+               cryptedbuf[cryptedlen] = '\0';
+               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
+               cryptedbuf = NULL;
+               RETVAL_TRUE;
+       }
+       if (keyresource == -1)
+               EVP_PKEY_free(pkey);
+       if (cryptedbuf)
+               efree(cryptedbuf);
 }
 /* }}} */
 
-/* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert[, mixed recipkey])
-       decrypt the S/MIME message in the file name infilename and output the results to the file name outfilename.  recipcert is a cert for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key */
-
-PHP_FUNCTION(openssl_pkcs7_decrypt)
+/* {{{ proto bool openssl_public_decrypt(string data, string crypted, resource key [, int padding])
+   Decrypt data with public key */
+PHP_FUNCTION(openssl_public_decrypt)
 {
-       zval * recipcert, * recipkey = NULL;
-       X509 * cert = NULL;
-       EVP_PKEY * key = NULL;
-       long certresval, keyresval;
-       BIO * in = NULL, * out = NULL, * datain = NULL;
-       PKCS7 * p7 = NULL;
-       char * infilename;      long infilename_len;
-       char * outfilename;     long outfilename_len;
+       zval *key, *crypted;
+       EVP_PKEY *pkey;
+       int cryptedlen;
+       unsigned char *cryptedbuf;
+       unsigned char *crypttemp;
+       int successful = 0;
+       long keyresource = -1;
+       long padding = RSA_PKCS1_PADDING;
+       char * data;
+       long data_len;
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|z", &infilename, &infilename_len,
-                               &outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE)
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
                return;
 
        RETVAL_FALSE;
-
-       cert = php_openssl_x509_from_zval(&recipcert, 0, &certresval TSRMLS_CC);
-       if (cert == NULL)       {
-               zend_error(E_WARNING, "%s(): unable to coerce parameter 3 to x509 cert", get_active_function_name(TSRMLS_C));
-               goto clean_exit;
+       
+       pkey = php_openssl_evp_from_zval(&key, 1, NULL, 0, &keyresource TSRMLS_CC);
+       if (pkey == NULL)       {
+               zend_error(E_WARNING, "%s(): key parameter is not a valid public key", get_active_function_name(TSRMLS_C));
+               RETURN_FALSE;
        }
 
-       key = php_openssl_evp_from_zval(recipkey ? &recipkey : &recipcert, 0, "", 0, &keyresval TSRMLS_CC);
-       if (key == NULL)        {
-               zend_error(E_WARNING, "%s(): unable to get private key", get_active_function_name(TSRMLS_C));
-               goto clean_exit;
-       }
+       cryptedlen = EVP_PKEY_size(pkey);
+       crypttemp = emalloc(cryptedlen + 1);
 
-       in = BIO_new_file(infilename, "r");
-       if (in == NULL) {
-               goto clean_exit;
+       switch (pkey->type) {
+               case EVP_PKEY_RSA:
+               case EVP_PKEY_RSA2:
+                       cryptedlen = RSA_public_decrypt(data_len, 
+                                       data, 
+                                       crypttemp, 
+                                       pkey->pkey.rsa, 
+                                       padding);
+                       if (cryptedlen != -1) {
+                               cryptedbuf = emalloc(cryptedlen + 1);
+                               memcpy(cryptedbuf, crypttemp, cryptedlen);
+                               successful = 1;
+                       }
+                       break;
+                       
+               default:
+                       zend_error(E_WARNING, "%s(): key type not supported in this PHP build!");
+                
        }
-       out = BIO_new_file(outfilename, "w");
-       if (out == NULL)        {
-               goto clean_exit;
+
+       efree(crypttemp);
+
+       if (successful) {
+               zval_dtor(crypted);
+               cryptedbuf[cryptedlen] = '\0';
+               ZVAL_STRINGL(crypted, cryptedbuf, cryptedlen, 0);
+               cryptedbuf = NULL;
+               RETVAL_TRUE;
        }
 
-       p7 = SMIME_read_PKCS7(in, &datain);
+       if (cryptedbuf)
+               efree(cryptedbuf);
+       if (keyresource == -1)
+               EVP_PKEY_free(pkey);
+}
+/* }}} */
 
-       if (p7 == NULL)
-               goto clean_exit;
+/* {{{ proto mixed openssl_error_string()
+       returns a description of the last error, and alters the index of the error messages. returns false when the are no more messages. */
+PHP_FUNCTION(openssl_error_string)
+{
+       char buf[512];
+       unsigned long val;
 
-       if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED))
-               RETVAL_TRUE;
+       if (ZEND_NUM_ARGS() != 0)       {
+               WRONG_PARAM_COUNT;
+       }
 
-clean_exit:
-       PKCS7_free(p7);
-       BIO_free(datain);
-       BIO_free(in);
-       BIO_free(out);
-       if (cert && certresval == -1)
-               X509_free(cert);
-       if (key && keyresval == -1)
-               EVP_PKEY_free(key);
+       val = ERR_get_error();
+       if (val)
+       {
+               RETURN_STRING(ERR_error_string(val, buf), 1);
+       }
+       else
+       {
+               RETURN_FALSE;
+       }
 }
 /* }}} */
 
@@ -1728,22 +2759,6 @@ PHP_FUNCTION(openssl_open)
 }
 /* }}} */
 
-/* {{{ _php_pkey_free() */
-static void _php_pkey_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
-{
-       EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr;
-       EVP_PKEY_free(pkey);
-}
-/* }}} */
-
-/* {{{ _php_x509_free() */
-static void _php_x509_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
-{
-       X509 *x509 = (X509 *)rsrc->ptr;
-       X509_free(x509);
-}
-/* }}} */
-
 /*
  * Local variables:
  * tab-width: 8