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 EC public and private keys. A 16 * PEM-encoded EC 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 EC keys from a file or save EC 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 EC 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 EC 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, "EC", 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 a EC 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 /* 146 * Set cipher. Let's use AES-256-CBC, because it is 147 * more quantum resistant. 148 */ 149 if (OSSL_ENCODER_CTX_set_cipher(ectx, "AES-256-CBC", propq) == 0) { 150 fprintf(stderr, "OSSL_ENCODER_CTX_set_cipher() failed\n"); 151 goto cleanup; 152 } 153 154 /* Set passphrase. */ 155 if (OSSL_ENCODER_CTX_set_passphrase(ectx, 156 (const unsigned char *)passphrase, 157 strlen(passphrase)) 158 == 0) { 159 fprintf(stderr, "OSSL_ENCODER_CTX_set_passphrase() failed\n"); 160 goto cleanup; 161 } 162 } 163 164 /* Do the encode, writing to the given file. */ 165 if (OSSL_ENCODER_to_fp(ectx, f) == 0) { 166 fprintf(stderr, "OSSL_ENCODER_to_fp() failed\n"); 167 goto cleanup; 168 } 169 170 ret = 1; 171 cleanup: 172 OSSL_ENCODER_CTX_free(ectx); 173 return ret; 174 } 175 176 int main(int argc, char **argv) 177 { 178 int ret = EXIT_FAILURE; 179 OSSL_LIB_CTX *libctx = NULL; 180 EVP_PKEY *pkey = NULL; 181 const char *passphrase_in = NULL, *passphrase_out = NULL; 182 183 /* usage: ec_encode <passphrase-in> <passphrase-out> */ 184 if (argc > 1 && argv[1][0]) 185 passphrase_in = argv[1]; 186 187 if (argc > 2 && argv[2][0]) 188 passphrase_out = argv[2]; 189 190 /* Decode PEM key from stdin and then PEM encode it to stdout. */ 191 pkey = load_key(libctx, stdin, passphrase_in); 192 if (pkey == NULL) { 193 fprintf(stderr, "Failed to decode key\n"); 194 goto cleanup; 195 } 196 197 if (store_key(pkey, stdout, passphrase_out) == 0) { 198 fprintf(stderr, "Failed to encode key\n"); 199 goto cleanup; 200 } 201 202 ret = EXIT_SUCCESS; 203 cleanup: 204 EVP_PKEY_free(pkey); 205 OSSL_LIB_CTX_free(libctx); 206 return ret; 207 } 208