static pj_status_t ssl_create(pj_ssl_sock_t *ssock)
{
    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
#if !defined(OPENSSL_NO_DH)
    BIO *bio;
    DH *dh;
    long options;
#endif
    SSL_METHOD *ssl_method = NULL;
    SSL_CTX *ctx;
    pj_uint32_t ssl_opt = 0;
    pj_ssl_cert_t *cert;
    int mode, rc;
    pj_status_t status;
        
    pj_assert(ssock);

    cert = ssock->cert;

    /* Make sure OpenSSL library has been initialized */
    init_openssl();

    set_entropy(ssock);

    if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT)
	ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23;

    /* Determine SSL method to use */
    /* Specific version methods are deprecated since 1.1.0 */
#if (USING_LIBRESSL && LIBRESSL_VERSION_NUMBER < 0x2020100fL)\
    || OPENSSL_VERSION_NUMBER < 0x10100000L
    switch (ssock->param.proto) {
    case PJ_SSL_SOCK_PROTO_TLS1:
	ssl_method = (SSL_METHOD*)TLSv1_method();
	break;
#ifndef OPENSSL_NO_SSL2
    case PJ_SSL_SOCK_PROTO_SSL2:
	ssl_method = (SSL_METHOD*)SSLv2_method();
	break;
#endif
#ifndef OPENSSL_NO_SSL3_METHOD
    case PJ_SSL_SOCK_PROTO_SSL3:
	ssl_method = (SSL_METHOD*)SSLv3_method();
#endif
	break;
    }
#endif

    if (!ssl_method) {
#if (USING_LIBRESSL && LIBRESSL_VERSION_NUMBER < 0x2020100fL)\
    || OPENSSL_VERSION_NUMBER < 0x10100000L
	ssl_method = (SSL_METHOD*)SSLv23_method();
#else
	ssl_method = (SSL_METHOD*)TLS_method();
#endif

#ifdef SSL_OP_NO_SSLv2
	/** Check if SSLv2 is enabled */
	ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL2)==0)?
		    SSL_OP_NO_SSLv2:0;
#endif

#ifdef SSL_OP_NO_SSLv3
	/** Check if SSLv3 is enabled */
	ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL3)==0)?
		    SSL_OP_NO_SSLv3:0;
#endif

#ifdef SSL_OP_NO_TLSv1
	/** Check if TLSv1 is enabled */
	ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1)==0)?
		    SSL_OP_NO_TLSv1:0;
#endif

#ifdef SSL_OP_NO_TLSv1_1
	/** Check if TLSv1_1 is enabled */
	ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1)==0)?
		    SSL_OP_NO_TLSv1_1:0;
#endif

#ifdef SSL_OP_NO_TLSv1_2
	/** Check if TLSv1_2 is enabled */
	ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2)==0)?
		    SSL_OP_NO_TLSv1_2:0;
#endif

#ifdef SSL_OP_NO_TLSv1_3
	/** Check if TLSv1_3 is enabled */
	ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3)==0)?
		    SSL_OP_NO_TLSv1_3:0;
