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