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