1 /*- 2 * Copyright 2022 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 rv = 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)) == 0) { 67 fprintf(stderr, "OSSL_DECODER_CTX_set_passphrase() failed\n"); 68 goto cleanup; 69 } 70 } 71 72 /* Do the decode, reading from file. */ 73 if (OSSL_DECODER_from_fp(dctx, f) == 0) { 74 fprintf(stderr, "OSSL_DECODER_from_fp() failed\n"); 75 goto cleanup; 76 } 77 78 rv = 1; 79 cleanup: 80 OSSL_DECODER_CTX_free(dctx); 81 82 /* 83 * pkey is created by OSSL_DECODER_CTX_new_for_pkey, but we 84 * might fail subsequently, so ensure it's properly freed 85 * in this case. 86 */ 87 if (rv == 0) { 88 EVP_PKEY_free(pkey); 89 pkey = NULL; 90 } 91 92 return pkey; 93 } 94 95 /* 96 * Store an RSA public or private key to a file using PEM encoding. 97 * 98 * If a passphrase is supplied, the file is encrypted, otherwise 99 * it is unencrypted. 100 */ 101 static int store_key(EVP_PKEY *pkey, FILE *f, const char *passphrase) 102 { 103 int rv = 0; 104 int selection; 105 OSSL_ENCODER_CTX *ectx = NULL; 106 107 /* 108 * Create a PEM encoder context. 109 * 110 * For raw (non-PEM-encoded) output, change "PEM" to "DER". 111 * 112 * The selection argument controls whether the private key is exported 113 * (EVP_PKEY_KEYPAIR), or only the public key (EVP_PKEY_PUBLIC_KEY). The 114 * former will fail if we only have a public key. 115 * 116 * Note that unlike the decode API, you cannot specify zero here. 117 * 118 * Purely for the sake of demonstration, here we choose to export the whole 119 * key if a passphrase is provided and the public key otherwise. 120 */ 121 selection = (passphrase != NULL) 122 ? EVP_PKEY_KEYPAIR 123 : EVP_PKEY_PUBLIC_KEY; 124 125 ectx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "PEM", NULL, propq); 126 if (ectx == NULL) { 127 fprintf(stderr, "OSSL_ENCODER_CTX_new_for_pkey() failed\n"); 128 goto cleanup; 129 } 130 131 /* 132 * Set passphrase if provided; the encoded output will then be encrypted 133 * using the passphrase. 134 * 135 * Alternative methods for specifying passphrases exist, such as a callback 136 * (see OSSL_ENCODER_CTX_set_passphrase_cb(3), just as for OSSL_DECODER_CTX; 137 * however you are less likely to need them as you presumably know whether 138 * encryption is desired in advance. 139 * 140 * Note that specifying a passphrase alone is not enough to cause the 141 * key to be encrypted. You must set both a cipher and a passphrase. 142 */ 143 if (passphrase != NULL) { 144 /* Set cipher. AES-128-CBC is a reasonable default. */ 145 if (OSSL_ENCODER_CTX_set_cipher(ectx, "AES-128-CBC", propq) == 0) { 146 fprintf(stderr, "OSSL_ENCODER_CTX_set_cipher() failed\n"); 147 goto cleanup; 148 } 149 150 /* Set passphrase. */ 151 if (OSSL_ENCODER_CTX_set_passphrase(ectx, 152 (const unsigned char *)passphrase, 153 strlen(passphrase)) == 0) { 154 fprintf(stderr, "OSSL_ENCODER_CTX_set_passphrase() failed\n"); 155 goto cleanup; 156 } 157 } 158 159 /* Do the encode, writing to the given file. */ 160 if (OSSL_ENCODER_to_fp(ectx, f) == 0) { 161 fprintf(stderr, "OSSL_ENCODER_to_fp() failed\n"); 162 goto cleanup; 163 } 164 165 rv = 1; 166 cleanup: 167 OSSL_ENCODER_CTX_free(ectx); 168 return rv; 169 } 170 171 int main(int argc, char **argv) 172 { 173 int rv = 1; 174 OSSL_LIB_CTX *libctx = NULL; 175 EVP_PKEY *pkey = NULL; 176 const char *passphrase_in = NULL, *passphrase_out = NULL; 177 178 /* usage: rsa_encode <passphrase-in> <passphrase-out> */ 179 if (argc > 1 && argv[1][0]) 180 passphrase_in = argv[1]; 181 182 if (argc > 2 && argv[2][0]) 183 passphrase_out = argv[2]; 184 185 /* Decode PEM key from stdin and then PEM encode it to stdout. */ 186 pkey = load_key(libctx, stdin, passphrase_in); 187 if (pkey == NULL) { 188 fprintf(stderr, "Failed to decode key\n"); 189 goto cleanup; 190 } 191 192 if (store_key(pkey, stdout, passphrase_out) == 0) { 193 fprintf(stderr, "Failed to encode key\n"); 194 goto cleanup; 195 } 196 197 rv = 0; 198 cleanup: 199 EVP_PKEY_free(pkey); 200 OSSL_LIB_CTX_free(libctx); 201 return rv; 202 } 203