From 826a42a0888624780f6758df1282288cb34a570f Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Tue, 16 Nov 2004 17:30:59 +0000 Subject: [PATCH] PR: 910 Add command line options -certform, -keyform and -pass to s_client and s_server. This supports the use of alternative passphrase sources, key formats and keys handled by an ENGINE. Update docs. --- CHANGES | 5 ++ apps/s_apps.h | 1 + apps/s_cb.c | 26 ++++++++++ apps/s_client.c | 57 +++++++++++++++++++- apps/s_server.c | 117 ++++++++++++++++++++++++++++++++++++++++-- doc/apps/s_client.pod | 16 ++++++ doc/apps/s_server.pod | 23 +++++++++ 7 files changed, 238 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index caee856df0..ef77f885fd 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,11 @@ Changes between 0.9.7e and 0.9.8 [xx XXX xxxx] + *) New arguments -certform, -keyform and -pass for s_client and s_server + to allow alternative format key and certificate files and passphrase + sources. + [Steve Henson] + *) New structure X509_VERIFY_PARAM which combines current verify parameters, update associated structures and add various utility functions. diff --git a/apps/s_apps.h b/apps/s_apps.h index b3919b4690..4e989b819c 100644 --- a/apps/s_apps.h +++ b/apps/s_apps.h @@ -154,6 +154,7 @@ int MS_CALLBACK verify_callback(int ok, X509_STORE_CTX *ctx); #endif #ifdef HEADER_SSL_H int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file); +int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key); #endif int init_client(int *sock, char *server, int port); int should_retry(int i); diff --git a/apps/s_cb.c b/apps/s_cb.c index 65cd0ff76b..21aab25d7e 100644 --- a/apps/s_cb.c +++ b/apps/s_cb.c @@ -229,6 +229,32 @@ int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file) return(1); } +int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key) + { + if (SSL_CTX_use_certificate(ctx,cert) <= 0) + { + BIO_printf(bio_err,"error setting certificate\n"); + ERR_print_errors(bio_err); + return 0; + } + if (SSL_CTX_use_PrivateKey(ctx,key) <= 0) + { + BIO_printf(bio_err,"error setting private key\n"); + ERR_print_errors(bio_err); + return 0; + } + + + /* Now we know that a key and cert have been set against + * the SSL context */ + if (!SSL_CTX_check_private_key(ctx)) + { + BIO_printf(bio_err,"Private key does not match the certificate public key\n"); + return 0; + } + return 1; + } + long MS_CALLBACK bio_dump_callback(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret) { diff --git a/apps/s_client.c b/apps/s_client.c index 603d8ce6b1..0204b517b1 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -189,8 +189,11 @@ static void sc_usage(void) BIO_printf(bio_err," -verify arg - turn on peer certificate verification\n"); BIO_printf(bio_err," -cert arg - certificate file to use, PEM format assumed\n"); - BIO_printf(bio_err," -key arg - Private key file to use, PEM format assumed, in cert file if\n"); + BIO_printf(bio_err," -certform arg - certificate format (PEM or DER) PEM default\n"); + BIO_printf(bio_err," -key arg - Private key file to use, in cert file if\n"); BIO_printf(bio_err," not specified but cert file is.\n"); + BIO_printf(bio_err," -keyform arg - key format (PEM or DER) PEM default\n"); + BIO_printf(bio_err," -pass arg - private key file pass phrase source\n"); BIO_printf(bio_err," -CApath arg - PEM format directory of CA's\n"); BIO_printf(bio_err," -CAfile arg - PEM format file of CA's\n"); BIO_printf(bio_err," -reconnect - Drop and re-make the connection with the same Session-ID\n"); @@ -241,6 +244,10 @@ int MAIN(int argc, char **argv) int full_log=1; char *host=SSL_HOST_NAME; char *cert_file=NULL,*key_file=NULL; + int cert_format = FORMAT_PEM, key_format = FORMAT_PEM; + char *passarg = NULL, *pass = NULL; + X509 *cert = NULL; + EVP_PKEY *key = NULL; char *CApath=NULL,*CAfile=NULL,*cipher=NULL; int reconnect=0,badop=0,verify=SSL_VERIFY_NONE,bugs=0; int crlf=0; @@ -329,6 +336,11 @@ int MAIN(int argc, char **argv) if (--argc < 1) goto bad; cert_file= *(++argv); } + else if (strcmp(*argv,"-certform") == 0) + { + if (--argc < 1) goto bad; + cert_format = str2fmt(*(++argv)); + } else if (strcmp(*argv,"-crl_check") == 0) vflags |= X509_V_FLAG_CRL_CHECK; else if (strcmp(*argv,"-crl_check_all") == 0) @@ -370,6 +382,16 @@ int MAIN(int argc, char **argv) #endif else if (strcmp(*argv,"-bugs") == 0) bugs=1; + else if (strcmp(*argv,"-keyform") == 0) + { + if (--argc < 1) goto bad; + key_format = str2fmt(*(++argv)); + } + else if (strcmp(*argv,"-pass") == 0) + { + if (--argc < 1) goto bad; + passarg = *(++argv); + } else if (strcmp(*argv,"-key") == 0) { if (--argc < 1) goto bad; @@ -451,6 +473,31 @@ bad: #ifndef OPENSSL_NO_ENGINE e = setup_engine(bio_err, engine_id, 1); #endif + if (!app_passwd(bio_err, passarg, NULL, &pass, NULL)) + { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + if (key_file == NULL) + key_file = cert_file; + + key = load_key(bio_err, key_file, key_format, 0, pass, e, + "client certificate private key file"); + if (!key) + { + ERR_print_errors(bio_err); + goto end; + } + + cert = load_cert(bio_err,cert_file,cert_format, + NULL, e, "client certificate file"); + + if (!cert) + { + ERR_print_errors(bio_err); + goto end; + } if (!app_RAND_load_file(NULL, bio_err, 1) && inrand == NULL && !RAND_status()) @@ -499,7 +546,7 @@ bad: #endif SSL_CTX_set_verify(ctx,verify,verify_callback); - if (!set_cert_stuff(ctx,cert_file,key_file)) + if (!set_cert_key_stuff(ctx,cert,key)) goto end; if ((!SSL_CTX_load_verify_locations(ctx,CAfile,CApath)) || @@ -932,6 +979,12 @@ end: if (con != NULL) SSL_free(con); if (con2 != NULL) SSL_free(con2); if (ctx != NULL) SSL_CTX_free(ctx); + if (cert) + X509_free(cert); + if (key) + EVP_PKEY_free(key); + if (pass) + OPENSSL_free(pass); if (cbuf != NULL) { OPENSSL_cleanse(cbuf,BUFSIZZ); OPENSSL_free(cbuf); } if (sbuf != NULL) { OPENSSL_cleanse(sbuf,BUFSIZZ); OPENSSL_free(sbuf); } if (mbuf != NULL) { OPENSSL_cleanse(mbuf,BUFSIZZ); OPENSSL_free(mbuf); } diff --git a/apps/s_server.c b/apps/s_server.c index cce835c239..0f102ce7cb 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -296,12 +296,18 @@ static void sv_usage(void) BIO_printf(bio_err," -context arg - set session ID context\n"); BIO_printf(bio_err," -verify arg - turn on peer certificate verification\n"); BIO_printf(bio_err," -Verify arg - turn on peer certificate verification, must have a cert.\n"); - BIO_printf(bio_err," -cert arg - certificate file to use, PEM format assumed\n"); + BIO_printf(bio_err," -cert arg - certificate file to use\n"); BIO_printf(bio_err," (default is %s)\n",TEST_CERT); - BIO_printf(bio_err," -key arg - Private Key file to use, PEM format assumed, in cert file if\n"); + BIO_printf(bio_err," -certform arg - certificate format (PEM or DER) PEM default\n"); + BIO_printf(bio_err," -key arg - Private Key file to use, in cert file if\n"); BIO_printf(bio_err," not specified (default is %s)\n",TEST_CERT); + BIO_printf(bio_err," -keyform arg - key format (PEM, DER or ENGINE) PEM default\n"); + BIO_printf(bio_err," -pass arg - private key file pass phrase source\n"); BIO_printf(bio_err," -dcert arg - second certificate file to use (usually for DSA)\n"); + BIO_printf(bio_err," -dcertform x - second certificate format (PEM or DER) PEM default\n"); BIO_printf(bio_err," -dkey arg - second private key file to use (usually for DSA)\n"); + BIO_printf(bio_err," -dkeyform arg - second key format (PEM, DER or ENGINE) PEM default\n"); + BIO_printf(bio_err," -dpass arg - second private key file pass phrase source\n"); BIO_printf(bio_err," -dhparam arg - DH parameter file to use, in cert file if not specified\n"); BIO_printf(bio_err," or a default set of parameters is used\n"); #ifndef OPENSSL_NO_ECDH @@ -522,6 +528,12 @@ int MAIN(int argc, char *argv[]) ENGINE *e=NULL; #endif char *inrand=NULL; + int s_cert_format = FORMAT_PEM, s_key_format = FORMAT_PEM; + char *passarg = NULL, *pass = NULL; + char *dpassarg = NULL, *dpass = NULL; + int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM; + X509 *s_cert = NULL, *s_dcert = NULL; + EVP_PKEY *s_key = NULL, *s_dkey = NULL; #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) meth=SSLv23_server_method(); @@ -588,11 +600,26 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; s_cert_file= *(++argv); } + else if (strcmp(*argv,"-certform") == 0) + { + if (--argc < 1) goto bad; + s_cert_format = str2fmt(*(++argv)); + } else if (strcmp(*argv,"-key") == 0) { if (--argc < 1) goto bad; s_key_file= *(++argv); } + else if (strcmp(*argv,"-keyform") == 0) + { + if (--argc < 1) goto bad; + s_key_format = str2fmt(*(++argv)); + } + else if (strcmp(*argv,"-pass") == 0) + { + if (--argc < 1) goto bad; + passarg = *(++argv); + } else if (strcmp(*argv,"-dhparam") == 0) { if (--argc < 1) goto bad; @@ -605,11 +632,26 @@ int MAIN(int argc, char *argv[]) named_curve = *(++argv); } #endif + else if (strcmp(*argv,"-dcertform") == 0) + { + if (--argc < 1) goto bad; + s_dcert_format = str2fmt(*(++argv)); + } else if (strcmp(*argv,"-dcert") == 0) { if (--argc < 1) goto bad; s_dcert_file= *(++argv); } + else if (strcmp(*argv,"-dkeyform") == 0) + { + if (--argc < 1) goto bad; + s_dkey_format = str2fmt(*(++argv)); + } + else if (strcmp(*argv,"-dpass") == 0) + { + if (--argc < 1) goto bad; + dpassarg = *(++argv); + } else if (strcmp(*argv,"-dkey") == 0) { if (--argc < 1) goto bad; @@ -739,6 +781,59 @@ bad: e = setup_engine(bio_err, engine_id, 1); #endif + if (!app_passwd(bio_err, passarg, dpassarg, &pass, &dpass)) + { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + + if (s_key_file == NULL) + s_key_file = s_cert_file; + + s_key = load_key(bio_err, s_key_file, s_key_format, 0, pass, e, + "server certificate private key file"); + if (!s_key) + { + ERR_print_errors(bio_err); + goto end; + } + + s_cert = load_cert(bio_err,s_cert_file,s_cert_format, + NULL, e, "server certificate file"); + + if (!s_cert) + { + ERR_print_errors(bio_err); + goto end; + } + + if (s_dcert_file) + { + + if (s_dkey_file == NULL) + s_dkey_file = s_dcert_file; + + s_dkey = load_key(bio_err, s_dkey_file, s_dkey_format, + 0, dpass, e, + "second certificate private key file"); + if (!s_dkey) + { + ERR_print_errors(bio_err); + goto end; + } + + s_dcert = load_cert(bio_err,s_dcert_file,s_dcert_format, + NULL, e, "second server certificate file"); + + if (!s_dcert) + { + ERR_print_errors(bio_err); + goto end; + } + + } + if (!app_RAND_load_file(NULL, bio_err, 1) && inrand == NULL && !RAND_status()) { @@ -903,11 +998,11 @@ bad: } #endif - if (!set_cert_stuff(ctx,s_cert_file,s_key_file)) + if (!set_cert_key_stuff(ctx,s_cert,s_key)) goto end; - if (s_dcert_file != NULL) + if (s_dcert != NULL) { - if (!set_cert_stuff(ctx,s_dcert_file,s_dkey_file)) + if (!set_cert_key_stuff(ctx,s_dcert,s_dkey)) goto end; } @@ -958,6 +1053,18 @@ bad: ret=0; end: if (ctx != NULL) SSL_CTX_free(ctx); + if (s_cert) + X509_free(s_cert); + if (s_dcert) + X509_free(s_dcert); + if (s_key) + EVP_PKEY_free(s_key); + if (s_dkey) + EVP_PKEY_free(s_dkey); + if (pass) + OPENSSL_free(pass); + if (dpass) + OPENSSL_free(dpass); if (bio_s_out != NULL) { BIO_free(bio_s_out); diff --git a/doc/apps/s_client.pod b/doc/apps/s_client.pod index 8d19079973..e1e1ba9865 100644 --- a/doc/apps/s_client.pod +++ b/doc/apps/s_client.pod @@ -11,7 +11,10 @@ B B [B<-connect host:port>] [B<-verify depth>] [B<-cert filename>] +[B<-certform DER|PEM>] [B<-key filename>] +[B<-keyform DER|PEM>] +[B<-pass arg>] [B<-CApath directory>] [B<-CAfile filename>] [B<-reconnect>] @@ -57,11 +60,24 @@ then an attempt is made to connect to the local host on port 4433. The certificate to use, if one is requested by the server. The default is not to use a certificate. +=item B<-certform format> + +The certificate format to use: DER or PEM. PEM is the default. + =item B<-key keyfile> The private key to use. If not specified then the certificate file will be used. +=item B<-keyform format> + +The private format to use: DER or PEM. PEM is the default. + +=item B<-pass arg> + +the private key password source. For more information about the format of B +see the B section in L. + =item B<-verify depth> The verify depth to use. This specifies the maximum length of the diff --git a/doc/apps/s_server.pod b/doc/apps/s_server.pod index 1d21921e47..7c1a9581d9 100644 --- a/doc/apps/s_server.pod +++ b/doc/apps/s_server.pod @@ -13,9 +13,15 @@ B B [B<-verify depth>] [B<-Verify depth>] [B<-cert filename>] +[B<-certform DER|PEM>] [B<-key keyfile>] +[B<-keyform DER|PEM>] +[B<-pass arg>] [B<-dcert filename>] +[B<-dcertform DER|PEM>] [B<-dkey keyfile>] +[B<-dkeyform DER|PEM>] +[B<-dpass arg>] [B<-dhparam filename>] [B<-nbio>] [B<-nbio_test>] @@ -70,11 +76,24 @@ certificate and some require a certificate with a certain public key type: for example the DSS cipher suites require a certificate containing a DSS (DSA) key. If not specified then the filename "server.pem" will be used. +=item B<-certform format> + +The certificate format to use: DER or PEM. PEM is the default. + =item B<-key keyfile> The private key to use. If not specified then the certificate file will be used. +=item B<-keyform format> + +The private format to use: DER or PEM. PEM is the default. + +=item B<-pass arg> + +the private key password source. For more information about the format of B +see the B section in L. + =item B<-dcert filename>, B<-dkey keyname> specify an additional certificate and private key, these behave in the @@ -86,6 +105,10 @@ and some a DSS (DSA) key. By using RSA and DSS certificates and keys a server can support clients which only support RSA or DSS cipher suites by using an appropriate certificate. +=item B<-dcertform format>, B<-dkeyform format>, B<-dpass arg> + +addtional certificate and private key format and passphrase respectively. + =item B<-nocert> if this option is set then no certificate is used. This restricts the -- 2.34.1