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