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