#include <stdio.h>
#include <stdlib.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
 
int main(int argc, char *argv[])
{
    int              encrypt;
    PKCS7            *pkcs7;
    const EVP_CIPHER *cipher;
    STACK_OF(X509)   *certs;
    X509             *cert;
    EVP_PKEY         *pkey;
    FILE             *fp;
    BIO              *pkcs7_bio, *in, *out;
 
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
    seed_prng();
 
    --argc, ++argv;
    if (argc < 2)
    {
        fprintf(stderr, "Usage: ed (encrypt | decrypt) [privkey.pem] cert.pem "
                        "...\n");
        goto err;
    }
    if (!strcmp(*argv, "encrypt"))
        encrypt = 1;
    else if(!strcmp(*argv, "decrypt"))
        encrypt = 0;
    else
    {
        fprintf(stderr, "Usage: ed (encrypt | decrypt) [privkey.pem] cert.pem "
                        "...\n");
        goto err;
    }
    --argc, ++argv;
 
    /* W͂ƕWo͂BIOIuWFNgݒ */
    if (!(in = BIO_new_fp(stdin, BIO_NOCLOSE))  |  | 
        !(out = BIO_new_fp(stdout, BIO_NOCLOSE)))
    {
        fprintf(stderr, "Error creating BIO objects\n");
        goto err;
    }
 
    if (encrypt)
    {
        /* Í̑ΏۂɂāAÍIďׂؖēǂݍ */
        cipher = EVP_des_ede3_cbc();
        certs = sk_X509_new_null();
 
        while (argc)
        {
            X509 *tmp;
 
            if (!(fp = fopen(*argv, "r"))  |  | 
                !(tmp = PEM_read_X509(fp, NULL, NULL, NULL)))
            {
                fprintf(stderr, "Error reading encryption certificate in %s\n",
                        *argv);
                goto err;
            }
            sk_X509_push(certs, tmp);
            fclose(fp);
            --argc, ++argv;
        }
 
        if (!(pkcs7 = PKCS7_encrypt(certs, in, cipher, 0)))
        {
            ERR_print_errors_fp(stderr);
            fprintf(stderr, "Error making the PKCS#7 object\n");
            goto err;
        }
        if (SMIME_write_PKCS7(out, pkcs7, in, 0) != 1)
        {
            fprintf(stderr, "Error writing the S/MIME data\n");
            goto err;
        }
    }
    else
    {
        if (!(fp = fopen(*argv, "r"))  |  | 
            !(pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)))
        {
            fprintf(stderr, "Error reading private key in %s\n", *argv);
            goto err;
        }
        fclose(fp);
        --argc, ++argv;
        if (!(fp = fopen(*argv, "r"))  |  | 
            !(cert = PEM_read_X509(fp, NULL, NULL, NULL)))
        {
            fprintf(stderr, "Error reading decryption certificate in %s\n",
                    *argv);
            goto err;
        }
        fclose(fp);
        --argc, ++argv;
 
        if (argc)
            fprintf(stderr, "Warning: excess parameters specified. "
                            "Ignoring...\n");
 
        if (!(pkcs7 = SMIME_read_PKCS7(in, &pkcs7_bio)))
        {
            fprintf(stderr, "Error reading PKCS#7 object\n");
            goto err;
        }
        if (PKCS7_decrypt(pkcs7, pkey, cert, out, 0) != 1)
        {
            fprintf(stderr, "Error decrypting PKCS#7 object\n");
            goto err;
        }
    }
 
    return 0;
err:
    return -1;
}
