1 1.1 christos /* 2 1.1 christos * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos * this file except in compliance with the License. You can obtain a copy 6 1.1 christos * in the file LICENSE in the source distribution or at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos */ 9 1.1 christos 10 1.1 christos /* Necessary for legacy RSA public key export */ 11 1.1 christos #define OPENSSL_SUPPRESS_DEPRECATED 12 1.1 christos 13 1.1 christos #include <openssl/opensslconf.h> 14 1.1 christos 15 1.1 christos #include <stdio.h> 16 1.1 christos #include <stdlib.h> 17 1.1 christos #include <string.h> 18 1.1 christos #include <time.h> 19 1.1 christos #include "apps.h" 20 1.1 christos #include "progs.h" 21 1.1 christos #include <openssl/bio.h> 22 1.1 christos #include <openssl/err.h> 23 1.1 christos #include <openssl/rsa.h> 24 1.1 christos #include <openssl/evp.h> 25 1.1 christos #include <openssl/x509.h> 26 1.1 christos #include <openssl/pem.h> 27 1.1 christos #include <openssl/bn.h> 28 1.1 christos #include <openssl/encoder.h> 29 1.1 christos 30 1.1 christos /* 31 1.1 christos * This include is to get OSSL_KEYMGMT_SELECT_*, which feels a bit 32 1.1 christos * much just for those macros... they might serve better as EVP macros. 33 1.1 christos */ 34 1.1 christos #include <openssl/core_dispatch.h> 35 1.1 christos 36 1.1 christos #ifndef OPENSSL_NO_RC4 37 1.1.1.2 christos #define DEFAULT_PVK_ENCR_STRENGTH 2 38 1.1 christos #else 39 1.1.1.2 christos #define DEFAULT_PVK_ENCR_STRENGTH 0 40 1.1 christos #endif 41 1.1 christos 42 1.1 christos typedef enum OPTION_choice { 43 1.1 christos OPT_COMMON, 44 1.1.1.2 christos OPT_INFORM, 45 1.1.1.2 christos OPT_OUTFORM, 46 1.1.1.2 christos OPT_ENGINE, 47 1.1.1.2 christos OPT_IN, 48 1.1.1.2 christos OPT_OUT, 49 1.1.1.2 christos OPT_PUBIN, 50 1.1.1.2 christos OPT_PUBOUT, 51 1.1.1.2 christos OPT_PASSOUT, 52 1.1.1.2 christos OPT_PASSIN, 53 1.1.1.2 christos OPT_RSAPUBKEY_IN, 54 1.1.1.2 christos OPT_RSAPUBKEY_OUT, 55 1.1 christos /* Do not change the order here; see case statements below */ 56 1.1.1.2 christos OPT_PVK_NONE, 57 1.1.1.2 christos OPT_PVK_WEAK, 58 1.1.1.2 christos OPT_PVK_STRONG, 59 1.1.1.2 christos OPT_NOOUT, 60 1.1.1.2 christos OPT_TEXT, 61 1.1.1.2 christos OPT_MODULUS, 62 1.1.1.2 christos OPT_CHECK, 63 1.1.1.2 christos OPT_CIPHER, 64 1.1.1.2 christos OPT_PROV_ENUM, 65 1.1.1.2 christos OPT_TRADITIONAL 66 1.1 christos } OPTION_CHOICE; 67 1.1 christos 68 1.1 christos const OPTIONS rsa_options[] = { 69 1.1 christos OPT_SECTION("General"), 70 1.1.1.2 christos { "help", OPT_HELP, '-', "Display this summary" }, 71 1.1.1.2 christos { "check", OPT_CHECK, '-', "Verify key consistency" }, 72 1.1.1.2 christos { "", OPT_CIPHER, '-', "Any supported cipher" }, 73 1.1 christos #ifndef OPENSSL_NO_ENGINE 74 1.1.1.2 christos { "engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device" }, 75 1.1 christos #endif 76 1.1 christos 77 1.1 christos OPT_SECTION("Input"), 78 1.1.1.2 christos { "in", OPT_IN, 's', "Input file" }, 79 1.1.1.2 christos { "inform", OPT_INFORM, 'f', "Input format (DER/PEM/P12/ENGINE)" }, 80 1.1.1.2 christos { "pubin", OPT_PUBIN, '-', "Expect a public key in input file" }, 81 1.1.1.2 christos { "RSAPublicKey_in", OPT_RSAPUBKEY_IN, '-', "Input is an RSAPublicKey" }, 82 1.1.1.2 christos { "passin", OPT_PASSIN, 's', "Input file pass phrase source" }, 83 1.1 christos 84 1.1 christos OPT_SECTION("Output"), 85 1.1.1.2 christos { "out", OPT_OUT, '>', "Output file" }, 86 1.1.1.2 christos { "outform", OPT_OUTFORM, 'f', "Output format, one of DER PEM PVK" }, 87 1.1.1.2 christos { "pubout", OPT_PUBOUT, '-', "Output a public key" }, 88 1.1.1.2 christos { "RSAPublicKey_out", OPT_RSAPUBKEY_OUT, '-', "Output is an RSAPublicKey" }, 89 1.1.1.2 christos { "passout", OPT_PASSOUT, 's', "Output file pass phrase source" }, 90 1.1.1.2 christos { "noout", OPT_NOOUT, '-', "Don't print key out" }, 91 1.1.1.2 christos { "text", OPT_TEXT, '-', "Print the key in text" }, 92 1.1.1.2 christos { "modulus", OPT_MODULUS, '-', "Print the RSA key modulus" }, 93 1.1.1.2 christos { "traditional", OPT_TRADITIONAL, '-', 94 1.1.1.2 christos "Use traditional format for private keys" }, 95 1.1 christos 96 1.1 christos #ifndef OPENSSL_NO_RC4 97 1.1 christos OPT_SECTION("PVK"), 98 1.1.1.2 christos { "pvk-strong", OPT_PVK_STRONG, '-', "Enable 'Strong' PVK encoding level (default)" }, 99 1.1.1.2 christos { "pvk-weak", OPT_PVK_WEAK, '-', "Enable 'Weak' PVK encoding level" }, 100 1.1.1.2 christos { "pvk-none", OPT_PVK_NONE, '-', "Don't enforce PVK encoding" }, 101 1.1 christos #endif 102 1.1 christos 103 1.1 christos OPT_PROV_OPTIONS, 104 1.1.1.2 christos { NULL } 105 1.1 christos }; 106 1.1 christos 107 1.1 christos static int try_legacy_encoding(EVP_PKEY *pkey, int outformat, int pubout, 108 1.1.1.2 christos BIO *out) 109 1.1 christos { 110 1.1 christos int ret = 0; 111 1.1 christos #ifndef OPENSSL_NO_DEPRECATED_3_0 112 1.1 christos const RSA *rsa = EVP_PKEY_get0_RSA(pkey); 113 1.1 christos 114 1.1 christos if (rsa == NULL) 115 1.1 christos return 0; 116 1.1 christos 117 1.1 christos if (outformat == FORMAT_ASN1) { 118 1.1 christos if (pubout == 2) 119 1.1 christos ret = i2d_RSAPublicKey_bio(out, rsa) > 0; 120 1.1 christos else 121 1.1 christos ret = i2d_RSA_PUBKEY_bio(out, rsa) > 0; 122 1.1 christos } else if (outformat == FORMAT_PEM) { 123 1.1 christos if (pubout == 2) 124 1.1 christos ret = PEM_write_bio_RSAPublicKey(out, rsa) > 0; 125 1.1 christos else 126 1.1 christos ret = PEM_write_bio_RSA_PUBKEY(out, rsa) > 0; 127 1.1.1.2 christos #ifndef OPENSSL_NO_DSA 128 1.1 christos } else if (outformat == FORMAT_MSBLOB || outformat == FORMAT_PVK) { 129 1.1 christos ret = i2b_PublicKey_bio(out, pkey) > 0; 130 1.1.1.2 christos #endif 131 1.1 christos } 132 1.1 christos #endif 133 1.1 christos 134 1.1 christos return ret; 135 1.1 christos } 136 1.1 christos 137 1.1 christos int rsa_main(int argc, char **argv) 138 1.1 christos { 139 1.1 christos ENGINE *e = NULL; 140 1.1 christos BIO *out = NULL; 141 1.1 christos EVP_PKEY *pkey = NULL; 142 1.1 christos EVP_PKEY_CTX *pctx; 143 1.1 christos EVP_CIPHER *enc = NULL; 144 1.1 christos char *infile = NULL, *outfile = NULL, *ciphername = NULL, *prog; 145 1.1 christos char *passin = NULL, *passout = NULL, *passinarg = NULL, *passoutarg = NULL; 146 1.1 christos int private = 0; 147 1.1 christos int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, text = 0, check = 0; 148 1.1 christos int noout = 0, modulus = 0, pubin = 0, pubout = 0, ret = 1; 149 1.1 christos int pvk_encr = DEFAULT_PVK_ENCR_STRENGTH; 150 1.1 christos OPTION_CHOICE o; 151 1.1 christos int traditional = 0; 152 1.1 christos const char *output_type = NULL; 153 1.1 christos const char *output_structure = NULL; 154 1.1 christos int selection = 0; 155 1.1 christos OSSL_ENCODER_CTX *ectx = NULL; 156 1.1 christos 157 1.1 christos opt_set_unknown_name("cipher"); 158 1.1 christos prog = opt_init(argc, argv, rsa_options); 159 1.1 christos while ((o = opt_next()) != OPT_EOF) { 160 1.1 christos switch (o) { 161 1.1 christos case OPT_EOF: 162 1.1 christos case OPT_ERR: 163 1.1.1.2 christos opthelp: 164 1.1 christos BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); 165 1.1 christos goto end; 166 1.1 christos case OPT_HELP: 167 1.1 christos opt_help(rsa_options); 168 1.1 christos ret = 0; 169 1.1 christos goto end; 170 1.1 christos case OPT_INFORM: 171 1.1 christos if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat)) 172 1.1 christos goto opthelp; 173 1.1 christos break; 174 1.1 christos case OPT_IN: 175 1.1 christos infile = opt_arg(); 176 1.1 christos break; 177 1.1 christos case OPT_OUTFORM: 178 1.1 christos if (!opt_format(opt_arg(), OPT_FMT_ANY, &outformat)) 179 1.1 christos goto opthelp; 180 1.1 christos break; 181 1.1 christos case OPT_OUT: 182 1.1 christos outfile = opt_arg(); 183 1.1 christos break; 184 1.1 christos case OPT_PASSIN: 185 1.1 christos passinarg = opt_arg(); 186 1.1 christos break; 187 1.1 christos case OPT_PASSOUT: 188 1.1 christos passoutarg = opt_arg(); 189 1.1 christos break; 190 1.1 christos case OPT_ENGINE: 191 1.1 christos e = setup_engine(opt_arg(), 0); 192 1.1 christos break; 193 1.1 christos case OPT_PUBIN: 194 1.1 christos pubin = 1; 195 1.1 christos break; 196 1.1 christos case OPT_PUBOUT: 197 1.1 christos pubout = 1; 198 1.1 christos break; 199 1.1 christos case OPT_RSAPUBKEY_IN: 200 1.1 christos pubin = 2; 201 1.1 christos break; 202 1.1 christos case OPT_RSAPUBKEY_OUT: 203 1.1 christos pubout = 2; 204 1.1 christos break; 205 1.1.1.2 christos case OPT_PVK_STRONG: /* pvk_encr:= 2 */ 206 1.1.1.2 christos case OPT_PVK_WEAK: /* pvk_encr:= 1 */ 207 1.1.1.2 christos case OPT_PVK_NONE: /* pvk_encr:= 0 */ 208 1.1 christos pvk_encr = (o - OPT_PVK_NONE); 209 1.1 christos break; 210 1.1 christos case OPT_NOOUT: 211 1.1 christos noout = 1; 212 1.1 christos break; 213 1.1 christos case OPT_TEXT: 214 1.1 christos text = 1; 215 1.1 christos break; 216 1.1 christos case OPT_MODULUS: 217 1.1 christos modulus = 1; 218 1.1 christos break; 219 1.1 christos case OPT_CHECK: 220 1.1 christos check = 1; 221 1.1 christos break; 222 1.1 christos case OPT_CIPHER: 223 1.1 christos ciphername = opt_unknown(); 224 1.1 christos break; 225 1.1 christos case OPT_PROV_CASES: 226 1.1 christos if (!opt_provider(o)) 227 1.1 christos goto end; 228 1.1 christos break; 229 1.1 christos case OPT_TRADITIONAL: 230 1.1 christos traditional = 1; 231 1.1 christos break; 232 1.1 christos } 233 1.1 christos } 234 1.1 christos 235 1.1 christos /* No extra arguments. */ 236 1.1 christos if (!opt_check_rest_arg(NULL)) 237 1.1 christos goto opthelp; 238 1.1 christos 239 1.1 christos if (!opt_cipher(ciphername, &enc)) 240 1.1 christos goto opthelp; 241 1.1 christos private = (text && !pubin) || (!pubout && !noout); 242 1.1 christos 243 1.1 christos if (!app_passwd(passinarg, passoutarg, &passin, &passout)) { 244 1.1 christos BIO_printf(bio_err, "Error getting passwords\n"); 245 1.1 christos goto end; 246 1.1 christos } 247 1.1 christos if (check && pubin) { 248 1.1 christos BIO_printf(bio_err, "Only private keys can be checked\n"); 249 1.1 christos goto end; 250 1.1 christos } 251 1.1 christos 252 1.1 christos if (pubin) { 253 1.1 christos int tmpformat = FORMAT_UNDEF; 254 1.1 christos 255 1.1 christos if (pubin == 2) { 256 1.1 christos if (informat == FORMAT_PEM) 257 1.1 christos tmpformat = FORMAT_PEMRSA; 258 1.1 christos else if (informat == FORMAT_ASN1) 259 1.1 christos tmpformat = FORMAT_ASN1RSA; 260 1.1 christos } else { 261 1.1 christos tmpformat = informat; 262 1.1 christos } 263 1.1 christos 264 1.1 christos pkey = load_pubkey(infile, tmpformat, 1, passin, e, "public key"); 265 1.1 christos } else { 266 1.1 christos pkey = load_key(infile, informat, 1, passin, e, "private key"); 267 1.1 christos } 268 1.1 christos 269 1.1 christos if (pkey == NULL) { 270 1.1 christos ERR_print_errors(bio_err); 271 1.1 christos goto end; 272 1.1 christos } 273 1.1 christos if (!EVP_PKEY_is_a(pkey, "RSA") && !EVP_PKEY_is_a(pkey, "RSA-PSS")) { 274 1.1 christos BIO_printf(bio_err, "Not an RSA key\n"); 275 1.1 christos goto end; 276 1.1 christos } 277 1.1 christos 278 1.1 christos out = bio_open_owner(outfile, outformat, private); 279 1.1 christos if (out == NULL) 280 1.1 christos goto end; 281 1.1 christos 282 1.1 christos if (text) { 283 1.1 christos assert(pubin || private); 284 1.1 christos if ((pubin && EVP_PKEY_print_public(out, pkey, 0, NULL) <= 0) 285 1.1 christos || (!pubin && EVP_PKEY_print_private(out, pkey, 0, NULL) <= 0)) { 286 1.1 christos perror(outfile); 287 1.1 christos ERR_print_errors(bio_err); 288 1.1 christos goto end; 289 1.1 christos } 290 1.1 christos } 291 1.1 christos 292 1.1 christos if (modulus) { 293 1.1 christos BIGNUM *n = NULL; 294 1.1 christos 295 1.1 christos /* Every RSA key has an 'n' */ 296 1.1 christos EVP_PKEY_get_bn_param(pkey, "n", &n); 297 1.1 christos BIO_printf(out, "Modulus="); 298 1.1 christos BN_print(out, n); 299 1.1 christos BIO_printf(out, "\n"); 300 1.1 christos BN_free(n); 301 1.1 christos } 302 1.1 christos 303 1.1 christos if (check) { 304 1.1 christos int r; 305 1.1 christos 306 1.1 christos pctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL); 307 1.1 christos if (pctx == NULL) { 308 1.1 christos BIO_printf(bio_err, "RSA unable to create PKEY context\n"); 309 1.1 christos ERR_print_errors(bio_err); 310 1.1 christos goto end; 311 1.1 christos } 312 1.1 christos r = EVP_PKEY_check(pctx); 313 1.1 christos EVP_PKEY_CTX_free(pctx); 314 1.1 christos 315 1.1 christos if (r == 1) { 316 1.1 christos BIO_printf(out, "RSA key ok\n"); 317 1.1 christos } else if (r == 0) { 318 1.1 christos BIO_printf(bio_err, "RSA key not ok\n"); 319 1.1 christos ERR_print_errors(bio_err); 320 1.1 christos } else if (r < 0) { 321 1.1 christos ERR_print_errors(bio_err); 322 1.1 christos goto end; 323 1.1 christos } 324 1.1 christos } 325 1.1 christos 326 1.1 christos if (noout) { 327 1.1 christos ret = 0; 328 1.1 christos goto end; 329 1.1 christos } 330 1.1 christos BIO_printf(bio_err, "writing RSA key\n"); 331 1.1 christos 332 1.1 christos /* Choose output type for the format */ 333 1.1 christos if (outformat == FORMAT_ASN1) { 334 1.1 christos output_type = "DER"; 335 1.1 christos } else if (outformat == FORMAT_PEM) { 336 1.1 christos output_type = "PEM"; 337 1.1 christos } else if (outformat == FORMAT_MSBLOB) { 338 1.1 christos output_type = "MSBLOB"; 339 1.1 christos } else if (outformat == FORMAT_PVK) { 340 1.1 christos if (pubin) { 341 1.1 christos BIO_printf(bio_err, "PVK form impossible with public key input\n"); 342 1.1 christos goto end; 343 1.1 christos } 344 1.1 christos output_type = "PVK"; 345 1.1 christos } else { 346 1.1 christos BIO_printf(bio_err, "bad output format specified for outfile\n"); 347 1.1 christos goto end; 348 1.1 christos } 349 1.1 christos 350 1.1 christos /* Select what you want in the output */ 351 1.1 christos if (pubout || pubin) { 352 1.1 christos selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY; 353 1.1 christos } else { 354 1.1 christos assert(private); 355 1.1 christos selection = (OSSL_KEYMGMT_SELECT_KEYPAIR 356 1.1.1.2 christos | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS); 357 1.1 christos } 358 1.1 christos 359 1.1 christos /* For DER based output, select the desired output structure */ 360 1.1 christos if (outformat == FORMAT_ASN1 || outformat == FORMAT_PEM) { 361 1.1 christos if (pubout || pubin) { 362 1.1 christos if (pubout == 2) 363 1.1 christos output_structure = "pkcs1"; /* "type-specific" would work too */ 364 1.1 christos else 365 1.1 christos output_structure = "SubjectPublicKeyInfo"; 366 1.1 christos } else { 367 1.1 christos assert(private); 368 1.1 christos if (traditional) 369 1.1 christos output_structure = "pkcs1"; /* "type-specific" would work too */ 370 1.1 christos else 371 1.1 christos output_structure = "PrivateKeyInfo"; 372 1.1 christos } 373 1.1 christos } 374 1.1 christos 375 1.1 christos /* Now, perform the encoding */ 376 1.1 christos ectx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, 377 1.1.1.2 christos output_type, output_structure, 378 1.1.1.2 christos NULL); 379 1.1 christos if (OSSL_ENCODER_CTX_get_num_encoders(ectx) == 0) { 380 1.1 christos if ((!pubout && !pubin) 381 1.1 christos || !try_legacy_encoding(pkey, outformat, pubout, out)) 382 1.1 christos BIO_printf(bio_err, "%s format not supported\n", output_type); 383 1.1 christos else 384 1.1 christos ret = 0; 385 1.1 christos goto end; 386 1.1 christos } 387 1.1 christos 388 1.1 christos /* Passphrase setup */ 389 1.1 christos if (enc != NULL) 390 1.1 christos OSSL_ENCODER_CTX_set_cipher(ectx, EVP_CIPHER_get0_name(enc), NULL); 391 1.1 christos 392 1.1 christos /* Default passphrase prompter */ 393 1.1 christos if (enc != NULL || outformat == FORMAT_PVK) { 394 1.1 christos OSSL_ENCODER_CTX_set_passphrase_ui(ectx, get_ui_method(), NULL); 395 1.1 christos if (passout != NULL) 396 1.1 christos /* When passout given, override the passphrase prompter */ 397 1.1 christos OSSL_ENCODER_CTX_set_passphrase(ectx, 398 1.1.1.2 christos (const unsigned char *)passout, 399 1.1.1.2 christos strlen(passout)); 400 1.1 christos } 401 1.1 christos 402 1.1 christos /* PVK is a bit special... */ 403 1.1 christos if (outformat == FORMAT_PVK) { 404 1.1 christos OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; 405 1.1 christos 406 1.1 christos params[0] = OSSL_PARAM_construct_int("encrypt-level", &pvk_encr); 407 1.1 christos if (!OSSL_ENCODER_CTX_set_params(ectx, params)) { 408 1.1 christos BIO_printf(bio_err, "invalid PVK encryption level\n"); 409 1.1 christos goto end; 410 1.1 christos } 411 1.1 christos } 412 1.1 christos 413 1.1 christos if (!OSSL_ENCODER_to_bio(ectx, out)) { 414 1.1 christos BIO_printf(bio_err, "unable to write key\n"); 415 1.1 christos ERR_print_errors(bio_err); 416 1.1 christos goto end; 417 1.1 christos } 418 1.1 christos ret = 0; 419 1.1.1.2 christos end: 420 1.1 christos OSSL_ENCODER_CTX_free(ectx); 421 1.1 christos release_engine(e); 422 1.1 christos BIO_free_all(out); 423 1.1 christos EVP_PKEY_free(pkey); 424 1.1 christos EVP_CIPHER_free(enc); 425 1.1 christos OPENSSL_free(passin); 426 1.1 christos OPENSSL_free(passout); 427 1.1 christos return ret; 428 1.1 christos } 429