1*b0100c00Stb /* $OpenBSD: pkcs8.c,v 1.18 2025/01/02 12:31:44 tb Exp $ */ 2dab3f910Sjsing /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 3dab3f910Sjsing * project 1999-2004. 4dab3f910Sjsing */ 5dab3f910Sjsing /* ==================================================================== 6dab3f910Sjsing * Copyright (c) 1999 The OpenSSL Project. All rights reserved. 7dab3f910Sjsing * 8dab3f910Sjsing * Redistribution and use in source and binary forms, with or without 9dab3f910Sjsing * modification, are permitted provided that the following conditions 10dab3f910Sjsing * are met: 11dab3f910Sjsing * 12dab3f910Sjsing * 1. Redistributions of source code must retain the above copyright 13dab3f910Sjsing * notice, this list of conditions and the following disclaimer. 14dab3f910Sjsing * 15dab3f910Sjsing * 2. Redistributions in binary form must reproduce the above copyright 16dab3f910Sjsing * notice, this list of conditions and the following disclaimer in 17dab3f910Sjsing * the documentation and/or other materials provided with the 18dab3f910Sjsing * distribution. 19dab3f910Sjsing * 20dab3f910Sjsing * 3. All advertising materials mentioning features or use of this 21dab3f910Sjsing * software must display the following acknowledgment: 22dab3f910Sjsing * "This product includes software developed by the OpenSSL Project 23dab3f910Sjsing * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 24dab3f910Sjsing * 25dab3f910Sjsing * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 26dab3f910Sjsing * endorse or promote products derived from this software without 27dab3f910Sjsing * prior written permission. For written permission, please contact 28dab3f910Sjsing * licensing@OpenSSL.org. 29dab3f910Sjsing * 30dab3f910Sjsing * 5. Products derived from this software may not be called "OpenSSL" 31dab3f910Sjsing * nor may "OpenSSL" appear in their names without prior written 32dab3f910Sjsing * permission of the OpenSSL Project. 33dab3f910Sjsing * 34dab3f910Sjsing * 6. Redistributions of any form whatsoever must retain the following 35dab3f910Sjsing * acknowledgment: 36dab3f910Sjsing * "This product includes software developed by the OpenSSL Project 37dab3f910Sjsing * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 38dab3f910Sjsing * 39dab3f910Sjsing * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 40dab3f910Sjsing * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41dab3f910Sjsing * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42dab3f910Sjsing * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 43dab3f910Sjsing * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44dab3f910Sjsing * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45dab3f910Sjsing * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46dab3f910Sjsing * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47dab3f910Sjsing * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 48dab3f910Sjsing * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 49dab3f910Sjsing * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 50dab3f910Sjsing * OF THE POSSIBILITY OF SUCH DAMAGE. 51dab3f910Sjsing * ==================================================================== 52dab3f910Sjsing * 53dab3f910Sjsing * This product includes cryptographic software written by Eric Young 54dab3f910Sjsing * (eay@cryptsoft.com). This product includes software written by Tim 55dab3f910Sjsing * Hudson (tjh@cryptsoft.com). 56dab3f910Sjsing * 57dab3f910Sjsing */ 58dab3f910Sjsing 59dab3f910Sjsing #include <stdio.h> 60dab3f910Sjsing #include <string.h> 61dab3f910Sjsing 62dab3f910Sjsing #include "apps.h" 63dab3f910Sjsing 64dab3f910Sjsing #include <openssl/err.h> 65dab3f910Sjsing #include <openssl/evp.h> 66dab3f910Sjsing #include <openssl/pem.h> 67dab3f910Sjsing #include <openssl/pkcs12.h> 68dab3f910Sjsing 69aca35b63Sdoug static struct { 70aca35b63Sdoug const EVP_CIPHER *cipher; 71aca35b63Sdoug char *infile; 72aca35b63Sdoug int informat; 73aca35b63Sdoug int iter; 74aca35b63Sdoug int nocrypt; 75aca35b63Sdoug char *outfile; 76aca35b63Sdoug int outformat; 77aca35b63Sdoug char *passargin; 78aca35b63Sdoug char *passargout; 79aca35b63Sdoug int pbe_nid; 80aca35b63Sdoug int topk8; 81e7718adaStb } cfg; 82aca35b63Sdoug 83aca35b63Sdoug static int 84aca35b63Sdoug pkcs8_opt_v1(char *arg) 85aca35b63Sdoug { 86e7718adaStb if ((cfg.pbe_nid = OBJ_txt2nid(arg)) == NID_undef) { 87aca35b63Sdoug fprintf(stderr, "Unknown PBE algorithm '%s'\n", arg); 88aca35b63Sdoug return (1); 89aca35b63Sdoug } 90aca35b63Sdoug 91aca35b63Sdoug return (0); 92aca35b63Sdoug } 93aca35b63Sdoug 94aca35b63Sdoug static int 95aca35b63Sdoug pkcs8_opt_v2(char *arg) 96aca35b63Sdoug { 97e7718adaStb if ((cfg.cipher = EVP_get_cipherbyname(arg)) == NULL) { 98aca35b63Sdoug fprintf(stderr, "Unknown cipher '%s'\n", arg); 99aca35b63Sdoug return (1); 100aca35b63Sdoug } 101aca35b63Sdoug 102aca35b63Sdoug return (0); 103aca35b63Sdoug } 104aca35b63Sdoug 105ea149709Sguenther static const struct option pkcs8_options[] = { 106aca35b63Sdoug { 107aca35b63Sdoug .name = "in", 108aca35b63Sdoug .argname = "file", 109aca35b63Sdoug .desc = "Input file (default stdin)", 110aca35b63Sdoug .type = OPTION_ARG, 111e7718adaStb .opt.arg = &cfg.infile, 112aca35b63Sdoug }, 113aca35b63Sdoug { 114aca35b63Sdoug .name = "inform", 1156d04dddaSjmc .argname = "der | pem", 1166d04dddaSjmc .desc = "Input format (default PEM)", 117aca35b63Sdoug .type = OPTION_ARG_FORMAT, 118e7718adaStb .opt.value = &cfg.informat, 119aca35b63Sdoug }, 120aca35b63Sdoug { 121aca35b63Sdoug .name = "nocrypt", 122aca35b63Sdoug .desc = "Use or expect unencrypted private key", 123aca35b63Sdoug .type = OPTION_FLAG, 124e7718adaStb .opt.flag = &cfg.nocrypt, 125aca35b63Sdoug }, 126aca35b63Sdoug { 127aca35b63Sdoug .name = "noiter", 128aca35b63Sdoug .desc = "Use 1 as iteration count", 129aca35b63Sdoug .type = OPTION_VALUE, 130aca35b63Sdoug .value = 1, 131e7718adaStb .opt.value = &cfg.iter, 132aca35b63Sdoug }, 133aca35b63Sdoug { 134aca35b63Sdoug .name = "out", 135aca35b63Sdoug .argname = "file", 136aca35b63Sdoug .desc = "Output file (default stdout)", 137aca35b63Sdoug .type = OPTION_ARG, 138e7718adaStb .opt.arg = &cfg.outfile, 139aca35b63Sdoug }, 140aca35b63Sdoug { 141aca35b63Sdoug .name = "outform", 1426d04dddaSjmc .argname = "der | pem", 1436d04dddaSjmc .desc = "Output format (default PEM)", 144aca35b63Sdoug .type = OPTION_ARG_FORMAT, 145e7718adaStb .opt.value = &cfg.outformat, 146aca35b63Sdoug }, 147aca35b63Sdoug { 148aca35b63Sdoug .name = "passin", 149aca35b63Sdoug .argname = "source", 150aca35b63Sdoug .desc = "Input file passphrase source", 151aca35b63Sdoug .type = OPTION_ARG, 152e7718adaStb .opt.arg = &cfg.passargin, 153aca35b63Sdoug }, 154aca35b63Sdoug { 155aca35b63Sdoug .name = "passout", 156aca35b63Sdoug .argname = "source", 157aca35b63Sdoug .desc = "Output file passphrase source", 158aca35b63Sdoug .type = OPTION_ARG, 159e7718adaStb .opt.arg = &cfg.passargout, 160aca35b63Sdoug }, 161aca35b63Sdoug { 162aca35b63Sdoug .name = "topk8", 163aca35b63Sdoug .desc = "Read traditional format key and write PKCS#8 format" 164aca35b63Sdoug " key", 165aca35b63Sdoug .type = OPTION_FLAG, 166e7718adaStb .opt.flag = &cfg.topk8, 167aca35b63Sdoug }, 168aca35b63Sdoug { 169aca35b63Sdoug .name = "v1", 170aca35b63Sdoug .argname = "algorithm", 171aca35b63Sdoug .desc = "Use PKCS#5 v1.5 or PKCS#12 with given algorithm", 172aca35b63Sdoug .type = OPTION_ARG_FUNC, 173aca35b63Sdoug .opt.argfunc = pkcs8_opt_v1, 174aca35b63Sdoug }, 175aca35b63Sdoug { 176aca35b63Sdoug .name = "v2", 177aca35b63Sdoug .argname = "cipher", 178aca35b63Sdoug .desc = "Use PKCS#5 v2.0 with given cipher", 179aca35b63Sdoug .type = OPTION_ARG_FUNC, 180aca35b63Sdoug .opt.argfunc = pkcs8_opt_v2, 181aca35b63Sdoug }, 182aca35b63Sdoug { NULL }, 183aca35b63Sdoug }; 184aca35b63Sdoug 185aca35b63Sdoug static void 186440d1414Stb pkcs8_usage(void) 187aca35b63Sdoug { 1886d04dddaSjmc fprintf(stderr, "usage: pkcs8 [-in file] [inform der | pem] " 1896d04dddaSjmc "[-nocrypt] [-noiter]\n" 1906d04dddaSjmc " [-out file] [-outform der | pem] [-passin arg]\n" 1916d04dddaSjmc " [-passout arg] [-topk8] [-v1 alg] [-v2 alg]\n\n"); 192aca35b63Sdoug options_usage(pkcs8_options); 193aca35b63Sdoug } 194dab3f910Sjsing 195dab3f910Sjsing int 196dab3f910Sjsing pkcs8_main(int argc, char **argv) 197dab3f910Sjsing { 198dab3f910Sjsing BIO *in = NULL, *out = NULL; 199dab3f910Sjsing X509_SIG *p8 = NULL; 200dab3f910Sjsing PKCS8_PRIV_KEY_INFO *p8inf = NULL; 201dab3f910Sjsing EVP_PKEY *pkey = NULL; 202dab3f910Sjsing char pass[50], *passin = NULL, *passout = NULL, *p8pass = NULL; 203dab3f910Sjsing int ret = 1; 204dab3f910Sjsing 20551811eadSderaadt if (pledge("stdio cpath wpath rpath tty", NULL) == -1) { 2069bc487adSdoug perror("pledge"); 207e370f0eeSdoug exit(1); 208e370f0eeSdoug } 2099bc487adSdoug 210e7718adaStb memset(&cfg, 0, sizeof(cfg)); 211dab3f910Sjsing 212e7718adaStb cfg.iter = PKCS12_DEFAULT_ITER; 213e7718adaStb cfg.informat = FORMAT_PEM; 214e7718adaStb cfg.outformat = FORMAT_PEM; 215e7718adaStb cfg.pbe_nid = -1; 216aca35b63Sdoug 217aca35b63Sdoug if (options_parse(argc, argv, pkcs8_options, NULL, NULL) != 0) { 218aca35b63Sdoug pkcs8_usage(); 219aca35b63Sdoug return (1); 220dab3f910Sjsing } 221dab3f910Sjsing 222e7718adaStb if (!app_passwd(bio_err, cfg.passargin, 223e7718adaStb cfg.passargout, &passin, &passout)) { 224dab3f910Sjsing BIO_printf(bio_err, "Error getting passwords\n"); 225dab3f910Sjsing goto end; 226dab3f910Sjsing } 227e7718adaStb if ((cfg.pbe_nid == -1) && !cfg.cipher) 228e7718adaStb cfg.pbe_nid = NID_pbeWithMD5AndDES_CBC; 229dab3f910Sjsing 230e7718adaStb if (cfg.infile) { 231e7718adaStb if (!(in = BIO_new_file(cfg.infile, "rb"))) { 232dab3f910Sjsing BIO_printf(bio_err, 233aca35b63Sdoug "Can't open input file '%s'\n", 234e7718adaStb cfg.infile); 235dab3f910Sjsing goto end; 236dab3f910Sjsing } 237dab3f910Sjsing } else 238dab3f910Sjsing in = BIO_new_fp(stdin, BIO_NOCLOSE); 239dab3f910Sjsing 240e7718adaStb if (cfg.outfile) { 241e7718adaStb if (!(out = BIO_new_file(cfg.outfile, "wb"))) { 242aca35b63Sdoug BIO_printf(bio_err, "Can't open output file '%s'\n", 243e7718adaStb cfg.outfile); 244dab3f910Sjsing goto end; 245dab3f910Sjsing } 246dab3f910Sjsing } else { 247dab3f910Sjsing out = BIO_new_fp(stdout, BIO_NOCLOSE); 248dab3f910Sjsing } 249e7718adaStb if (cfg.topk8) { 250e7718adaStb pkey = load_key(bio_err, cfg.infile, 251e7718adaStb cfg.informat, 1, passin, "key"); 252dab3f910Sjsing if (!pkey) 253dab3f910Sjsing goto end; 2548e223a04Stb if (!(p8inf = EVP_PKEY2PKCS8(pkey))) { 255dab3f910Sjsing BIO_printf(bio_err, "Error converting key\n"); 256dab3f910Sjsing ERR_print_errors(bio_err); 257dab3f910Sjsing goto end; 258dab3f910Sjsing } 259e7718adaStb if (cfg.nocrypt) { 260e7718adaStb if (cfg.outformat == FORMAT_PEM) 261dab3f910Sjsing PEM_write_bio_PKCS8_PRIV_KEY_INFO(out, p8inf); 262e7718adaStb else if (cfg.outformat == FORMAT_ASN1) 263dab3f910Sjsing i2d_PKCS8_PRIV_KEY_INFO_bio(out, p8inf); 264dab3f910Sjsing else { 265aca35b63Sdoug BIO_printf(bio_err, 266aca35b63Sdoug "Bad format specified for key\n"); 267dab3f910Sjsing goto end; 268dab3f910Sjsing } 269dab3f910Sjsing } else { 270dab3f910Sjsing if (passout) 271dab3f910Sjsing p8pass = passout; 272dab3f910Sjsing else { 273dab3f910Sjsing p8pass = pass; 274aca35b63Sdoug if (EVP_read_pw_string(pass, sizeof pass, 275aca35b63Sdoug "Enter Encryption Password:", 1)) 276dab3f910Sjsing goto end; 277dab3f910Sjsing } 278e7718adaStb if (!(p8 = PKCS8_encrypt(cfg.pbe_nid, 279e7718adaStb cfg.cipher, p8pass, strlen(p8pass), 280e7718adaStb NULL, 0, cfg.iter, p8inf))) { 281dab3f910Sjsing BIO_printf(bio_err, "Error encrypting key\n"); 282dab3f910Sjsing ERR_print_errors(bio_err); 283dab3f910Sjsing goto end; 284dab3f910Sjsing } 285e7718adaStb if (cfg.outformat == FORMAT_PEM) 286dab3f910Sjsing PEM_write_bio_PKCS8(out, p8); 287e7718adaStb else if (cfg.outformat == FORMAT_ASN1) 288dab3f910Sjsing i2d_PKCS8_bio(out, p8); 289dab3f910Sjsing else { 290aca35b63Sdoug BIO_printf(bio_err, 291aca35b63Sdoug "Bad format specified for key\n"); 292dab3f910Sjsing goto end; 293dab3f910Sjsing } 294dab3f910Sjsing } 295dab3f910Sjsing 296dab3f910Sjsing ret = 0; 297dab3f910Sjsing goto end; 298dab3f910Sjsing } 299e7718adaStb if (cfg.nocrypt) { 300e7718adaStb if (cfg.informat == FORMAT_PEM) 301aca35b63Sdoug p8inf = PEM_read_bio_PKCS8_PRIV_KEY_INFO(in, NULL, 302aca35b63Sdoug NULL, NULL); 303e7718adaStb else if (cfg.informat == FORMAT_ASN1) 304dab3f910Sjsing p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(in, NULL); 305dab3f910Sjsing else { 306dab3f910Sjsing BIO_printf(bio_err, "Bad format specified for key\n"); 307dab3f910Sjsing goto end; 308dab3f910Sjsing } 309dab3f910Sjsing } else { 310e7718adaStb if (cfg.informat == FORMAT_PEM) 311dab3f910Sjsing p8 = PEM_read_bio_PKCS8(in, NULL, NULL, NULL); 312e7718adaStb else if (cfg.informat == FORMAT_ASN1) 313dab3f910Sjsing p8 = d2i_PKCS8_bio(in, NULL); 314dab3f910Sjsing else { 315dab3f910Sjsing BIO_printf(bio_err, "Bad format specified for key\n"); 316dab3f910Sjsing goto end; 317dab3f910Sjsing } 318dab3f910Sjsing 319dab3f910Sjsing if (!p8) { 320dab3f910Sjsing BIO_printf(bio_err, "Error reading key\n"); 321dab3f910Sjsing ERR_print_errors(bio_err); 322dab3f910Sjsing goto end; 323dab3f910Sjsing } 324dab3f910Sjsing if (passin) 325dab3f910Sjsing p8pass = passin; 326dab3f910Sjsing else { 327dab3f910Sjsing p8pass = pass; 328aca35b63Sdoug EVP_read_pw_string(pass, sizeof pass, 329aca35b63Sdoug "Enter Password:", 0); 330dab3f910Sjsing } 331dab3f910Sjsing p8inf = PKCS8_decrypt(p8, p8pass, strlen(p8pass)); 332dab3f910Sjsing } 333dab3f910Sjsing 334dab3f910Sjsing if (!p8inf) { 335dab3f910Sjsing BIO_printf(bio_err, "Error decrypting key\n"); 336dab3f910Sjsing ERR_print_errors(bio_err); 337dab3f910Sjsing goto end; 338dab3f910Sjsing } 339dab3f910Sjsing if (!(pkey = EVP_PKCS82PKEY(p8inf))) { 340dab3f910Sjsing BIO_printf(bio_err, "Error converting key\n"); 341dab3f910Sjsing ERR_print_errors(bio_err); 342dab3f910Sjsing goto end; 343dab3f910Sjsing } 344e7718adaStb if (cfg.outformat == FORMAT_PEM) 345aca35b63Sdoug PEM_write_bio_PrivateKey(out, pkey, NULL, NULL, 0, NULL, 346aca35b63Sdoug passout); 347e7718adaStb else if (cfg.outformat == FORMAT_ASN1) 348dab3f910Sjsing i2d_PrivateKey_bio(out, pkey); 349dab3f910Sjsing else { 350dab3f910Sjsing BIO_printf(bio_err, "Bad format specified for key\n"); 351dab3f910Sjsing goto end; 352dab3f910Sjsing } 353dab3f910Sjsing ret = 0; 354dab3f910Sjsing 355dab3f910Sjsing end: 356dab3f910Sjsing X509_SIG_free(p8); 357dab3f910Sjsing PKCS8_PRIV_KEY_INFO_free(p8inf); 358dab3f910Sjsing EVP_PKEY_free(pkey); 359dab3f910Sjsing BIO_free_all(out); 360dab3f910Sjsing BIO_free(in); 361dab3f910Sjsing free(passin); 362dab3f910Sjsing free(passout); 363dab3f910Sjsing 364dab3f910Sjsing return ret; 365dab3f910Sjsing } 366