#include <stdio.h>
#include <stdlib.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/pem.h>
 
void handle_error(const char *file, int lineno, const char *msg)
{
    fprintf(stderr, "** %s:%i %s\n", file, lineno, msg);
    ERR_print_errors_fp(stderr);
    exit(-1);
}
#define int_error(msg)  handle_error(__FILE__, __LINE__, msg)
 
#define PKEY_FILE   "privkey.pem"
#define REQ_FILE    "newreq.pem"
#define ENTRY_COUNT 6
 
struct entry
{
    char *key;
    char *value;
};
 
struct entry entries[ENTRY_COUNT] = 
{
    { "countryName",            "US"              },
    { "stateOrProvinceName",    "VA"              },
    { "localityName",           "Fairfax"         },
    { "organizationName",       "Zork.org"        },
    { "organizationalUnitName", "Server Division" },
    { "commonName",             "Server 36, Engineering"  },
};
 
int main(int argc, char *argv[])
{
    int       i;
    X509_REQ  *req;
    X509_NAME *subj;
    EVP_PKEY  *pkey;
    EVP_MD    *digest;
    FILE      *fp;
 
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
    seed_prng();
 
    /* ܂閧ǂݎ */
    if (!(fp = fopen(PKEY_FILE, "r")))
        int_error("Error reading private key file");
    if (!(pkey = PEM_read_PrivateKey(fp, NULL, NULL, "secret")))
        int_error("Error reading private key in file");
    fclose(fp);
 
    /* VK̗v쐬At */
    if (!(req = X509_REQ_new()))
        int_error("Failed to create X509_REQ object");
    X509_REQ_set_pubkey(req, pkey); 
 
    /* subjectName蓖Ă */
    if (!(subj = X509_NAME_new()))
        int_error("Failed to create X509_NAME object");
 
    for (i = 0;  i < ENTRY_COUNT;  i++)
    {
        int             nid;
        X509_NAME_ENTRY *ent;
    
        if ((nid = OBJ_txt2nid(entries[i].key)) == NID_undef)
        {
            fprintf(stderr, "Error finding NID for %s\n", entries[i].key);
            int_error("Error on lookup");
        }
        if (!(ent = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC,
                                                 entries[i].value, -1)))
            int_error("Error creating Name entry from NID");
        if (X509_NAME_add_entry(subj, ent, -1, 0) != 1)
            int_error("Error adding entry to Name");
    } 
    if (X509_REQ_set_subject_name(req, subj) != 1)
        int_error("Error adding subject to request");
    /* KvFQDN܂߂g̈ǉ */
    {
        X509_EXTENSION           *ext;
        STACK_OF(X509_EXTENSION) *extlist;
        char                     *name = "subjectAltName";
        char                     *value = "DNS:splat.zork.org";
 
        extlist = sk_X509_EXTENSION_new_null();
 
        if (!(ext = X509V3_EXT_conf(NULL, NULL, name, value)))
            int_error("Error creating subjectAltName extension");
 
        sk_X509_EXTENSION_push(extlist, ext);
 
        if (!X509_REQ_add_extensions(req, extlist))
            int_error("Error adding subjectAltName to the request");
        sk_X509_EXTENSION_pop_free(extlist, X509_EXTENSION_free);
    }
 
    /* K؂ȃ_CWFXgAvɏ */
    if (EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA)
        digest = EVP_dss1();
    else if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
        digest = EVP_sha1();
    else
        int_error("Error checking public key for a valid digest");
    if (!(X509_REQ_sign(req, pkey, digest)))
        int_error("Error signing request");
 
    /* v */
    if (!(fp = fopen(REQ_FILE, "w")))
        int_error("Error writing to request file");
    if (PEM_write_X509_REQ(fp, req) != 1)
        int_error("Error while writing request");
    fclose(fp);
 
    EVP_PKEY_free(pkey);
    X509_REQ_free(req);
    return 0;
}
