Introduction to the

This is a supplement to OpenSSL: simple and easy to use RSA encryption and decryption.

In a real project, it’s not as simple as the previous article, it’s much more complicated. There must be essential reasons and principles behind complex affairs, and we are Holmes who excavates the principles and explores the essence.

In today’s article, you will get involved in a real project using RSA encryption and decryption. Before reading the following, you are expected to download the source code for OpenSSL, or download the code sample from my previous article.

I downloaded the source version of openSSL-source-1.0f, which corresponds to the compiled version I downloaded from Precompiled – Openssl.

Description of project

The development language of the project is still C language, and we use OpenSSL to simulate cases in the actual project.

The server uses RSA to encrypt the original data, and then adopts Base64 encoding to transmit the encrypted data to the client through HTTP.

The client receives the data, decodes it using Base64, decrypts it using RSA, and finally gets the raw data.

Note that the size of the data received by the client may be larger than 128 bytes. As we know, the maximum length of RSA encryption plaintext is 117 bytes, and the maximum length of decryption is 128 bytes. Therefore, if the size is larger than this, the data needs to be decrypted in segments.

The general flow chart is as follows:

A little confused

Some friends may ask, the plaintext size encrypted for hairy RSA is 117 bytes, and the maximum number of bytes decrypted is 128 bytes, isn’t it better to have the same, at least easy to understand?

The above conclusion is based on the premise that the RSA key is 1024 bits, that is, 128 bytes (1024/8=128). Similarly, if the key is 512 bits, the maximum length of RSA decryption bytes should be (512/8) 64 bytes, and the maximum length of plaintext encrypted is (64-11) 53 bytes.

In the openSSL source code, we can see the following code:

# define RSA_PKCS1_PADDING_SIZE  11
Copy the code

You can see the rsa_sign function in the rsa_sign.c file:

int RSA_sign(int type, const unsigned char *m, unsigned int m_len,
             unsigned char *sigret, unsigned int *siglen, RSA *rsa)
{
    int encrypt_len, encoded_len = 0, ret = 0;
    unsigned char *tmps = NULL;
    const unsigned char *encoded = NULL;

    if (rsa->meth->rsa_sign) {
        return rsa->meth->rsa_sign(type, m, m_len, sigret, siglen, rsa);
    }

    /* Compute the encoded digest. */
    if (type == NID_md5_sha1) {
        /* * buta pile to the MD5/SHA1 combination in TLS 1.1 and * earlier. It has no DigestInfo wrapper but otherwise is * RSASSA-PKCS1-v1_5. */
        if(m_len ! = SSL_SIG_LENGTH) { RSAerr(RSA_F_RSA_SIGN, RSA_R_INVALID_MESSAGE_LENGTH);return 0;
        }
        encoded_len = SSL_SIG_LENGTH;
        encoded = m;
    } else {
        if(! encode_pkcs1(&tmps, &encoded_len, type, m, m_len))goto err;
        encoded = tmps;
    }

    if (encoded_len > RSA_size(rsa) - RSA_PKCS1_PADDING_SIZE) {
        RSAerr(RSA_F_RSA_SIGN, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY);
        goto err;
    }
    encrypt_len = RSA_private_encrypt(encoded_len, encoded, sigret, rsa,
                                      RSA_PKCS1_PADDING);
    if (encrypt_len <= 0)
        goto err;

    *siglen = encrypt_len;
    ret = 1;

err:
    OPENSSL_clear_free(tmps, (size_t)encoded_len);
    return ret;
}
Copy the code

As you can see, RSA_PKCS1_PADDING takes 11 bytes, so 127+11 is exactly 128 bytes.

The length of the plaintext encrypted by RSA is limited by the RSA padding mode, as shown in the following table:

Fill the way The input The output note
RSA_PKCS1_PADDING Modulus must be at least 11 bytes shorter than RSA key modulus RSA_size(RSA) -11, RSA_size(RSA)=128 bytes for 1024bit key, 128-11=117 bytes for plaintext. If the input plaintext is too long, it must be cut and then filled. As long as modulus The most common filling method
RSA_PKCS1_OAEP_PADDING RSA_size (rsa) – 41 As long as modulus The optimal asymmetric filling OAEP has the highest safety
RSA_NO_PADDING It can be as long as the RSA key module. If the input plaintext is too long, it must be cut and filled. As long as modulus

Note the following conclusions:

  • In different padding modes, the maximum length of data that can be encrypted with a key of the same length varies;
  • Under different key lengths, the maximum length of data that can be encrypted using the same padding mode is different.

Read the section on PKCS #1: RSA Encryption Version 1.5 in rfc2313.

The war

The actual code is mainly in the example_rsa3() function in the main.c file.

The raw data is a string www.veryitman.com, including the public and private keys below.

// The original data is a string of www.veryitman.com
	unsigned char plainText[] = "www.veryitman.com";

	unsigned char publicKey[] = "-----BEGIN PUBLIC KEY-----\n"
		"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrPgCMJW17JN2DW7tZFk/FB6pU\n"
		"pLvLOo6G/EuND8XZptffXbyiY2VscMRhP+kKVeaLO9HuEYR3Zl78x8oR6prytstc\n"
		"/MueersWDxh4iGSHsZXGxA41hXrXLRElrSTRc43ea18o0zMxZoVZiR2JFt7QcgM+\n"
		"T6eOrvj59MhXv9O46QIDAQAB\n"
		"-----END PUBLIC KEY-----\n";

	unsigned char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n"
		"MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKs+AIwlbXsk3YNb\n"
		"u1kWT8UHqlSku8s6job8S40Pxdmm199dvKJjZWxwxGE/6QpV5os70e4RhHdmXvzH\n"
		"yhHqmvK2y1z8y556uxYPGHiIZIexlcbEDjWFetctESWtJNFzjd5rXyjTMzFmhVmJ\n"
		"HYkW3tByAz5Pp46u+Pn0yFe/07jpAgMBAAECgYBj1YH8MtXhNVzveEuBZMCc3hsv\n"
		"vdq+YSU3DV/+nXN7sQmp77xJ8CjxT80t5VS38dy2z+lUImJYOhamyNPGHkC2y84V\n"
		"7i5+e6ScQve1gnwHqRKGBjtSCaYOqm9rTDECCTT1oMU26sfYznWlJqMrkJp1jWn7\n"
		"aAwr+3FcX2XhD74ZAQJBAN34Y6fmHLRPv21MsdgGqUjKgyFvJfLUmtFFgb6sLEWc\n"
		"k22J3BAFAcNCTLYHFZwMhL/nwaw9/7rIUJD+lcl6n3cCQQDFfrN14qKC3GJfoBZ8\n"
		"k9S6F7Ss514DDPzIuenbafhoUjZDVcjLw9EmYZQjpfsQ3WdNICUKRrDHZay1Pz+s\n"
		"YkKfAkB+OKfaquS5t/t/2LPsxuTuipIEqiKnMjSTOfYsidVnBEFlcZZc2awF76aV\n"
		"f/PO1+OJCO2910ebXBtMSCi++GbDAkEAmc7zNPwsVH4OnyquWJdJNSUBMSd/sCCN\n"
		"PkaMOrVtINHmMMq+dvMqEBoupRS/U4Ma0JYYQsiLJL+qof2AOWDNQQJAcquLGHLT\n"
		"eGDDLluHo+kkIGwZi4aK/fDoylZ0NCEtYyMtShQ3JmllST9kmb9NJX2gMsejsirc\n"
		"H6ObxqZPbka6UA==\n"
		"-----END RSA PRIVATE KEY-----\n";
Copy the code

Encrypt data with a private key as shown in the following example:

// Private key encryption
int encrypted_length = private_key_encrypt(plainText, len, privateKey, encrypted_str);
if (- 1 == encrypted_length)
{
	printf("Private Encrypt failed\n");
	exit(0);
}
Copy the code

After the private key is encrypted, Base64 encoding is performed:

char *base64_content;
size_t encrypted_str_length = strlen(encrypted_str);
int encode_res = mzc_base64_encode(encrypted_str, encrypted_str_length, &base64_content);
if (0! = encode_res) {printf("Base64 encode failed\n");
	exit(0);
}
printf("Base64 encode content: %s\n\n", base64_content);
printf("Base64 encode content's length: %i\n\n".strlen(base64_content));
Copy the code

At this point, the above two steps simulate the completion of server-side encryption. Let’s continue simulating client-side decryption.

First, Base64 decoding is performed on the Base64 encoded data.

char *base64DecodeOutput;
size_t decode_output_length;
int decode_res = mzc_base64_decode(base64_content, &base64DecodeOutput, &decode_output_length);
printf("base64 decode content: %s\n\n", base64DecodeOutput);
printf("base64 decode content's length: %i\n\n", decode_output_length);
if (0! = decode_res) {printf("Base64 decode failed\n");
    exit(0);
}
Copy the code

Take a look at the print:

base64 decode content's length: 160
Copy the code

Obviously, the length is larger than 128 and needs to be segmented.

// Maximum decryption length
#define RSA_MAX_DECRYPT_SIZE 128

// The length of each decrypted paragraph
int chunk = 0;
unsigned char tmp_dstr[RSA_MAX_DECRYPT_SIZE];
memset(tmp_dstr, '\ 0'.sizeof(tmp_dstr));

// Decrypt (public key decryption)
while (chunk <= decode_output_length)
{
    int decrypted_length = public_key_decrypt(base64DecodeOutput, RSA_MAX_DECRYPT_SIZE, publicKey, tmp_dstr);
    memcpy(decrypted_str, tmp_dstr, decrypted_length);
    printf("Current decrypted content length =%d\n", decrypted_length);
    if (- 1 == decrypted_length)
    {
        printf("Public Decrypt failed\n");
        exit(0);
    }
    chunk += decrypted_length;
}

printf("... \n\n");
printf("Final decrypted string =%s\n", decrypted_str);
Copy the code

Output result:

. Final decryptedstring =www.veryitman.com

Copy the code

At this point, the whole process is simply simulated.

If you are interested, you can implement the segmented encryption process. I’m not going to show you how to do that, but I’ll just add it to the source code.


Ask how much sorrow you can have, just like a river flowing east.