1 /*- 2 * Copyright 2022-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 #include <string.h> 10 #include <openssl/decoder.h> 11 #include <openssl/encoder.h> 12 #include <openssl/evp.h> 13 14 /* 15 * Example showing the encoding and decoding of RSA public and private keys. A 16 * PEM-encoded RSA key is read in from stdin, decoded, and then re-encoded and 17 * output for demonstration purposes. Both public and private keys are accepted. 18 * 19 * This can be used to load RSA keys from a file or save RSA keys to a file. 20 */ 21 22 /* A property query used for selecting algorithm implementations. */ 23 static const char *propq = NULL; 24 25 /* 26 * Load a PEM-encoded RSA key from a file, optionally decrypting it with a 27 * supplied passphrase. 28 */ 29 static EVP_PKEY *load_key(OSSL_LIB_CTX *libctx, FILE *f, const char *passphrase) 30 { 31 int ret = 0; 32 EVP_PKEY *pkey = NULL; 33 OSSL_DECODER_CTX *dctx = NULL; 34 int selection = 0; 35 36 /* 37 * Create PEM decoder context expecting an RSA key. 38 * 39 * For raw (non-PEM-encoded) keys, change "PEM" to "DER". 40 * 41 * The selection argument here specifies whether we are willing to accept a 42 * public key, private key, or either. If it is set to zero, either will be 43 * accepted. If set to EVP_PKEY_KEYPAIR, a private key will be required, and 44 * if set to EVP_PKEY_PUBLIC_KEY, a public key will be required. 45 */ 46 dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, "RSA", 47 selection, 48 libctx, propq); 49 if (dctx == NULL) { 50 fprintf(stderr, "OSSL_DECODER_CTX_new_for_pkey() failed\n"); 51 goto cleanup; 52 } 53 54 /* 55 * Set passphrase if provided; needed to decrypt encrypted PEM files. 56 * If the input is not encrypted, any passphrase provided is ignored. 57 * 58 * Alternative methods for specifying passphrases exist, such as a callback 59 * (see OSSL_DECODER_CTX_set_passphrase_cb(3)), which may be more useful for 60 * interactive applications which do not know if a passphrase should be 61 * prompted for in advance, or for GUI applications. 62 */ 63 if (passphrase != NULL) { 64 if (OSSL_DECODER_CTX_set_passphrase(dctx, 65 (const unsigned char *)passphrase, 66 strlen(passphrase)) 67 == 0) { 68 fprintf(stderr, "OSSL_DECODER_CTX_set_passphrase() failed\n"); 69 goto cleanup; 70 } 71 } 72 73 /* Do the decode, reading from file. */ 74 if (OSSL_DECODER_from_fp(dctx, f) == 0) { 75 fprintf(stderr, "OSSL_DECODER_from_fp() failed\n"); 76 goto cleanup; 77 } 78 79 ret = 1; 80 cleanup: 81 OSSL_DECODER_CTX_free(dctx); 82 83 /* 84 * pkey is created by OSSL_DECODER_CTX_new_for_pkey, but we 85 * might fail subsequently, so ensure it's properly freed 86 * in this case. 87 */ 88 if (ret == 0) { 89 EVP_PKEY_free(pkey); 90 pkey = NULL; 91 } 92 93 return pkey; 94 } 95 96 /* 97 * Store an RSA public or private key to a file using PEM encoding. 98 * 99 * If a passphrase is supplied, the file is encrypted, otherwise 100 * it is unencrypted. 101 */ 102 static int store_key(EVP_PKEY *pkey, FILE *f, const char *passphrase) 103 { 104 int ret = 0; 105 int selection; 106 OSSL_ENCODER_CTX *ectx = NULL; 107 108 /* 109 * Create a PEM encoder context. 110 * 111 * For raw (non-PEM-encoded) output, change "PEM" to "DER". 112 * 113 * The selection argument controls whether the private key is exported 114 * (EVP_PKEY_KEYPAIR), or only the public key (EVP_PKEY_PUBLIC_KEY). The 115 * former will fail if we only have a public key. 116 * 117 * Note that unlike the decode API, you cannot specify zero here. 118 * 119 * Purely for the sake of demonstration, here we choose to export the whole 120 * key if a passphrase is provided and the public key otherwise. 121 */ 122 selection = (passphrase != NULL) 123 ? EVP_PKEY_KEYPAIR 124 : EVP_PKEY_PUBLIC_KEY; 125 126 ectx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "PEM", NULL, propq); 127 if (ectx == NULL) { 128 fprintf(stderr, "OSSL_ENCODER_CTX_new_for_pkey() failed\n"); 129 goto cleanup; 130 } 131 132 /* 133 * Set passphrase if provided; the encoded output will then be encrypted 134 * using the passphrase. 135 * 136 * Alternative methods for specifying passphrases exist, such as a callback 137 * (see OSSL_ENCODER_CTX_set_passphrase_cb(3), just as for OSSL_DECODER_CTX; 138 * however you are less likely to need them as you presumably know whether 139 * encryption is desired in advance. 140 * 141 * Note that specifying a passphrase alone is not enough to cause the 142 * key to be encrypted. You must set both a cipher and a passphrase. 143 */ 144 if (passphrase != NULL) { 145 /* Set cipher. AES-128-CBC is a reasonable default. */ 146 if (OSSL_ENCODER_CTX_set_cipher(ectx, "AES-128-CBC", propq) == 0) { 147 fprintf(stderr, "OSSL_ENCODER_CTX_set_cipher() failed\n"); 148 goto cleanup; 149 } 150 151 /* Set passphrase. */ 152 if (OSSL_ENCODER_CTX_set_passphrase(ectx, 153 (const unsigned char *)passphrase, 154 strlen(passphrase)) 155 == 0) { 156 fprintf(stderr, "OSSL_ENCODER_CTX_set_passphrase() failed\n"); 157 goto cleanup; 158 } 159 } 160 161 /* Do the encode, writing to the given file. */ 162 if (OSSL_ENCODER_to_fp(ectx, f) == 0) { 163 fprintf(stderr, "OSSL_ENCODER_to_fp() failed\n"); 164 goto cleanup; 165 } 166 167 ret = 1; 168 cleanup: 169 OSSL_ENCODER_CTX_free(ectx); 170 return ret; 171 } 172 173 int main(int argc, char **argv) 174 { 175 int ret = EXIT_FAILURE; 176 OSSL_LIB_CTX *libctx = NULL; 177 EVP_PKEY *pkey = NULL; 178 const char *passphrase_in = NULL, *passphrase_out = NULL; 179 180 /* usage: rsa_encode <passphrase-in> <passphrase-out> */ 181 if (argc > 1 && argv[1][0]) 182 passphrase_in = argv[1]; 183 184 if (argc > 2 && argv[2][0]) 185 passphrase_out = argv[2]; 186 187 /* Decode PEM key from stdin and then PEM encode it to stdout. */ 188 pkey = load_key(libctx, stdin, passphrase_in); 189 if (pkey == NULL) { 190 fprintf(stderr, "Failed to decode key\n"); 191 goto cleanup; 192 } 193 194 if (store_key(pkey, stdout, passphrase_out) == 0) { 195 fprintf(stderr, "Failed to encode key\n"); 196 goto cleanup; 197 } 198 199 ret = EXIT_SUCCESS; 200 cleanup: 201 EVP_PKEY_free(pkey); 202 OSSL_LIB_CTX_free(libctx); 203 return ret; 204 } 205