diff options
author | Daniel Fiala <daniel@openssl.org> | 2022-09-06 06:27:46 +0200 |
---|---|---|
committer | Pauli <pauli@openssl.org> | 2022-09-09 07:26:42 +1000 |
commit | a4b7136ebfd154636f607c50aaeec778a75b2d26 (patch) | |
tree | 3c552103333810aacaef654a277c3ef2ea32fd8f /demos | |
parent | 07687790a055a039bba93ee00ac970c9710f0669 (diff) | |
download | openssl-new-a4b7136ebfd154636f607c50aaeec778a75b2d26.tar.gz |
Add an EVP demo for key encoding using EC
Fixes openssl#14117
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19143)
Diffstat (limited to 'demos')
-rw-r--r-- | demos/encode/Makefile | 6 | ||||
-rw-r--r-- | demos/encode/ec_encode.c | 205 |
2 files changed, 208 insertions, 3 deletions
diff --git a/demos/encode/Makefile b/demos/encode/Makefile index fc1c06c6d7..b88d76b51d 100644 --- a/demos/encode/Makefile +++ b/demos/encode/Makefile @@ -7,14 +7,14 @@ CFLAGS = -I../../include -g -Wall LDFLAGS = -L../.. LDLIBS = -lcrypto -all: rsa_encode +all: ec_encode rsa_encode %.o: %.c $(CC) $(CFLAGS) -c $< -rsa_encode: rsa_encode.o +%_encode: %_encode.o test: ; clean: - $(RM) *.o rsa_encode + $(RM) *.o rsa_encode ec_encode diff --git a/demos/encode/ec_encode.c b/demos/encode/ec_encode.c new file mode 100644 index 0000000000..8c296fbad8 --- /dev/null +++ b/demos/encode/ec_encode.c @@ -0,0 +1,205 @@ +/*- + * Copyright 2022 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 EC public and private keys. A + * PEM-encoded EC 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 EC keys from a file or save EC keys to a file. + */ + +/* A property query used for selecting algorithm implementations. */ +static const char *propq = NULL; + +/* + * Load a PEM-encoded EC 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 rv = 0; + EVP_PKEY *pkey = NULL; + OSSL_DECODER_CTX *dctx = NULL; + int selection = 0; + + /* + * Create PEM decoder context expecting an EC 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, "EC", + 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; + } + + rv = 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 (rv == 0) { + EVP_PKEY_free(pkey); + pkey = NULL; + } + + return pkey; +} + +/* + * Store a EC 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 rv = 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. Let's use AES-256-CBC, because it is + * more quantum resistant. + */ + if (OSSL_ENCODER_CTX_set_cipher(ectx, "AES-256-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; + } + + rv = 1; +cleanup: + OSSL_ENCODER_CTX_free(ectx); + return rv; +} + +int main(int argc, char **argv) +{ + int rv = 1; + OSSL_LIB_CTX *libctx = NULL; + EVP_PKEY *pkey = NULL; + const char *passphrase_in = NULL, *passphrase_out = NULL; + + /* usage: ec_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; + } + + rv = 0; +cleanup: + EVP_PKEY_free(pkey); + OSSL_LIB_CTX_free(libctx); + return rv; +} |