Search code examples
phpc++encryptionopensslrsa

RSA encryption using c++ (incorrect result)


I have PHP code, which make RSA encryption. This PHP code give me correct output. I need same result, but using c++. I try make c++ code, but got incorrect RSA encryption result. Not same like if using php code. What i need change in c++ code, for getting same result like on php? Thanks.

PHP code:

<?php
    include('Crypt/RSA.php');

    $publickey_mod = "981b4b1e5d0a4db79229c454d091ec76afb1e0b945ea68ee3402ab82450b7fc0b6030641dc8fb098f864f847cdb0fabf7d2f1514ddea140a4d51195cdbbc0ff05eb5bb3a708aed95c6481a6b120c308a577494eec0135cb3dd478c5b9279b95edf8fcd875dfb309a35d2454ba47ce7032961f41ea51dfc3b2a2ee776d2a60edbe33f02a82a4d40c323f4eca0802321d3e163b20ed7e44714c9f476ce51c0010f410cdfa7bb93524550e9e2390b5e470e8450f11a8840ca6eb0a9d343892fb87ae52c17af1720017e82055c3e1ea7e184923b9efce36e910a72886971db9f19eda6d3533de6e3949d92434730691209fc5e0a8f3ac525c7aca900782e6b2244b7";
    $publickey_exp = "010001";
    $password = "password";
        
    $rsa = new Crypt_RSA();
    $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
    $rsa->loadKey(array(
        'n' => new Math_BigInteger($publickey_mod, 16),
        'e' => new Math_BigInteger($publickey_exp, 16)
    ));
        
    echo "result=\"" . base64_encode($rsa->encrypt($password)) . "\"";
?>

PHP outputs:

#1
result="N29/RawYj7rbAc4347axmxlD5vAN2kUWDUURX7ADU1pr9+b9SC7i+TzqXyhI0MumVuQOD6uc/t1uwCbuNjz/X6W5scEBapemO4OZzjBJLrEf1gTA9wJKub5VE3dUhbrTWh30NsrL/ZZgVs9vp9kqCFXpJESU8OGfnfiiMBN0WSEZjSfieDe6hkn+00Hwjl1ZmouKMxpgBjmZlXIK7PkmLs6HBsScgnscCPUUX/vfPy/krpBVjaR3xgky0sbKvpZi6pwh0AjfAVbJk8nYS/5SuXEcORpRRm2PBQAk9/4cDiEsEqODBICzIerBln/3FnsI6OTOLHYkhZ5vQLyp6pTjvw=="

#2
result="U1Wy9TQP5K4DznmvuiLl90aPKIfxrzjAkQKGrp740NEh3mgWWtHPICJFa8ORm0LpJ5ZBoZi04CvHaYS+HAC5HTJn+S6xkp+8lX8xiurxDjAKWNQc5d/la9jNinB45DUQZOEiGg5u+Qpzgt48qNH3Y5PrwIMlDSil1u3YpuL+LMHwGCMD5dg5wsRX23HXJucGWOkRepST3+BLQns0Jp6ivoBOKH6wvFS41hYH3zK9d4vmuu7oKUl2c6G5G1A0f7SzFw2PsbQns/814dLr02vi/WAnD/N0sY221NlS5t9IApO2wCf0k/MmL69GixqPY8/bThJ2nmgYQktkAOCXouSQvA=="

#3
result="SF7o6F7/vVikGSzPl06yMwOc/si7EtMhbLm19/RSmuaFP+GcC6DHYGhk/BUOiwKb+ZzzxoMNFjum8J52z/ScvYEiEM7ieTDrqBnk1p/dTzWfGJ+Rs3+FsEYzrQ/l0Rh4s9TPC3s8V0dhO+eoRlT/8vyZMXe7win8XWbwBxQ6tDcemzjLAiT/c44re6qBidzDGd7kP/Vxd/ocy712/1fFP2Rm5O4YHb/5AulG5ISxDvUa98TQuGWBeV1j5cBzwodFi6sWcVR9LJtpOCjf4GH5GpO5rGEBDMB7DHwrwhTHMulKC2JxdAi+7SJtI1nSlN/AHyrIXPPADsgINH+qPFl+eA=="

