Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 1 | /* |
Matt Caswell | aff636a | 2021-05-06 13:03:23 +0100 | [diff] [blame] | 2 | * Copyright 1999-2021 The OpenSSL Project Authors. All Rights Reserved. |
Rich Salz | 846e33c | 2016-05-17 14:18:30 -0400 | [diff] [blame] | 3 | * |
Richard Levitte | dffa752 | 2018-12-06 13:00:26 +0100 | [diff] [blame] | 4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
Rich Salz | 846e33c | 2016-05-17 14:18:30 -0400 | [diff] [blame] | 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 |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 8 | */ |
Rich Salz | 846e33c | 2016-05-17 14:18:30 -0400 | [diff] [blame] | 9 | |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 10 | #include <stdio.h> |
| 11 | #include <stdlib.h> |
| 12 | #include <string.h> |
| 13 | #include <time.h> |
| 14 | #include "apps.h" |
Richard Levitte | dab2cd6 | 2018-01-31 11:13:10 +0100 | [diff] [blame] | 15 | #include "progs.h" |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 16 | #include <openssl/bio.h> |
Bodo Möller | de83c12 | 2000-03-05 01:11:44 +0000 | [diff] [blame] | 17 | #include <openssl/conf.h> |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 18 | #include <openssl/err.h> |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 19 | #include <openssl/evp.h> |
| 20 | #include <openssl/x509.h> |
| 21 | #include <openssl/pem.h> |
| 22 | |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 23 | typedef enum OPTION_choice { |
Dr. David von Oheimb | b0f9601 | 2021-05-01 15:29:00 +0200 | [diff] [blame] | 24 | OPT_COMMON, |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 25 | OPT_NOOUT, OPT_PUBKEY, OPT_VERIFY, OPT_IN, OPT_OUT, |
| 26 | OPT_ENGINE, OPT_KEY, OPT_CHALLENGE, OPT_PASSIN, OPT_SPKAC, |
Pauli | e1a77f9 | 2021-06-10 10:06:20 +1000 | [diff] [blame] | 27 | OPT_SPKSECT, OPT_KEYFORM, OPT_DIGEST, |
Pauli | 6bd4e3f | 2020-02-25 14:29:30 +1000 | [diff] [blame] | 28 | OPT_PROV_ENUM |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 29 | } OPTION_CHOICE; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 30 | |
FdaSilvaYY | 44c83eb | 2016-03-13 14:07:50 +0100 | [diff] [blame] | 31 | const OPTIONS spkac_options[] = { |
Rich Salz | 5388f98 | 2019-11-08 06:08:30 +1000 | [diff] [blame] | 32 | OPT_SECTION("General"), |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 33 | {"help", OPT_HELP, '-', "Display this summary"}, |
FdaSilvaYY | 12d56b2 | 2016-07-31 19:02:50 +0200 | [diff] [blame] | 34 | {"spksect", OPT_SPKSECT, 's', |
| 35 | "Specify the name of an SPKAC-dedicated section of configuration"}, |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 36 | #ifndef OPENSSL_NO_ENGINE |
| 37 | {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, |
| 38 | #endif |
Rich Salz | 5388f98 | 2019-11-08 06:08:30 +1000 | [diff] [blame] | 39 | |
| 40 | OPT_SECTION("Input"), |
| 41 | {"in", OPT_IN, '<', "Input file"}, |
| 42 | {"key", OPT_KEY, '<', "Create SPKAC using private key"}, |
Dr. David von Oheimb | 6d382c7 | 2020-05-06 13:51:50 +0200 | [diff] [blame] | 43 | {"keyform", OPT_KEYFORM, 'f', "Private key file format (ENGINE, other values ignored)"}, |
Rich Salz | 5388f98 | 2019-11-08 06:08:30 +1000 | [diff] [blame] | 44 | {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, |
| 45 | {"challenge", OPT_CHALLENGE, 's', "Challenge string"}, |
| 46 | {"spkac", OPT_SPKAC, 's', "Alternative SPKAC name"}, |
| 47 | |
| 48 | OPT_SECTION("Output"), |
Pauli | e1a77f9 | 2021-06-10 10:06:20 +1000 | [diff] [blame] | 49 | {"digest", OPT_DIGEST, 's', "Sign new SPKAC with the specified digest (default: MD5)" }, |
Rich Salz | 5388f98 | 2019-11-08 06:08:30 +1000 | [diff] [blame] | 50 | {"out", OPT_OUT, '>', "Output file"}, |
| 51 | {"noout", OPT_NOOUT, '-', "Don't print SPKAC"}, |
| 52 | {"pubkey", OPT_PUBKEY, '-', "Output public key"}, |
| 53 | {"verify", OPT_VERIFY, '-', "Verify SPKAC signature"}, |
Pauli | 6bd4e3f | 2020-02-25 14:29:30 +1000 | [diff] [blame] | 54 | |
| 55 | OPT_PROV_OPTIONS, |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 56 | {NULL} |
| 57 | }; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 58 | |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 59 | int spkac_main(int argc, char **argv) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 60 | { |
Rich Salz | cc01d21 | 2015-05-28 13:52:55 -0400 | [diff] [blame] | 61 | BIO *out = NULL; |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 62 | CONF *conf = NULL; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 63 | ENGINE *e = NULL; |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 64 | EVP_PKEY *pkey = NULL; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 65 | NETSCAPE_SPKI *spki = NULL; |
Rich Salz | 333b070 | 2015-04-25 15:41:29 -0400 | [diff] [blame] | 66 | char *challenge = NULL, *keyfile = NULL; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 67 | char *infile = NULL, *outfile = NULL, *passinarg = NULL, *passin = NULL; |
| 68 | char *spkstr = NULL, *prog; |
| 69 | const char *spkac = "SPKAC", *spksect = "default"; |
Pauli | e1a77f9 | 2021-06-10 10:06:20 +1000 | [diff] [blame] | 70 | const char *digest = "MD5"; |
| 71 | EVP_MD *md = NULL; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 72 | int i, ret = 1, verify = 0, noout = 0, pubkey = 0; |
Tomas Mraz | d382e79 | 2021-04-30 16:57:53 +0200 | [diff] [blame] | 73 | int keyformat = FORMAT_UNDEF; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 74 | OPTION_CHOICE o; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 75 | |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 76 | prog = opt_init(argc, argv, spkac_options); |
| 77 | while ((o = opt_next()) != OPT_EOF) { |
| 78 | switch (o) { |
| 79 | case OPT_EOF: |
| 80 | case OPT_ERR: |
Kurt Roeckx | 0335851 | 2016-02-14 20:45:02 +0100 | [diff] [blame] | 81 | opthelp: |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 82 | BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); |
| 83 | goto end; |
| 84 | case OPT_HELP: |
| 85 | opt_help(spkac_options); |
| 86 | ret = 0; |
| 87 | goto end; |
| 88 | case OPT_IN: |
| 89 | infile = opt_arg(); |
| 90 | break; |
| 91 | case OPT_OUT: |
| 92 | outfile = opt_arg(); |
| 93 | break; |
| 94 | case OPT_NOOUT: |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 95 | noout = 1; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 96 | break; |
| 97 | case OPT_PUBKEY: |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 98 | pubkey = 1; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 99 | break; |
| 100 | case OPT_VERIFY: |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 101 | verify = 1; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 102 | break; |
| 103 | case OPT_PASSIN: |
| 104 | passinarg = opt_arg(); |
| 105 | break; |
| 106 | case OPT_KEY: |
| 107 | keyfile = opt_arg(); |
| 108 | break; |
Luke Faraone | 66e5970 | 2017-05-15 18:23:17 -0700 | [diff] [blame] | 109 | case OPT_KEYFORM: |
| 110 | if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyformat)) |
| 111 | goto opthelp; |
Dr. Matthias St. Pierre | 32c6985 | 2018-04-26 13:57:14 +0200 | [diff] [blame] | 112 | break; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 113 | case OPT_CHALLENGE: |
| 114 | challenge = opt_arg(); |
| 115 | break; |
| 116 | case OPT_SPKAC: |
| 117 | spkac = opt_arg(); |
| 118 | break; |
| 119 | case OPT_SPKSECT: |
| 120 | spksect = opt_arg(); |
| 121 | break; |
Pauli | e1a77f9 | 2021-06-10 10:06:20 +1000 | [diff] [blame] | 122 | case OPT_DIGEST: |
| 123 | digest = opt_arg(); |
| 124 | break; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 125 | case OPT_ENGINE: |
Rich Salz | 333b070 | 2015-04-25 15:41:29 -0400 | [diff] [blame] | 126 | e = setup_engine(opt_arg(), 0); |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 127 | break; |
Pauli | 6bd4e3f | 2020-02-25 14:29:30 +1000 | [diff] [blame] | 128 | case OPT_PROV_CASES: |
| 129 | if (!opt_provider(o)) |
| 130 | goto end; |
| 131 | break; |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 132 | } |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 133 | } |
Rich Salz | 021410e | 2020-11-28 16:12:58 -0500 | [diff] [blame] | 134 | |
| 135 | /* No extra arguments. */ |
Dr. David von Oheimb | d9f0735 | 2021-08-27 15:33:18 +0200 | [diff] [blame] | 136 | if (!opt_check_rest_arg(NULL)) |
Kurt Roeckx | 0335851 | 2016-02-14 20:45:02 +0100 | [diff] [blame] | 137 | goto opthelp; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 138 | |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 139 | if (!app_passwd(passinarg, NULL, &passin, NULL)) { |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 140 | BIO_printf(bio_err, "Error getting password\n"); |
| 141 | goto end; |
| 142 | } |
Richard Levitte | 5270e70 | 2000-10-26 21:07:28 +0000 | [diff] [blame] | 143 | |
Paul Yang | f2582f0 | 2017-06-10 02:22:22 +0800 | [diff] [blame] | 144 | if (keyfile != NULL) { |
Pauli | e1a77f9 | 2021-06-10 10:06:20 +1000 | [diff] [blame] | 145 | if (!opt_md(digest, &md)) |
| 146 | goto end; |
| 147 | |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 148 | pkey = load_key(strcmp(keyfile, "-") ? keyfile : NULL, |
Luke Faraone | 66e5970 | 2017-05-15 18:23:17 -0700 | [diff] [blame] | 149 | keyformat, 1, passin, e, "private key"); |
Paul Yang | f2582f0 | 2017-06-10 02:22:22 +0800 | [diff] [blame] | 150 | if (pkey == NULL) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 151 | goto end; |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 152 | spki = NETSCAPE_SPKI_new(); |
Paul Yang | f2582f0 | 2017-06-10 02:22:22 +0800 | [diff] [blame] | 153 | if (spki == NULL) |
| 154 | goto end; |
| 155 | if (challenge != NULL) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 156 | ASN1_STRING_set(spki->spkac->challenge, |
| 157 | challenge, (int)strlen(challenge)); |
Vladimir Panteleev | 8293fb6 | 2020-03-03 18:04:00 +0000 | [diff] [blame] | 158 | if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) { |
| 159 | BIO_printf(bio_err, "Error setting public key\n"); |
| 160 | goto end; |
| 161 | } |
Pauli | e1a77f9 | 2021-06-10 10:06:20 +1000 | [diff] [blame] | 162 | i = NETSCAPE_SPKI_sign(spki, pkey, md); |
Vladimir Panteleev | 8293fb6 | 2020-03-03 18:04:00 +0000 | [diff] [blame] | 163 | if (i <= 0) { |
| 164 | BIO_printf(bio_err, "Error signing SPKAC\n"); |
| 165 | goto end; |
| 166 | } |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 167 | spkstr = NETSCAPE_SPKI_b64_encode(spki); |
Paul Yang | f2582f0 | 2017-06-10 02:22:22 +0800 | [diff] [blame] | 168 | if (spkstr == NULL) |
| 169 | goto end; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 170 | |
Richard Levitte | bdd58d9 | 2015-09-04 12:49:06 +0200 | [diff] [blame] | 171 | out = bio_open_default(outfile, 'w', FORMAT_TEXT); |
Matt Caswell | 08f6ae5 | 2016-08-24 11:22:47 +0100 | [diff] [blame] | 172 | if (out == NULL) { |
| 173 | OPENSSL_free(spkstr); |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 174 | goto end; |
Matt Caswell | 08f6ae5 | 2016-08-24 11:22:47 +0100 | [diff] [blame] | 175 | } |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 176 | BIO_printf(out, "SPKAC=%s\n", spkstr); |
| 177 | OPENSSL_free(spkstr); |
| 178 | ret = 0; |
| 179 | goto end; |
| 180 | } |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 181 | |
Rich Salz | cc01d21 | 2015-05-28 13:52:55 -0400 | [diff] [blame] | 182 | if ((conf = app_load_config(infile)) == NULL) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 183 | goto end; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 184 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 185 | spkstr = NCONF_get_string(conf, spksect, spkac); |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 186 | |
Matt Caswell | 96487cd | 2015-10-30 11:18:04 +0000 | [diff] [blame] | 187 | if (spkstr == NULL) { |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 188 | BIO_printf(bio_err, "Can't find SPKAC called \"%s\"\n", spkac); |
| 189 | ERR_print_errors(bio_err); |
| 190 | goto end; |
| 191 | } |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 192 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 193 | spki = NETSCAPE_SPKI_b64_decode(spkstr, -1); |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 194 | |
Paul Yang | f2582f0 | 2017-06-10 02:22:22 +0800 | [diff] [blame] | 195 | if (spki == NULL) { |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 196 | BIO_printf(bio_err, "Error loading SPKAC\n"); |
| 197 | ERR_print_errors(bio_err); |
| 198 | goto end; |
| 199 | } |
| 200 | |
Richard Levitte | bdd58d9 | 2015-09-04 12:49:06 +0200 | [diff] [blame] | 201 | out = bio_open_default(outfile, 'w', FORMAT_TEXT); |
Rich Salz | 7e1b748 | 2015-04-24 15:26:15 -0400 | [diff] [blame] | 202 | if (out == NULL) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 203 | goto end; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 204 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 205 | if (!noout) |
| 206 | NETSCAPE_SPKI_print(out, spki); |
| 207 | pkey = NETSCAPE_SPKI_get_pubkey(spki); |
| 208 | if (verify) { |
| 209 | i = NETSCAPE_SPKI_verify(spki, pkey); |
Paul Yang | f2582f0 | 2017-06-10 02:22:22 +0800 | [diff] [blame] | 210 | if (i > 0) { |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 211 | BIO_printf(bio_err, "Signature OK\n"); |
Paul Yang | f2582f0 | 2017-06-10 02:22:22 +0800 | [diff] [blame] | 212 | } else { |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 213 | BIO_printf(bio_err, "Signature Failure\n"); |
| 214 | ERR_print_errors(bio_err); |
| 215 | goto end; |
| 216 | } |
| 217 | } |
| 218 | if (pubkey) |
| 219 | PEM_write_bio_PUBKEY(out, pkey); |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 220 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 221 | ret = 0; |
Dr. Stephen Henson | 8ce9716 | 1999-09-03 01:08:34 +0000 | [diff] [blame] | 222 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 223 | end: |
Pauli | e1a77f9 | 2021-06-10 10:06:20 +1000 | [diff] [blame] | 224 | EVP_MD_free(md); |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 225 | NCONF_free(conf); |
| 226 | NETSCAPE_SPKI_free(spki); |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 227 | BIO_free_all(out); |
| 228 | EVP_PKEY_free(pkey); |
Richard Levitte | dd1abd4 | 2016-09-28 23:39:18 +0200 | [diff] [blame] | 229 | release_engine(e); |
Rich Salz | b548a1f | 2015-05-01 10:02:07 -0400 | [diff] [blame] | 230 | OPENSSL_free(passin); |
KaoruToda | 26a7d93 | 2017-10-17 23:04:09 +0900 | [diff] [blame] | 231 | return ret; |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 232 | } |