Hi,
Awhile ago I mentioned wanting to get proxy support (RFC 3280, yes it's
expired, but in use) into openssl as simply as possible.
I've built the attached module, that does what I wanted. What's the best way
to try and get this integrated into the standard distribution?
Thanks,
--Ivan
/* apps/x509_proxy.c */
/* Copyright (C) 2006 Ivan R. Judson ([EMAIL PROTECTED])
* All rights reserved.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef OPENSSL_NO_STDIO
#define APPS_WIN16
#endif
#include "apps.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/bn.h>
#include <openssl/lhash.h>
#ifndef OPENSSL_NO_RSA
#include <openssl/rsa.h>
#endif
#undef PROG
#define PROG x509_proxy_main
#undef POSTFIX
#define POSTFIX ".srl"
#define DEF_DAYS 1
static const char *x509_proxy_usage[]={
"usage: x509_proxy args\n",
" -inform arg - input format - default PEM (one of DER, NET or PEM)\n",
" -keyform arg - private key format - default PEM\n",
" -cert arg - input certificate\n",
" -key arg - input private key\n",
" -proxy arg - input proxy certificate\n",
" -out arg - output file - default stdout\n",
" -passin arg - private key password source\n",
" -days arg - How long till expiry of a signed certificate - def 30
days\n",
" -md2/-md5/-sha1/-mdc2 - digest to use\n",
" -create - create a proxy certificate\n",
" -info - print the certificate in text form\n",
NULL
};
#ifndef OPENSSL_NO_RSA
static int MS_CALLBACK genrsa_cb(int p, int n, BN_GENCB *cb);
#endif
int MAIN(int, char **);
int MAIN(int argc, char **argv)
{
char *proxyfile=NULL, *outfile=NULL, *keyfile=NULL, *certfile=NULL;
char *alias=NULL, *passin=NULL, *passargin=NULL;
const char **pp;
int ret=1, num, badops=0, informat, keyformat, info=0;
int create=0, days=DEF_DAYS, modulus=512;
unsigned long nmflag = 0, certflag = 0;
const EVP_MD *md_alg, *digest=EVP_md5();
ASN1_INTEGER *sno = NULL;
BIO *out=NULL, *STDout=NULL;
ENGINE *e = NULL;
EVP_PKEY *userkey=NULL;
STACK_OF(ASN1_OBJECT) *trust = NULL, *reject = NULL;
X509 *usercert=NULL, *proxy=NULL;
#ifndef OPENSSL_NO_ENGINE
char *engine=NULL;
#endif
apps_startup();
if (bio_err == NULL)
bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
if (!load_config(bio_err, NULL))
goto end;
STDout=BIO_new_fp(stdout,BIO_NOCLOSE);
#ifdef OPENSSL_SYS_VMS
{
BIO *tmpbio = BIO_new(BIO_f_linebuffer());
STDout = BIO_push(tmpbio, STDout);
}
#endif
informat=FORMAT_PEM;
keyformat=FORMAT_PEM;
argc--;
argv++;
num=0;
while (argc >= 1) {
if (strcmp(*argv,"-inform") == 0) {
if (--argc < 1) goto bad;
informat=str2fmt(*(++argv));
} else if (strcmp(*argv,"-keyform") == 0) {
if (--argc < 1) goto bad;
keyformat=str2fmt(*(++argv));
} else if (strcmp(*argv,"-days") == 0) {
if (--argc < 1) goto bad;
days=atoi(*(++argv));
if (days == 0) {
BIO_printf(STDout,"bad number of days\n");
goto bad;
}
} else if (strcmp(*argv,"-passin") == 0) {
if (--argc < 1) goto bad;
passargin= *(++argv);
} else if (strcmp(*argv,"-cert") == 0) {
if (--argc < 1) goto bad;
certfile= *(++argv);
} else if (strcmp(*argv,"-key") == 0) {
if (--argc < 1) goto bad;
keyfile= *(++argv);
} else if (strcmp(*argv,"-proxy") == 0) {
if (--argc < 1) goto bad;
proxyfile= *(++argv);
} else if (strcmp(*argv,"-out") == 0) {
if (--argc < 1) goto bad;
outfile= *(++argv);
} else if (strcmp(*argv,"-create") == 0)
create = ++num;
else if (strcmp(*argv,"-info") == 0)
info = ++num;
else if ((md_alg=EVP_get_digestbyname(*argv + 1)))
digest=md_alg;
else {
BIO_printf(bio_err,"unknown option %s\n",*argv);
badops=1;
break;
}
argc--;
argv++;
}
if (badops) {
bad:
for (pp=x509_proxy_usage; (*pp != NULL); pp++)
BIO_printf(bio_err,"%s",*pp);
goto end;
}
/* Initialization */
#ifndef OPENSSL_NO_ENGINE
e = setup_engine(bio_err, engine, 0);
#endif
if (create)
app_RAND_load_file(NULL, bio_err, 0);
ERR_load_crypto_strings();
if (!app_passwd(bio_err, passargin, NULL, &passin, NULL)) {
BIO_printf(bio_err, "Error getting password\n");
goto end;
}
/* Create output file or filehandle */
OBJ_create("2.99999.3",
"SET.ex3","SET x509v3 extension 3");
out=BIO_new(BIO_s_file());
if (out == NULL) {
ERR_print_errors(bio_err);
goto end;
}
if (outfile == NULL) {
BIO_set_fp(out,stdout,BIO_NOCLOSE);
#ifdef OPENSSL_SYS_VMS
{
BIO *tmpbio = BIO_new(BIO_f_linebuffer());
out = BIO_push(tmpbio, out);
}
#endif
} else {
if (BIO_write_filename(out,outfile) <= 0) {
perror(outfile);
goto end;
}
}
/* If print the proxy, print it! */
if (info) {
proxy = load_cert(bio_err, proxyfile, informat, NULL, e, "Certificate");
X509_print_ex(out, proxy, nmflag, certflag);
}
/* If create a new proxy, do that... */
if (create) {
long tout = 60*60*12*days;
unsigned long f4=RSA_F4;
EVP_PKEY *proxykey = EVP_PKEY_new(), *signkey = EVP_PKEY_new();
LHASH *hash = lh_new(NULL, NULL);
X509V3_CTX ctx;
X509_EXTENSION *ku_ext, *pci_ext;
X509_NAME *sname=NULL;
X509_NAME_ENTRY *cn=NULL;
RSA *rsa = RSA_new();
BN_GENCB cb;
BIGNUM *bn = BN_new();
/* Create a new X509 Certificate to be the proxy */
proxy=X509_new();
/* Load the certificate */
if (certfile) {
usercert = load_cert(bio_err, certfile, informat, NULL, e,"Certificate");
if (usercert == NULL) goto end;
while(X509_get_ext_count(usercert) > 0)
X509_delete_ext(usercert, 0);
}
/* Load the private key */
if (keyfile) {
userkey = load_key(bio_err, keyfile, FORMAT_PEM, 0,
NULL, e, "Private Key");
}
/* Generate a new RSA private key */
BN_GENCB_set(&cb, genrsa_cb, bio_err);
BIO_printf(bio_err,
"Generating RSA private key, %d bit long modulus\n",
modulus);
if (!BN_set_word(bn, f4) ||
!RSA_generate_key_ex(rsa, modulus, bn, &cb) ||
!EVP_PKEY_assign_RSA(proxykey, rsa)) {
BN_free(bn);
RSA_free(rsa);
goto end;
}
/* Create the fancy proxy extensions */
X509V3_set_conf_lhash(&ctx, hash);
ku_ext = X509V3_EXT_conf(hash, &ctx, "keyUsage",
"Digital Signature, Key Encipherment, Data Encipherment");
X509_EXTENSION_set_critical(ku_ext, 1);
pci_ext = X509V3_EXT_conf(hash, &ctx, "proxyCertInfo",
"critical, language:Inherit all");
X509_EXTENSION_set_critical(pci_ext, 1);
/* Get a serial number, creating one if necessary */
if (sno) {
if (!X509_set_serialNumber(proxy, sno)) goto end;
} else {
if (!rand_serial(NULL, X509_get_serialNumber(proxy))) goto end;
}
/* Assign the bits to the proxy */
if (!X509_set_pubkey(proxy, proxykey)) goto end;
if (!X509_set_version(proxy, 2)) goto end;
if (X509_gmtime_adj(X509_get_notBefore(proxy), (long)0) == NULL) goto end;
if (X509_gmtime_adj(X509_get_notAfter(proxy),(long)3600*24*days) == NULL)
goto end;
sname = X509_NAME_dup(X509_get_subject_name(usercert));
if (!X509_NAME_add_entry_by_txt(sname, "commonName", MBSTRING_ASC,
i2s_ASN1_INTEGER(NULL,
X509_get_serialNumber(proxy)),
-1, -1, 0))
goto end;
if (!X509_set_issuer_name(proxy, X509_get_subject_name(usercert)))
goto end;
if (!X509_set_subject_name(proxy, sname))
goto end;
if (!X509_add_ext(proxy, ku_ext, 1)) goto end;
if (!X509_add_ext(proxy, pci_ext, 1)) goto end;
/* Assign the user supplied private key to the signing key */
EVP_PKEY_assign_RSA(signkey, EVP_PKEY_get1_RSA(userkey));
/* sign the proxy */
if (!X509_sign(proxy, signkey, digest)) {
ERR_print_errors(bio_err);
RSA_free(rsa);
EVP_PKEY_free(proxykey);
goto end;
}
/* Print out the proxy */
if (!PEM_write_bio_X509(out, proxy)) {
BIO_printf(bio_err,"Unable to write proxy certificate\n");
ERR_print_errors(bio_err);
}
if (!PEM_write_bio_RSAPrivateKey(out, rsa, NULL, NULL, 0, NULL, NULL)) {
BIO_printf(bio_err,"Unable to write private key\n");
ERR_print_errors(bio_err);
}
if (!PEM_write_bio_X509(out, usercert)) {
BIO_printf(bio_err,"Unable to write user certificate\n");
ERR_print_errors(bio_err);
}
RSA_free(rsa);
}
ret=0;
/* Cleanup */
end:
if (create)
app_RAND_write_file(NULL, bio_err);
OBJ_cleanup();
BIO_free_all(out);
BIO_free_all(STDout);
X509_free(usercert);
EVP_PKEY_free(userkey);
ASN1_INTEGER_free(sno);
if (passin) OPENSSL_free(passin);
apps_shutdown();
OPENSSL_EXIT(ret);
}
static ASN1_INTEGER *x509_load_serial(char *CAfile, char *serialfile, int
create)
{
char *buf = NULL, *p;
ASN1_INTEGER *bs = NULL;
BIGNUM *serial = NULL;
size_t len;
len = ((serialfile == NULL)
?(strlen(CAfile)+strlen(POSTFIX)+1)
:(strlen(serialfile)))+1;
buf=OPENSSL_malloc(len);
if (buf == NULL) { BIO_printf(bio_err,"out of mem\n"); goto end; }
if (serialfile == NULL)
{
BUF_strlcpy(buf,CAfile,len);
for (p=buf; *p; p++)
if (*p == '.')
{
*p='\0';
break;
}
BUF_strlcat(buf,POSTFIX,len);
}
else
BUF_strlcpy(buf,serialfile,len);
serial = load_serial(buf, create, NULL);
if (serial == NULL) goto end;
if (!BN_add_word(serial,1))
{ BIO_printf(bio_err,"add_word failure\n"); goto end; }
if (!save_serial(buf, NULL, serial, &bs)) goto end;
end:
if (buf) OPENSSL_free(buf);
BN_free(serial);
return bs;
}
#ifndef OPENSSL_NO_RSA
static int MS_CALLBACK genrsa_cb(int p, int n, BN_GENCB *cb)
{
char c='*';
if (p == 0) c='.';
if (p == 1) c='+';
if (p == 2) c='*';
if (p == 3) c='\n';
BIO_write(cb->arg,&c,1);
(void)BIO_flush(cb->arg);
#ifdef LINT
p=n;
#endif
return 1;
}
#endif