#4
result="lIduaeOFDnOb1ddXJQ29RD/4j2iQsrcWgZUsLPrz+B51Xw4VSkO4fZJngqUkBw1mxaG9lvCGeID5j1v2Bv9ki9sfmApcGqV7aAPjHkiTBPvDjpWscGJlZXaHJfg1L/oBheF9mJ08uJzI5KAy6+TtByLrMhcLi077tj0rHvM92zqQhHHooGWaqbnWFzBlLT3Ham5ZcpHXbAnSYwWVc7ILno0Y/poCm+TGG8taXZXFZqEzYvKhDgm+LVHqZfpEE8rnQNfwLgJAZLsE/dWQvtFa/rHjcCA+O6qO2mFnVjg8i3iIzy4gHs12wb+BlLglrWvV1cl6XvNc1mGfDy4H324ptA=="

#5
result="KEgbzIA91SUS7c6sI3Zt4fx4LAcBZs6vth8iYqkZWPMjY+irb5oN4EihbZeR+9DbzTH4RidQBIj8Woj8cqo5tGSka29Bg+eBjIRjQozMlclEiCPg5YG1im5ozCOsTG/rHJr4IZZq1O2Qkh+uV4wvtJaym3H977qgfeHhwhSYbsQeqeyHZy5ieiVKdqAF1WvgHuBfEu6u2eXRoJt6JhmZA+4lZ5zGj6+Efj8njW+4cP+FBArdKZbkj+9UUMtiauYzX3nmVKAiMaVIkQidI+lTeaqOZzANGuqp9Ex8sYkeGLcpDELvC6wdBRaWr+dX9cuEtgeBIT24MkFGmiyc6CmLOA=="

c++ (openssl) code:

#include <string.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/opensslconf.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/rc4.h>
#include <openssl/bio.h>
#include <openssl/evp.h>

std::string base64_encode(const std::string& input)
{
    const auto base64_memory = BIO_new(BIO_s_mem());
    auto base64 = BIO_new(BIO_f_base64());
    base64 = BIO_push(base64, base64_memory);
    BIO_write(base64, input.c_str(), static_cast<int>(input.length()));
    BIO_flush(base64);
    BUF_MEM* buffer_memory{};
    BIO_get_mem_ptr(base64, &buffer_memory);
    auto base64_encoded = std::string(buffer_memory->data, buffer_memory->length - 1);
    BIO_free_all(base64);
    return base64_encoded;
}

int main()
{
    char key[] = "981b4b1e5d0a4db79229c454d091ec76afb1e0b945ea68ee3402ab82450b7fc0b6030641dc8fb098f864f847cdb0fabf7d2f1514ddea140a4d51195cdbbc0ff05eb5bb3a708aed95c6481a6b120c308a577494eec0135cb3dd478c5b9279b95edf8fcd875dfb309a35d2454ba47ce7032961f41ea51dfc3b2a2ee776d2a60edbe33f02a82a4d40c323f4eca0802321d3e163b20ed7e44714c9f476ce51c0010f410cdfa7bb93524550e9e2390b5e470e8450f11a8840ca6eb0a9d343892fb87ae52c17af1720017e82055c3e1ea7e184923b9efce36e910a72886971db9f19eda6d3533de6e3949d92434730691209fc5e0a8f3ac525c7aca900782e6b2244b7";

    char palavra[] = "password";
    char crip[512];
    RSA* pubkey = RSA_new();
    BIGNUM* modul = BN_new();
    BIGNUM* expon = BN_new();

    BN_hex2bn(&modul, (const char*)key);
    BN_hex2bn(&expon, "010001");

    RSA_set0_key(pubkey, modul, expon, NULL);

    RSA_public_encrypt(strlen((const char*)palavra), (const unsigned char*)palavra, (unsigned char*)crip, pubkey, RSA_PKCS1_PADDING);

    //std::cout << "result: " +  base64_encode(crip) << std::endl;
    std::cout << "result: " << crip << std::endl;

    return 0;
}

C++ outputs:

#1
result: SNpRW2q1crBPVLPYJ3ZmRm+OaVJkXeHYhQgHZ3Oo+eKe/A==

