Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You should actually be using the higher-level "Envelope Encryption" functions from <code>openssl/evp.h</code>, rather than the low-level RSA functions directly. These do most of the work for you and mean you don't have to reinvent the wheel.</p> <p>In this case, you'd use the <code>EVP_SealInit()</code>, <code>EVP_SealUpdate()</code> and <code>EVP_SealFinal()</code> functions. The corresponding decryption functions are <code>EVP_OpenInit()</code>, <code>EVP_OpenUpdate()</code> and <code>EVP_OpenFinal()</code>. I would suggest using <code>EVP_aes_128_cbc()</code> as the value of the cipher type parameter.</p> <p>Once you've got the public key loaded into an <code>RSA *</code> handle, you use <code>EVP_PKEY_assign_RSA()</code> to put it into an <code>EVP_PKEY *</code> handle for the EVP functions.</p> <p>Once you've got this going, to solve the authentication problem I mentioned in my comment, you'll need to established a trusted authority ("Trent"). Trent's public key is known to all users (distributed with the application or similar - just load it from a PEM file). Instead of exchanging bare RSA parameters, Alice and Bob exchange x509 certificates that contain their RSA public keys together with their name, and are signed by Trent. Alice and Bob then each verify the certificate they recieved from the other (using Trent's public key, which they already know), including checking that the associated name is the right one, before continuing the protocol. OpenSSL includes functions for loading and verifying certificates in the <code>x509.h</code> header.</p> <hr> <p>Here's an example of how to use <code>EVP_Seal*()</code> to encrypt a file given the recipient's public key. It takes the PEM RSA Public Key file (ie as generated by <code>openssl rsa -pubout</code>) as a command line argument, reads the source data from stdin and writes the encrypted data to stdout. To decrypt, use <code>EVP_Open*()</code> instead, and <code>PEM_read_RSAPrivateKey()</code> to read a private key rather than public key.</p> <p>It's not really that hard - and certainly less error prone than messing about generating padding, IVs and so on yourself (the Seal function does both the RSA and AES parts of the deal). Anyway, the code:</p> <pre><code>#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;openssl/evp.h&gt; #include &lt;openssl/pem.h&gt; #include &lt;openssl/rsa.h&gt; #include &lt;openssl/err.h&gt; #include &lt;arpa/inet.h&gt; /* For htonl() */ int do_evp_seal(FILE *rsa_pkey_file, FILE *in_file, FILE *out_file) { int retval = 0; RSA *rsa_pkey = NULL; EVP_PKEY *pkey = EVP_PKEY_new(); EVP_CIPHER_CTX ctx; unsigned char buffer[4096]; unsigned char buffer_out[4096 + EVP_MAX_IV_LENGTH]; size_t len; int len_out; unsigned char *ek; int eklen; uint32_t eklen_n; unsigned char iv[EVP_MAX_IV_LENGTH]; if (!PEM_read_RSA_PUBKEY(rsa_pkey_file, &amp;rsa_pkey, NULL, NULL)) { fprintf(stderr, "Error loading RSA Public Key File.\n"); ERR_print_errors_fp(stderr); retval = 2; goto out; } if (!EVP_PKEY_assign_RSA(pkey, rsa_pkey)) { fprintf(stderr, "EVP_PKEY_assign_RSA: failed.\n"); retval = 3; goto out; } EVP_CIPHER_CTX_init(&amp;ctx); ek = malloc(EVP_PKEY_size(pkey)); if (!EVP_SealInit(&amp;ctx, EVP_aes_128_cbc(), &amp;ek, &amp;eklen, iv, &amp;pkey, 1)) { fprintf(stderr, "EVP_SealInit: failed.\n"); retval = 3; goto out_free; } /* First we write out the encrypted key length, then the encrypted key, * then the iv (the IV length is fixed by the cipher we have chosen). */ eklen_n = htonl(eklen); if (fwrite(&amp;eklen_n, sizeof eklen_n, 1, out_file) != 1) { perror("output file"); retval = 5; goto out_free; } if (fwrite(ek, eklen, 1, out_file) != 1) { perror("output file"); retval = 5; goto out_free; } if (fwrite(iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc()), 1, out_file) != 1) { perror("output file"); retval = 5; goto out_free; } /* Now we process the input file and write the encrypted data to the * output file. */ while ((len = fread(buffer, 1, sizeof buffer, in_file)) &gt; 0) { if (!EVP_SealUpdate(&amp;ctx, buffer_out, &amp;len_out, buffer, len)) { fprintf(stderr, "EVP_SealUpdate: failed.\n"); retval = 3; goto out_free; } if (fwrite(buffer_out, len_out, 1, out_file) != 1) { perror("output file"); retval = 5; goto out_free; } } if (ferror(in_file)) { perror("input file"); retval = 4; goto out_free; } if (!EVP_SealFinal(&amp;ctx, buffer_out, &amp;len_out)) { fprintf(stderr, "EVP_SealFinal: failed.\n"); retval = 3; goto out_free; } if (fwrite(buffer_out, len_out, 1, out_file) != 1) { perror("output file"); retval = 5; goto out_free; } out_free: EVP_PKEY_free(pkey); free(ek); out: return retval; } int main(int argc, char *argv[]) { FILE *rsa_pkey_file; int rv; if (argc &lt; 2) { fprintf(stderr, "Usage: %s &lt;PEM RSA Public Key File&gt;\n", argv[0]); exit(1); } rsa_pkey_file = fopen(argv[1], "rb"); if (!rsa_pkey_file) { perror(argv[1]); fprintf(stderr, "Error loading PEM RSA Public Key File.\n"); exit(2); } rv = do_evp_seal(rsa_pkey_file, stdin, stdout); fclose(rsa_pkey_file); return rv; } </code></pre> <hr> <p>The code you've posted illustrates nicely why you should use the higher-level functions - you've fallen into a couple of pitfalls:</p> <ul> <li><p><code>rand()</code> is emphatically <strong>not</strong> a cryptographically strong random number generator! Generating your symmetric key using <code>rand()</code> is enough to make the entire system completely insecure. (The <code>EVP_*()</code> functions generate the necessary random numbers themselves, using a cryptographically strong RNG, seeded from an appropriate entropy source).</p></li> <li><p>You are setting the IV for CFB mode to a fixed value (zero). This negates any advantage of using CFB mode in the first place (allowing attackers to trivially perform block-replacement attacks and worse). (The <code>EVP_*()</code> functions generate an appropriate IV for you, when required).</p></li> <li><p><code>RSA_PKCS1_OAEP_PADDING</code> should be used if you're defining a new protocol, rather than interoperating with an existing protocol.</p></li> </ul> <hr> <p>The corresponding decryption code, for posterity:</p> <pre><code>#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;openssl/evp.h&gt; #include &lt;openssl/pem.h&gt; #include &lt;openssl/rsa.h&gt; #include &lt;openssl/err.h&gt; #include &lt;arpa/inet.h&gt; /* For htonl() */ int do_evp_unseal(FILE *rsa_pkey_file, FILE *in_file, FILE *out_file) { int retval = 0; RSA *rsa_pkey = NULL; EVP_PKEY *pkey = EVP_PKEY_new(); EVP_CIPHER_CTX ctx; unsigned char buffer[4096]; unsigned char buffer_out[4096 + EVP_MAX_IV_LENGTH]; size_t len; int len_out; unsigned char *ek; unsigned int eklen; uint32_t eklen_n; unsigned char iv[EVP_MAX_IV_LENGTH]; if (!PEM_read_RSAPrivateKey(rsa_pkey_file, &amp;rsa_pkey, NULL, NULL)) { fprintf(stderr, "Error loading RSA Private Key File.\n"); ERR_print_errors_fp(stderr); retval = 2; goto out; } if (!EVP_PKEY_assign_RSA(pkey, rsa_pkey)) { fprintf(stderr, "EVP_PKEY_assign_RSA: failed.\n"); retval = 3; goto out; } EVP_CIPHER_CTX_init(&amp;ctx); ek = malloc(EVP_PKEY_size(pkey)); /* First need to fetch the encrypted key length, encrypted key and IV */ if (fread(&amp;eklen_n, sizeof eklen_n, 1, in_file) != 1) { perror("input file"); retval = 4; goto out_free; } eklen = ntohl(eklen_n); if (eklen &gt; EVP_PKEY_size(pkey)) { fprintf(stderr, "Bad encrypted key length (%u &gt; %d)\n", eklen, EVP_PKEY_size(pkey)); retval = 4; goto out_free; } if (fread(ek, eklen, 1, in_file) != 1) { perror("input file"); retval = 4; goto out_free; } if (fread(iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc()), 1, in_file) != 1) { perror("input file"); retval = 4; goto out_free; } if (!EVP_OpenInit(&amp;ctx, EVP_aes_128_cbc(), ek, eklen, iv, pkey)) { fprintf(stderr, "EVP_OpenInit: failed.\n"); retval = 3; goto out_free; } while ((len = fread(buffer, 1, sizeof buffer, in_file)) &gt; 0) { if (!EVP_OpenUpdate(&amp;ctx, buffer_out, &amp;len_out, buffer, len)) { fprintf(stderr, "EVP_OpenUpdate: failed.\n"); retval = 3; goto out_free; } if (fwrite(buffer_out, len_out, 1, out_file) != 1) { perror("output file"); retval = 5; goto out_free; } } if (ferror(in_file)) { perror("input file"); retval = 4; goto out_free; } if (!EVP_OpenFinal(&amp;ctx, buffer_out, &amp;len_out)) { fprintf(stderr, "EVP_OpenFinal: failed.\n"); retval = 3; goto out_free; } if (fwrite(buffer_out, len_out, 1, out_file) != 1) { perror("output file"); retval = 5; goto out_free; } out_free: EVP_PKEY_free(pkey); free(ek); out: return retval; } int main(int argc, char *argv[]) { FILE *rsa_pkey_file; int rv; if (argc &lt; 2) { fprintf(stderr, "Usage: %s &lt;PEM RSA Private Key File&gt;\n", argv[0]); exit(1); } rsa_pkey_file = fopen(argv[1], "rb"); if (!rsa_pkey_file) { perror(argv[1]); fprintf(stderr, "Error loading PEM RSA Private Key File.\n"); exit(2); } rv = do_evp_unseal(rsa_pkey_file, stdin, stdout); fclose(rsa_pkey_file); return rv; } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload