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