#2
result: OO7UtCuhjtuh1CkiJyw//5iyKrCMjvHSQOfD3XM3IPIRxFwRfYm1vUoOQZdiJcNs6zdSkcBaHrvnQ/+vwtSmqKkv35NOacoK7cXNpG/i+L+zqYtFRc4/ryPHE5gLo25tHO5g+xHnls5yZc+AD0+IKJ1NJYL20Z5RzXUNYVOEG2zkUa8=

#3
result: IaHXTdFcrWF6eWA2fKsRf/7wEf87Rn0n9zld4jzCySX2msvE9MFPwaaD3eqlfpR3x8wcVyKBVhxaqSDPacBMqpCQOTJI5z9k78E8dkdUtbysfM/jdqVNv+ClSAFAN9R2mtKLaHCPBuYQzvHomwuhxagHLULOGfqKGVSM+hRmL+MK8Eux4xcUvTrarY4YGUbBiJk21m6aYELfpxls/yT1pxI1U3j3odIM4F/RkO3iJ03kVMFSItc3k81E9iZPPC1DowpaD81iZrMCmQbh9PH3U8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzHBhc3N3b3Jk

#4
result: BjuGPZe7QHOBMKKXMh8pDlddIbT9NOynRYOi9lGTDVb9YTZ8/X+YIHJKSGWdQ1/OCYgW

#5
result: B4wPPRrrAe5zplMoCcxtf3tzP3y7eC/fkE4cBHRL26clYjzEVyjMzPst/f1zWH++NQcEu8w8m3Fb1OSKdhGkn8gebFxPPZpautAquGDImfl0ftWYtnj2FrNDER4=

enter image description here


Solution

  • The behavior is actually caused by two counteracting bugs. The first bug always occurs, results in a constant lengthening of the output, and is the cause of the garbage at the end.
    The second bug does not always occur, results in a varying shortening of the output and, when it occurs, conceals the first bug.

    Bug 1: The modulus 98...b7 consists of 512 hex digits and thus has a size of 256 bytes (2048 bits). Since for RSA the size of the ciphertext is equal to the size of the modulus, crisp must be only 256 bytes and not 512 bytes as in the current code.
    This oversized crisp is the cause of the lengthened ciphertext. With the reduction to 256 bytes RSA_public_encrypt() performs a correct RSA encryption with PKCS#1 v1.5 padding and creates ciphertexts with correct length, i.e. 256 bytes.

    Bug 2: The string constructor implicitly used when passing the ciphertext to the base64_encode() function is only suitable for 0x00 terminated strings, not for binary data like ciphertexts which may contain 0x00 values. As already suspected in the comment, if the ciphertext contains 0x00 values, only the first null-terminated string is passed and the rest is lost.
    So that the full size of the data is taken into account, the size of the ciphertext must be specified explicitly. For this, the appropriate string constructor must be used (see here, (5) from buffer, and here). With this change, the Base64 encoded ciphertexts have the same size, analogous to the PHP code.

    The complete changes are:

    #define SIZE 256
    ..
    char crip[SIZE]; // fixes bug 1
    ...
    std::cout << "result:\n" + base64_encode(std::string(crip, SIZE)) << std::endl; // fixes bug 2
    

    Example for an output:

    result:
    LqUnij6tVy+Qprw9nyUeyYgZYwmz/GZ3/A/EVbaDA27ORHzc8tkHWTCVk7DssBSf
    LyPRmrBhyUGZMFBjpLH5Iz1VKBacR36UeNif+GzoYdrtmOaSzDs+B74isAToJngJ
    0X7hXIOFpMfQylrSFRFoajaBld+edBH9bOQ/U7ogQkoMDHKkTgT07MXj+p7s6dIe
    K8E39QIZj5lF85i14MJwEvUBpDKyx3lWjq32d+VJOFHX65g2SQuTugQeAs6+eG7s
    e7KDmExvvS+oGA6rInT+icfRN6CKskp4TCto1NgfwtVMVzEfbsHU2aZjcA3EAKSR
    Pq3EHfcW3Z2wSy2EjzIbcw==