#endif

    }

    /* Create SSL context */
    ctx = SSL_CTX_new(ssl_method);
    if (ctx == NULL) {
	return GET_SSL_STATUS(ssock);
    }
    ossock->ossl_ctx = ctx;

    if (ssl_opt)
	SSL_CTX_set_options(ctx, ssl_opt);

    /* Set cipher list */
    status = set_cipher_list(ssock);
    if (status != PJ_SUCCESS)
        return status;

    /* Apply credentials */
    if (cert) {
	/* Load CA list if one is specified. */
	if (cert->CA_file.slen || cert->CA_path.slen) {

	    rc = SSL_CTX_load_verify_locations(
			ctx,
			cert->CA_file.slen == 0 ? NULL : cert->CA_file.ptr,
			cert->CA_path.slen == 0 ? NULL : cert->CA_path.ptr);

	    if (rc != 1) {
		status = GET_SSL_STATUS(ssock);
		if (cert->CA_file.slen) {
		    PJ_PERROR(1,(ssock->pool->obj_name, status,
				 "Error loading CA list file '%s'",
				 cert->CA_file.ptr));
		}
		if (cert->CA_path.slen) {
		    PJ_PERROR(1,(ssock->pool->obj_name, status,
				 "Error loading CA path '%s'",
				 cert->CA_path.ptr));
		}
		SSL_CTX_free(ctx);
		return status;
	    } else {
		PJ_LOG(4,(ssock->pool->obj_name,
			  "CA certificates loaded from '%s%s%s'",
			  cert->CA_file.ptr,
			  ((cert->CA_file.slen && cert->CA_path.slen)?
				" + ":""),
			  cert->CA_path.ptr));
	    }
	}
    
	/* Set password callback */
	if (cert->privkey_pass.slen) {
	    SSL_CTX_set_default_passwd_cb(ctx, password_cb);
	    SSL_CTX_set_default_passwd_cb_userdata(ctx, cert);
	}


	/* Load certificate if one is specified */
	if (cert->cert_file.slen) {

	    /* Load certificate chain from file into ctx */
	    rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr);

	    if(rc != 1) {
		status = GET_SSL_STATUS(ssock);
		PJ_PERROR(1,(ssock->pool->obj_name, status,
			     "Error loading certificate chain file '%s'",
			     cert->cert_file.ptr));
		SSL_CTX_free(ctx);
		return status;
	    } else {
		PJ_LOG(4,(ssock->pool->obj_name,
			  "Certificate chain loaded from '%s'",
			  cert->cert_file.ptr));
	    }
	}


	/* Load private key if one is specified */
	if (cert->privkey_file.slen) {
	    /* Adds the first private key found in file to ctx */
	    rc = SSL_CTX_use_PrivateKey_file(ctx, cert->privkey_file.ptr, 
					     SSL_FILETYPE_PEM);

	    if(rc != 1) {
		status = GET_SSL_STATUS(ssock);
		PJ_PERROR(1,(ssock->pool->obj_name, status,
			     "Error adding private key from '%s'",
			     cert->privkey_file.ptr));
		SSL_CTX_free(ctx);
		return status;
	    } else {
		PJ_LOG(4,(ssock->pool->obj_name,
			  "Private key loaded from '%s'",
			  cert->privkey_file.ptr));
	    }

#if !defined(OPENSSL_NO_DH)
	    if (ssock->is_server) {
		bio = BIO_new_file(cert->privkey_file.ptr, "r");
		if (bio != NULL) {
		    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
		    if (dh != NULL) {
			if (SSL_CTX_set_tmp_dh(ctx, dh)) {
			    options = SSL_OP_CIPHER_SERVER_PREFERENCE |
    #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L
				      SSL_OP_SINGLE_ECDH_USE |
    #endif
				      SSL_OP_SINGLE_DH_USE;
			    options = SSL_CTX_set_options(ctx, options);
			    PJ_LOG(4,(ssock->pool->obj_name, "SSL DH "
				     "initialized, PFS cipher-suites enabled"));
			}
			DH_free(dh);
		    }
		    BIO_free(bio);
		}
	    }
#endif
	}

	/* Load from buffer. */
	if (cert->cert_buf.slen) {
	    BIO *cbio;
	    X509 *xcert = NULL;
	    
	    cbio = BIO_new_mem_buf((void*)cert->cert_buf.ptr,
				   cert->cert_buf.slen);
	    if (cbio != NULL) {
		xcert = PEM_read_bio_X509(cbio, NULL, 0, NULL);
		if (xcert != NULL) {
		    rc = SSL_CTX_use_certificate(ctx, xcert);
		    if (rc != 1) {
			status = GET_SSL_STATUS(ssock);
			PJ_PERROR(1,(ssock->pool->obj_name, status,
			      "Error loading chain certificate from buffer"));
			X509_free(xcert);
			BIO_free(cbio);
			SSL_CTX_free(ctx);
			return status;
		    } else {
			PJ_LOG(4,(ssock->pool->obj_name,
				  "Certificate chain loaded from buffer"));
		    }
		    X509_free(xcert);
		}
		BIO_free(cbio);
	    }	    
	}

	if (cert->CA_buf.slen) {
	    BIO *cbio = BIO_new_mem_buf((void*)cert->CA_buf.ptr,
					cert->CA_buf.slen);
	    X509_STORE *cts = SSL_CTX_get_cert_store(ctx);

	    if (cbio && cts) {
		STACK_OF(X509_INFO) *inf = PEM_X509_INFO_read_bio(cbio, NULL, 
								  NULL, NULL);

		if (inf != NULL) {
		    int i = 0, cnt = 0;
		    for (; i < sk_X509_INFO_num(inf); i++) {
			X509_INFO *itmp = sk_X509_INFO_value(inf, i);
			if (!itmp->x509)
			    continue;

			rc = X509_STORE_add_cert(cts, itmp->x509);
			if (rc == 1) {
			    ++cnt;
			} else {
#if PJ_LOG_MAX_LEVEL >= 4
			    char buf[256];
			    PJ_LOG(4,(ssock->pool->obj_name,
				      "Error adding CA cert: %s",
				      X509_NAME_oneline(
					X509_get_subject_name(itmp->x509),
					buf, sizeof(buf))));
#endif
			}
		    }
		    PJ_LOG(4,(ssock->pool->obj_name,
			      "CA certificates loaded from buffer (cnt=%d)",
			      cnt));
		}
		sk_X509_INFO_pop_free(inf, X509_INFO_free);
		BIO_free(cbio);
	    }
	}

	if (cert->privkey_buf.slen) {
	    BIO *kbio;	    
	    EVP_PKEY *pkey = NULL;

	    kbio = BIO_new_mem_buf((void*)cert->privkey_buf.ptr,
				   cert->privkey_buf.slen);
	    if (kbio != NULL) {
		pkey = PEM_read_bio_PrivateKey(kbio, NULL, &password_cb,
					       cert);
		if (pkey) {
		    rc = SSL_CTX_use_PrivateKey(ctx, pkey);
		    if (rc != 1) {
			status = GET_SSL_STATUS(ssock);
			PJ_PERROR(1,(ssock->pool->obj_name, status,
				     "Error adding private key from buffer"));
			EVP_PKEY_free(pkey);
			BIO_free(kbio);
			SSL_CTX_free(ctx);
			return status;
		    } else {
			PJ_LOG(4,(ssock->pool->obj_name,
				  "Private key loaded from buffer"));
		    }
		    EVP_PKEY_free(pkey);
		} else {
		    PJ_LOG(1,(ssock->pool->obj_name,
			      "Error reading private key from buffer"));
		}

		if (ssock->is_server) {
		    dh = PEM_read_bio_DHparams(kbio, NULL, NULL, NULL);
		    if (dh != NULL) {
			if (SSL_CTX_set_tmp_dh(ctx, dh)) {
			    options = SSL_OP_CIPHER_SERVER_PREFERENCE |
    #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L
				      SSL_OP_SINGLE_ECDH_USE |
    #endif
				      SSL_OP_SINGLE_DH_USE;
			    options = SSL_CTX_set_options(ctx, options);
			    PJ_LOG(4,(ssock->pool->obj_name, "SSL DH "
				     "initialized, PFS cipher-suites enabled"));
			}
			DH_free(dh);
		    }
		}
		BIO_free(kbio);
	    }	    
	}
    }

    if (ssock->is_server) {
	char *p = NULL;

	/* If certificate file name contains "_rsa.", let's check if there are
	 * ecc and dsa certificates too.
	 */
	if (cert && cert->cert_file.slen) {
	    const pj_str_t RSA = {"_rsa.", 5};
	    p = pj_strstr(&cert->cert_file, &RSA);
	    if (p) p++; /* Skip underscore */
	}
	if (p) {
	    /* Certificate type string length must be exactly 3 */
	    enum { CERT_TYPE_LEN = 3 };
	    const char* cert_types[] = { "ecc", "dsa" };
	    char *cf = cert->cert_file.ptr;
	    int i;

	    /* Check and load ECC & DSA certificates & private keys */
	    for (i = 0; i < PJ_ARRAY_SIZE(cert_types); ++i) {
		int err;

		pj_memcpy(p, cert_types[i], CERT_TYPE_LEN);
		if (!pj_file_exists(cf))
		    continue;

		err = SSL_CTX_use_certificate_chain_file(ctx, cf);
		if (err == 1)
		    err = SSL_CTX_use_PrivateKey_file(ctx, cf,
						      SSL_FILETYPE_PEM);
		if (err == 1) {
		    PJ_LOG(4,(ssock->pool->obj_name,
			      "Additional certificate '%s' loaded.", cf));
		} else {
		    PJ_PERROR(1,(ssock->pool->obj_name, GET_SSL_STATUS(ssock),
				 "Error loading certificate file '%s'", cf));
		    ERR_clear_error();
		}
	    }

	    /* Put back original name */
	    pj_memcpy(p, "rsa", CERT_TYPE_LEN);
	}

    #ifndef SSL_CTRL_SET_ECDH_AUTO
	#define SSL_CTRL_SET_ECDH_AUTO 94
    #endif

	/* SSL_CTX_set_ecdh_auto(ctx,on) requires OpenSSL 1.0.2 which wraps: */
	if (SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) {
	    PJ_LOG(4,(ssock->pool->obj_name, "SSL ECDH initialized "
		      "(automatic), faster PFS ciphers enabled"));
    #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L && \
	OPENSSL_VERSION_NUMBER < 0x10100000L
	} else {
	    /* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */
	    EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
	    if (ecdh != NULL) {
		if (SSL_CTX_set_tmp_ecdh(ctx, ecdh)) {
		    PJ_LOG(4,(ssock->pool->obj_name, "SSL ECDH initialized "
			      "(secp256r1), faster PFS cipher-suites enabled"));
		}
		EC_KEY_free(ecdh);
	    }
    #endif
	}
    } else {
	X509_STORE *pkix_validation_store = SSL_CTX_get_cert_store(ctx);
	if (NULL != pkix_validation_store) {
#if defined(X509_V_FLAG_TRUSTED_FIRST)
	    X509_STORE_set_flags(pkix_validation_store, 
				 X509_V_FLAG_TRUSTED_FIRST);
#endif
#if defined(X509_V_FLAG_PARTIAL_CHAIN)
	    X509_STORE_set_flags(pkix_validation_store, 
				 X509_V_FLAG_PARTIAL_CHAIN);
#endif
	}
    }

    /* Add certificate authorities for clients from CA.
     * Needed for certificate request during handshake.
     */
    if (cert && ssock->is_server) {
        STACK_OF(X509_NAME) *ca_dn = NULL;

        if (cert->CA_file.slen > 0) {
            ca_dn = SSL_load_client_CA_file(cert->CA_file.ptr);
        } else if (cert->CA_buf.slen > 0) {
            X509      *x  = NULL;
            X509_NAME *xn = NULL;
            STACK_OF(X509_NAME) *sk = NULL;
            BIO *new_bio = BIO_new_mem_buf((void*)cert->CA_buf.ptr,
					   cert->CA_buf.slen);

            sk = sk_X509_NAME_new(xname_cmp);

            if (sk != NULL && new_bio != NULL) {
                for (;;) {
                    if (PEM_read_bio_X509(new_bio, &x, NULL, NULL) == NULL)
                        break;

                    if ((xn = X509_get_subject_name(x)) == NULL)
                        break;

                    if ((xn = X509_NAME_dup(xn)) == NULL )
                        break;

                    if (sk_X509_NAME_find(sk, xn) >= 0) {
                        X509_NAME_free(xn);
                    } else {
                        sk_X509_NAME_push(sk, xn);
                    }
                    X509_free(x);
                    x = NULL;
                }
            }
            if (sk != NULL)
            	ca_dn = sk;
            if (new_bio != NULL)
                BIO_free(new_bio);
        }

	if (ca_dn != NULL) {
	    SSL_CTX_set_client_CA_list(ctx, ca_dn);
	    PJ_LOG(4,(ssock->pool->obj_name,
		      "CA certificates loaded from %s",
		      (cert->CA_file.slen?cert->CA_file.ptr:"buffer")));
	} else {
	    PJ_LOG(1,(ssock->pool->obj_name,
		      "Error reading CA certificates from %s",
		      (cert->CA_file.slen?cert->CA_file.ptr:"buffer")));
	}
    }

    /* Early sensitive data cleanup after OpenSSL context setup. However,
     * this cannot be done for listener sockets, as the data will still
     * be needed by accepted sockets.
     */
    if (cert && (!ssock->is_server || ssock->parent)) {
	pj_ssl_cert_wipe_keys(cert);	
    }

    /* Create SSL instance */
    ossock->ossl_ssl = SSL_new(ossock->ossl_ctx);
    if (ossock->ossl_ssl == NULL) {
	return GET_SSL_STATUS(ssock);
    }

    /* Set SSL sock as application data of SSL instance */
    SSL_set_ex_data(ossock->ossl_ssl, sslsock_idx, ssock);

    /* SSL verification options */
    mode = SSL_VERIFY_PEER;
    if (ssock->is_server && ssock->param.require_client_cert)
	mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;

    SSL_set_verify(ossock->ossl_ssl, mode, &verify_cb);

    /* Set curve list */
    status = set_curves_list(ssock);
    if (status != PJ_SUCCESS)
	return status;

    /* Set sigalg list */
    status = set_sigalgs(ssock);
    if (status != PJ_SUCCESS)
	return status;

    /* Setup SSL BIOs */
    ossock->ossl_rbio = BIO_new(BIO_s_mem());
    ossock->ossl_wbio = BIO_new(BIO_s_mem());
    (void)BIO_set_close(ossock->ossl_rbio, BIO_CLOSE);
    (void)BIO_set_close(ossock->ossl_wbio, BIO_CLOSE);
    SSL_set_bio(ossock->ossl_ssl, ossock->ossl_rbio, ossock->ossl_wbio);

    return PJ_SUCCESS;
}