]>
Commit | Line | Data |
---|---|---|
1483b37e | 1 | /*- |
da1c088f | 2 | * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. |
1483b37e HL |
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 RSA public and private keys. A | |
16 | * PEM-encoded RSA 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 RSA keys from a file or save RSA 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 RSA 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 | { | |
09ff84bd | 31 | int ret = 0; |
1483b37e HL |
32 | EVP_PKEY *pkey = NULL; |
33 | OSSL_DECODER_CTX *dctx = NULL; | |
34 | int selection = 0; | |
35 | ||
36 | /* | |
37 | * Create PEM decoder context expecting an RSA 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, "RSA", | |
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 | ||
09ff84bd | 78 | ret = 1; |
1483b37e HL |
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 | */ | |
09ff84bd | 87 | if (ret == 0) { |
1483b37e HL |
88 | EVP_PKEY_free(pkey); |
89 | pkey = NULL; | |
90 | } | |
91 | ||
92 | return pkey; | |
93 | } | |
94 | ||
95 | /* | |
a63fa5f7 | 96 | * Store an RSA public or private key to a file using PEM encoding. |
1483b37e HL |
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 | { | |
09ff84bd | 103 | int ret = 0; |
1483b37e HL |
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 | /* Set cipher. AES-128-CBC is a reasonable default. */ | |
145 | if (OSSL_ENCODER_CTX_set_cipher(ectx, "AES-128-CBC", propq) == 0) { | |
146 | fprintf(stderr, "OSSL_ENCODER_CTX_set_cipher() failed\n"); | |
147 | goto cleanup; | |
148 | } | |
149 | ||
150 | /* Set passphrase. */ | |
151 | if (OSSL_ENCODER_CTX_set_passphrase(ectx, | |
152 | (const unsigned char *)passphrase, | |
153 | strlen(passphrase)) == 0) { | |
154 | fprintf(stderr, "OSSL_ENCODER_CTX_set_passphrase() failed\n"); | |
155 | goto cleanup; | |
156 | } | |
157 | } | |
158 | ||
159 | /* Do the encode, writing to the given file. */ | |
160 | if (OSSL_ENCODER_to_fp(ectx, f) == 0) { | |
161 | fprintf(stderr, "OSSL_ENCODER_to_fp() failed\n"); | |
162 | goto cleanup; | |
163 | } | |
164 | ||
09ff84bd | 165 | ret = 1; |
1483b37e HL |
166 | cleanup: |
167 | OSSL_ENCODER_CTX_free(ectx); | |
09ff84bd | 168 | return ret; |
1483b37e HL |
169 | } |
170 | ||
171 | int main(int argc, char **argv) | |
172 | { | |
09ff84bd | 173 | int ret = EXIT_FAILURE; |
1483b37e HL |
174 | OSSL_LIB_CTX *libctx = NULL; |
175 | EVP_PKEY *pkey = NULL; | |
176 | const char *passphrase_in = NULL, *passphrase_out = NULL; | |
177 | ||
178 | /* usage: rsa_encode <passphrase-in> <passphrase-out> */ | |
179 | if (argc > 1 && argv[1][0]) | |
180 | passphrase_in = argv[1]; | |
181 | ||
182 | if (argc > 2 && argv[2][0]) | |
183 | passphrase_out = argv[2]; | |
184 | ||
185 | /* Decode PEM key from stdin and then PEM encode it to stdout. */ | |
186 | pkey = load_key(libctx, stdin, passphrase_in); | |
187 | if (pkey == NULL) { | |
188 | fprintf(stderr, "Failed to decode key\n"); | |
189 | goto cleanup; | |
190 | } | |
191 | ||
192 | if (store_key(pkey, stdout, passphrase_out) == 0) { | |
193 | fprintf(stderr, "Failed to encode key\n"); | |
194 | goto cleanup; | |
195 | } | |
196 | ||
09ff84bd | 197 | ret = EXIT_SUCCESS; |
1483b37e HL |
198 | cleanup: |
199 | EVP_PKEY_free(pkey); | |
200 | OSSL_LIB_CTX_free(libctx); | |
09ff84bd | 201 | return ret; |
1483b37e | 202 | } |