xref: /openssl/demos/encrypt/rsa_encrypt.c (revision 35530b11)
1 /*-
2  * Copyright 2021 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 
10 /*
11  * An example that uses EVP_PKEY_encrypt and EVP_PKEY_decrypt methods
12  * to encrypt and decrypt data using an RSA keypair.
13  * RSA encryption produces different encrypted output each time it is run,
14  * hence this is not a known answer test.
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <openssl/err.h>
20 #include <openssl/evp.h>
21 #include <openssl/decoder.h>
22 #include <openssl/core_names.h>
23 #include "rsa_encrypt.h"
24 
25 /* Input data to encrypt */
26 static const unsigned char msg[] =
27     "To be, or not to be, that is the question,\n"
28     "Whether tis nobler in the minde to suffer\n"
29     "The slings and arrowes of outragious fortune,\n"
30     "Or to take Armes again in a sea of troubles";
31 
32 /*
33  * For do_encrypt(), load an RSA public key from pub_key_der[].
34  * For do_decrypt(), load an RSA private key from priv_key_der[].
35  */
get_key(OSSL_LIB_CTX * libctx,const char * propq,int public)36 static EVP_PKEY *get_key(OSSL_LIB_CTX *libctx, const char *propq, int public)
37 {
38     OSSL_DECODER_CTX *dctx = NULL;
39     EVP_PKEY *pkey = NULL;
40     int selection;
41     const unsigned char *data;
42     size_t data_len;
43 
44     if (public) {
45         selection = EVP_PKEY_PUBLIC_KEY;
46         data = pub_key_der;
47         data_len = sizeof(pub_key_der);
48     } else {
49         selection = EVP_PKEY_KEYPAIR;
50         data = priv_key_der;
51         data_len = sizeof(priv_key_der);
52     }
53     dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, "RSA",
54                                          selection, libctx, propq);
55     (void)OSSL_DECODER_from_data(dctx, &data, &data_len);
56     OSSL_DECODER_CTX_free(dctx);
57     return pkey;
58 }
59 
60 /* Set optional parameters for RSA OAEP Padding */
set_optional_params(OSSL_PARAM * p,const char * propq)61 static void set_optional_params(OSSL_PARAM *p, const char *propq)
62 {
63     static unsigned char label[] = "label";
64 
65     /* "pkcs1" is used by default if the padding mode is not set */
66     *p++ = OSSL_PARAM_construct_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE,
67                                             OSSL_PKEY_RSA_PAD_MODE_OAEP, 0);
68     /* No oaep_label is used if this is not set */
69     *p++ = OSSL_PARAM_construct_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL,
70                                              label, sizeof(label));
71     /* "SHA1" is used if this is not set */
72     *p++ = OSSL_PARAM_construct_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST,
73                                             "SHA256", 0);
74     /*
75      * If a non default property query needs to be specified when fetching the
76      * OAEP digest then it needs to be specified here.
77      */
78     if (propq != NULL)
79         *p++ = OSSL_PARAM_construct_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS,
80                                                 (char *)propq, 0);
81 
82     /*
83      * OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST and
84      * OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS can also be optionally added
85      * here if the MGF1 digest differs from the OAEP digest.
86      */
87 
88     *p = OSSL_PARAM_construct_end();
89 }
90 
91 /*
92  * The length of the input data that can be encrypted is limited by the
93  * RSA key length minus some additional bytes that depends on the padding mode.
94  *
95  */
do_encrypt(OSSL_LIB_CTX * libctx,const unsigned char * in,size_t in_len,unsigned char ** out,size_t * out_len)96 static int do_encrypt(OSSL_LIB_CTX *libctx,
97                       const unsigned char *in, size_t in_len,
98                       unsigned char **out, size_t *out_len)
99 {
100     int ret = 0, public = 1;
101     size_t buf_len = 0;
102     unsigned char *buf = NULL;
103     const char *propq = NULL;
104     EVP_PKEY_CTX *ctx = NULL;
105     EVP_PKEY *pub_key = NULL;
106     OSSL_PARAM params[5];
107 
108     /* Get public key */
109     pub_key = get_key(libctx, propq, public);
110     if (pub_key == NULL) {
111         fprintf(stderr, "Get public key failed.\n");
112         goto cleanup;
113     }
114     ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pub_key, propq);
115     if (ctx == NULL) {
116         fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey() failed.\n");
117         goto cleanup;
118     }
119     set_optional_params(params, propq);
120     /* If no optional parameters are required then NULL can be passed */
121     if (EVP_PKEY_encrypt_init_ex(ctx, params) <= 0) {
122         fprintf(stderr, "EVP_PKEY_encrypt_init_ex() failed.\n");
123         goto cleanup;
124     }
125     /* Calculate the size required to hold the encrypted data */
126     if (EVP_PKEY_encrypt(ctx, NULL, &buf_len, in, in_len) <= 0) {
127         fprintf(stderr, "EVP_PKEY_encrypt() failed.\n");
128         goto cleanup;
129     }
130     buf = OPENSSL_zalloc(buf_len);
131     if (buf  == NULL) {
132         fprintf(stderr, "Malloc failed.\n");
133         goto cleanup;
134     }
135     if (EVP_PKEY_encrypt(ctx, buf, &buf_len, in, in_len) <= 0) {
136         fprintf(stderr, "EVP_PKEY_encrypt() failed.\n");
137         goto cleanup;
138     }
139     *out_len = buf_len;
140     *out = buf;
141     fprintf(stdout, "Encrypted:\n");
142     BIO_dump_indent_fp(stdout, buf, buf_len, 2);
143     fprintf(stdout, "\n");
144     ret = 1;
145 
146 cleanup:
147     if (!ret)
148         OPENSSL_free(buf);
149     EVP_PKEY_free(pub_key);
150     EVP_PKEY_CTX_free(ctx);
151     return ret;
152 }
153 
do_decrypt(OSSL_LIB_CTX * libctx,const char * in,size_t in_len,unsigned char ** out,size_t * out_len)154 static int do_decrypt(OSSL_LIB_CTX *libctx, const char *in, size_t in_len,
155                       unsigned char **out, size_t *out_len)
156 {
157     int ret = 0, public = 0;
158     size_t buf_len = 0;
159     unsigned char *buf = NULL;
160     const char *propq = NULL;
161     EVP_PKEY_CTX *ctx = NULL;
162     EVP_PKEY *priv_key = NULL;
163     OSSL_PARAM params[5];
164 
165     /* Get private key */
166     priv_key = get_key(libctx, propq, public);
167     if (priv_key == NULL) {
168         fprintf(stderr, "Get private key failed.\n");
169         goto cleanup;
170     }
171     ctx = EVP_PKEY_CTX_new_from_pkey(libctx, priv_key, propq);
172     if (ctx == NULL) {
173         fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey() failed.\n");
174         goto cleanup;
175     }
176 
177     /* The parameters used for encryption must also be used for decryption */
178     set_optional_params(params, propq);
179     /* If no optional parameters are required then NULL can be passed */
180     if (EVP_PKEY_decrypt_init_ex(ctx, params) <= 0) {
181         fprintf(stderr, "EVP_PKEY_decrypt_init_ex() failed.\n");
182         goto cleanup;
183     }
184     /* Calculate the size required to hold the decrypted data */
185     if (EVP_PKEY_decrypt(ctx, NULL, &buf_len, in, in_len) <= 0) {
186         fprintf(stderr, "EVP_PKEY_decrypt() failed.\n");
187         goto cleanup;
188     }
189     buf = OPENSSL_zalloc(buf_len);
190     if (buf == NULL) {
191         fprintf(stderr, "Malloc failed.\n");
192         goto cleanup;
193     }
194     if (EVP_PKEY_decrypt(ctx, buf, &buf_len, in, in_len) <= 0) {
195         fprintf(stderr, "EVP_PKEY_decrypt() failed.\n");
196         goto cleanup;
197     }
198     *out_len = buf_len;
199     *out = buf;
200     fprintf(stdout, "Decrypted:\n");
201     BIO_dump_indent_fp(stdout, buf, buf_len, 2);
202     fprintf(stdout, "\n");
203     ret = 1;
204 
205 cleanup:
206     if (!ret)
207         OPENSSL_free(buf);
208     EVP_PKEY_free(priv_key);
209     EVP_PKEY_CTX_free(ctx);
210     return ret;
211 }
212 
main(void)213 int main(void)
214 {
215     int ret = EXIT_FAILURE;
216     size_t msg_len = sizeof(msg) - 1;
217     size_t encrypted_len = 0, decrypted_len = 0;
218     unsigned char *encrypted = NULL, *decrypted = NULL;
219     OSSL_LIB_CTX *libctx = NULL;
220 
221     if (!do_encrypt(libctx, msg, msg_len, &encrypted, &encrypted_len)) {
222         fprintf(stderr, "encryption failed.\n");
223         goto cleanup;
224     }
225     if (!do_decrypt(libctx, encrypted, encrypted_len,
226                     &decrypted, &decrypted_len)) {
227         fprintf(stderr, "decryption failed.\n");
228         goto cleanup;
229     }
230     if (CRYPTO_memcmp(msg, decrypted, decrypted_len) != 0) {
231         fprintf(stderr, "Decrypted data does not match expected value\n");
232         goto cleanup;
233     }
234     ret = EXIT_SUCCESS;
235 
236 cleanup:
237     OPENSSL_free(decrypted);
238     OPENSSL_free(encrypted);
239     OSSL_LIB_CTX_free(libctx);
240     if (ret != EXIT_SUCCESS)
241         ERR_print_errors_fp(stderr);
242     return ret;
243 }
244