1 /* 2 * Copyright 1999-2023 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <time.h> 14 #include "apps.h" 15 #include "progs.h" 16 #include <openssl/bio.h> 17 #include <openssl/conf.h> 18 #include <openssl/err.h> 19 #include <openssl/evp.h> 20 #include <openssl/x509.h> 21 #include <openssl/pem.h> 22 23 typedef enum OPTION_choice { 24 OPT_COMMON, 25 OPT_NOOUT, 26 OPT_PUBKEY, 27 OPT_VERIFY, 28 OPT_IN, 29 OPT_OUT, 30 OPT_ENGINE, 31 OPT_KEY, 32 OPT_CHALLENGE, 33 OPT_PASSIN, 34 OPT_SPKAC, 35 OPT_SPKSECT, 36 OPT_KEYFORM, 37 OPT_DIGEST, 38 OPT_PROV_ENUM 39 } OPTION_CHOICE; 40 41 const OPTIONS spkac_options[] = { 42 OPT_SECTION("General"), 43 { "help", OPT_HELP, '-', "Display this summary" }, 44 { "spksect", OPT_SPKSECT, 's', 45 "Specify the name of an SPKAC-dedicated section of configuration" }, 46 #ifndef OPENSSL_NO_ENGINE 47 { "engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device" }, 48 #endif 49 50 OPT_SECTION("Input"), 51 { "in", OPT_IN, '<', "Input file" }, 52 { "key", OPT_KEY, '<', "Create SPKAC using private key" }, 53 { "keyform", OPT_KEYFORM, 'f', "Private key file format (ENGINE, other values ignored)" }, 54 { "passin", OPT_PASSIN, 's', "Input file pass phrase source" }, 55 { "challenge", OPT_CHALLENGE, 's', "Challenge string" }, 56 { "spkac", OPT_SPKAC, 's', "Alternative SPKAC name" }, 57 58 OPT_SECTION("Output"), 59 { "digest", OPT_DIGEST, 's', "Sign new SPKAC with the specified digest (default: MD5)" }, 60 { "out", OPT_OUT, '>', "Output file" }, 61 { "noout", OPT_NOOUT, '-', "Don't print SPKAC" }, 62 { "pubkey", OPT_PUBKEY, '-', "Output public key" }, 63 { "verify", OPT_VERIFY, '-', "Verify SPKAC signature" }, 64 65 OPT_PROV_OPTIONS, 66 { NULL } 67 }; 68 69 int spkac_main(int argc, char **argv) 70 { 71 BIO *out = NULL; 72 CONF *conf = NULL; 73 ENGINE *e = NULL; 74 EVP_PKEY *pkey = NULL; 75 NETSCAPE_SPKI *spki = NULL; 76 char *challenge = NULL, *keyfile = NULL; 77 char *infile = NULL, *outfile = NULL, *passinarg = NULL, *passin = NULL; 78 char *spkstr = NULL, *prog; 79 const char *spkac = "SPKAC", *spksect = "default"; 80 const char *digest = "MD5"; 81 EVP_MD *md = NULL; 82 int i, ret = 1, verify = 0, noout = 0, pubkey = 0; 83 int keyformat = FORMAT_UNDEF; 84 OPTION_CHOICE o; 85 86 prog = opt_init(argc, argv, spkac_options); 87 while ((o = opt_next()) != OPT_EOF) { 88 switch (o) { 89 case OPT_EOF: 90 case OPT_ERR: 91 opthelp: 92 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); 93 goto end; 94 case OPT_HELP: 95 opt_help(spkac_options); 96 ret = 0; 97 goto end; 98 case OPT_IN: 99 infile = opt_arg(); 100 break; 101 case OPT_OUT: 102 outfile = opt_arg(); 103 break; 104 case OPT_NOOUT: 105 noout = 1; 106 break; 107 case OPT_PUBKEY: 108 pubkey = 1; 109 break; 110 case OPT_VERIFY: 111 verify = 1; 112 break; 113 case OPT_PASSIN: 114 passinarg = opt_arg(); 115 break; 116 case OPT_KEY: 117 keyfile = opt_arg(); 118 break; 119 case OPT_KEYFORM: 120 if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyformat)) 121 goto opthelp; 122 break; 123 case OPT_CHALLENGE: 124 challenge = opt_arg(); 125 break; 126 case OPT_SPKAC: 127 spkac = opt_arg(); 128 break; 129 case OPT_SPKSECT: 130 spksect = opt_arg(); 131 break; 132 case OPT_DIGEST: 133 digest = opt_arg(); 134 break; 135 case OPT_ENGINE: 136 e = setup_engine(opt_arg(), 0); 137 break; 138 case OPT_PROV_CASES: 139 if (!opt_provider(o)) 140 goto end; 141 break; 142 } 143 } 144 145 /* No extra arguments. */ 146 if (!opt_check_rest_arg(NULL)) 147 goto opthelp; 148 149 if (!app_passwd(passinarg, NULL, &passin, NULL)) { 150 BIO_printf(bio_err, "Error getting password\n"); 151 goto end; 152 } 153 154 if (keyfile != NULL) { 155 if (!opt_md(digest, &md)) 156 goto end; 157 158 pkey = load_key(strcmp(keyfile, "-") ? keyfile : NULL, 159 keyformat, 1, passin, e, "private key"); 160 if (pkey == NULL) 161 goto end; 162 spki = NETSCAPE_SPKI_new(); 163 if (spki == NULL) 164 goto end; 165 if (challenge != NULL 166 && !ASN1_STRING_set(spki->spkac->challenge, 167 challenge, (int)strlen(challenge))) 168 goto end; 169 if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) { 170 BIO_printf(bio_err, "Error setting public key\n"); 171 goto end; 172 } 173 i = NETSCAPE_SPKI_sign(spki, pkey, md); 174 if (i <= 0) { 175 BIO_printf(bio_err, "Error signing SPKAC\n"); 176 goto end; 177 } 178 spkstr = NETSCAPE_SPKI_b64_encode(spki); 179 if (spkstr == NULL) 180 goto end; 181 182 out = bio_open_default(outfile, 'w', FORMAT_TEXT); 183 if (out == NULL) { 184 OPENSSL_free(spkstr); 185 goto end; 186 } 187 BIO_printf(out, "SPKAC=%s\n", spkstr); 188 OPENSSL_free(spkstr); 189 ret = 0; 190 goto end; 191 } 192 193 if ((conf = app_load_config(infile)) == NULL) 194 goto end; 195 196 spkstr = NCONF_get_string(conf, spksect, spkac); 197 198 if (spkstr == NULL) { 199 BIO_printf(bio_err, "Can't find SPKAC called \"%s\"\n", spkac); 200 ERR_print_errors(bio_err); 201 goto end; 202 } 203 204 spki = NETSCAPE_SPKI_b64_decode(spkstr, -1); 205 206 if (spki == NULL) { 207 BIO_printf(bio_err, "Error loading SPKAC\n"); 208 ERR_print_errors(bio_err); 209 goto end; 210 } 211 212 out = bio_open_default(outfile, 'w', FORMAT_TEXT); 213 if (out == NULL) 214 goto end; 215 216 if (!noout) 217 NETSCAPE_SPKI_print(out, spki); 218 pkey = NETSCAPE_SPKI_get_pubkey(spki); 219 if (verify) { 220 i = NETSCAPE_SPKI_verify(spki, pkey); 221 if (i > 0) { 222 BIO_printf(bio_err, "Signature OK\n"); 223 } else { 224 BIO_printf(bio_err, "Signature Failure\n"); 225 ERR_print_errors(bio_err); 226 goto end; 227 } 228 } 229 if (pubkey) 230 PEM_write_bio_PUBKEY(out, pkey); 231 232 ret = 0; 233 234 end: 235 EVP_MD_free(md); 236 NCONF_free(conf); 237 NETSCAPE_SPKI_free(spki); 238 BIO_free_all(out); 239 EVP_PKEY_free(pkey); 240 release_engine(e); 241 OPENSSL_free(passin); 242 return ret; 243 } 244