RSA加密解密

Java、JS、C++ RAS加密解密代码实现。

JavaScript RSA加解密

第三方相关组件

npm install node-rsa

代码实现-Java Script

  • 定义方法
const NodeRSA = require("node-rsa")

function getRSAKeyPair(){
    // 生成空对象
    let keyPair = new NodeRSA()
    keyPair.setOptions({
        encryptionScheme:"pkcs1"
    })
    // keyPairObj, 保存经 BASE64 编码处理之后 PEM 格式的 RSA 密钥对
    let keyPairObj = {
        publicKey: '',
        privateKey: ''
    }
    // keySize: 2048 公指数为:65537
    keyPair.generateKeyPair(2048, 65537)
    /**
     * 导出密钥,对输出的密钥做一些格式化处理,以便 Java 端能直接使用,算然经过处理但是并不影响 JS 端的密钥导入,及正确性。
     * 1. 公钥
     * 2. 私钥
     */
    keyPairObj.publicKey = keyPair.exportKey("pkcs8-public-pem").replace(/-----BEGIN PUBLIC KEY-----/, '').replace(/-----END PUBLIC KEY-----/, '').replace(/\n/g, '')
    keyPairObj.privateKey = keyPair.exportKey("pkcs8-private-pem").replace(/-----BEGIN PRIVATE KEY-----/, '').replace(/-----END PRIVATE KEY-----/, '').replace(/\n/g, '')
    return keyPairObj
}

/**
 * 公钥加密
 * @param content 要加密的内容
 * @param publicKey 加密用的公钥
 * @param encoding
 * @param source_encoding
 * @returns {string|Buffer}
 */
function publicKeyEncrypt(content, publicKey, encoding = "base64", source_encoding = "utf8"){
    // 导入 publicKey
    let key = new NodeRSA()
    key.setOptions({
        encryptionScheme:"pkcs1", // 默认是:pkcs1_oaep,Java 端默认是 pkcs1, 这里做个修改
    })
    key.importKey(publicKey, "pkcs8-public-pem")
    // 加密并返回加密结果
    return key.encrypt(content, encoding, source_encoding)
}

/**
 * 私钥解密,解密之后 返回 utf8编码的字符串
 * @param content: 公钥加密后的base64内容
 * @param privateKey: 解密用的私钥,格式是:pkcs8 pem
 * @param encoding: 加密之后的类型 buffer OR json, 默认是 buffer
 * @returns:默认返回值类型就是 encoding 的默认值,即 buffer
 */
function privateKeyDecrypt(content, privateKey, encoding = "buffer") {
    // 导入 privateKey
    let key = new NodeRSA()
    key.setOptions({
        encryptionScheme: "pkcs1", // 默认是:pkcs1_oaep,Java 端默认是 pkcs1, 这里做个修改
    })
    key.importKey(privateKey, "pkcs8-private-pem")
    // 解密
    return key.decrypt(content, encoding)
}
  • 测试代码
// RSA 测试
let content = "kldfajdjfjsdf+-**?/calk@1239"
// 生成密钥对
let keyPair = getRSAKeyPair();
let encrypted = publicKeyEncrypt(content, keyPair.publicKey);
let decrypted = privateKeyDecrypt(encrypted, keyPair.privateKey);
console.log("公钥:", keyPair.publicKey)
console.log("私钥:", keyPair.privateKey)
console.log("公钥加密密文(经 BASE64 处理):" + encrypted)
console.log("私钥解密之后的明文:" + decrypted.toString('utf8'))
  • 运行测试代码

node 你的js文件名.js

公钥: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtnAl229x43yLJVn26PifLbpflYkwOuYzaOS+ij5hmgjmsjwKsQTvB60WA9ET9qgP1Y4EX1LJoAUfGOg5c4fgDloUz5bq6rRI7+GVfXatOIMfaTdWYxCW/EGUE3IBci8dq14sY3neXALzhwKQLsirt76MAGojDQ3VoL5wEp/ZbE9QxUo4Fj9rLIfjEy+/MWXzUzedh1hqAJdtIydLUdXd4oVordK1UKXAc3g9K3rk3ZbE8wTe7tz5dh0XQr9lbo8+eR4X8b6cJT/bzhZsffZs55ZjvdNIcMdaT1IxycypmmUZCNAVl+KGQ0hiAA1CG/Qk6R0ep1fPwfEk0xgrkdwIDAQAB
私钥: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC2cCXbb3HjfIslWfbo+J8tul+ViTA65jNo5L6KPmGaCOayPAqxBO8HrRYD0RP2qA/VjgRfUsmgBR8Y6Dlzh+AOWhTPlurqtEjv4ZV9dq04gx9pN1ZjEJb8QZQTcgFyLx2rXixjed5cAvOHApAuyKu3vowAaiMNDdWgvnASn9lsT1DFSjgWsh+P+IETL78xZfNTN52HWGoAl20jJ0tR1d3ihWit0rVQpcBzeD0reuTdlsTzBN7u3Pl2HRdCv2Vujz55HhfxvpwlP9vOFmx99mznlmO900hwx1pPUjHJzKmaZRkI0BWX4oZDSGIADUIb9CTpHR6nV8/B8STTGCuR3AgMBAAECggEAbHw11s3ngGnf6F8d9x1Ysn2AN1Cz8GJRMc/MCjLcIRen71bK2MbhSMRBMhXS9Dqt58SijviQ0uoYutvkxfGHZ6FjfKn2mYiJUrPnDAtVKCS4uf1hDlmgHvL0FbgWagrgbrX6WVDvy/EpVSBOErPbgND7OYfjvfB7FzIHPNv3E3L2W2ZMuUaQC+MW8Ui+m7S0hgaCb4TRTEAfaHhYkIIMmy4YMMEweSFwTulZ99dUzDI4U+3MT9W+eMaq/kPk9nLdZJB4I+Q7KHDP6OiUbrU4qQEkGeISkeFO46NP0wgxYHB+jHiXo9Dbr2mnernDAIBltkXSZ4dm5/6Ig2UDzHw6qQKBgQD+TCQ63AOZ4sP4Z171iLFw9zBiH+lvuEqfyTMPP0VKM98pmGSs6m/Co1GSRrzhXNu4g8B+mH4XvxJJoDDoe/4mAUCLomPTOaOMOAzON8HddGhTCu5uKKjsFIYsP7fgL9wleiU8RQpA/HnOIbkR4DgkbHxG8cEyVXSN/lA/p0eSwwKBgQC3qNdsW5osHwNxiN7OGaxH6KCsKHnEDDYACKDVnWAlyePopSq/YWAa9Txnn4SfDDHgnXAsSmJgRciqCYzFFR8CUmKitQcn0g21KwpRgUKEWS8jFcXL1gtzn9T7wyedIWQOks7UztXw5c/ckXosotA+NR0EYNhLN8Gd0tp9QKikPQKBgGVL+1qe7ahNFndK5h60lspDDTjpMVnZdZB02ywFrhpC03CETgY7VM2ZtRKpru+ux6+WfAVxxTi8WhKOBy10xuZotUdqJo2rdiSS5zb2KvmGN1BtGFE1TSTNgCz8LMjXB8C+CTsa3VzJtdOQx2IBiRupQ1AXWTgtpkIxvdG9Vtt1AoGACXGrMi07rk0h9jKdkmeLjl8N9AxYxpYCKNpIuvNOwGKzT4E+w3yUXo6BvqpbPXp5Dh3KaNyiVmUAPo4jKPc/EvV9kPRSG+AXSFugOjS1YOzdGabC9wpYqGdtokyUuuW+qWkvLY49PhfzKlpot+bfYnOEvHbmaGfDkwQGrC4o/dkCgYAJe6Bn4pvnLWXYHaDmARjP/VEGq1i+8OgiJcVi+eNorkDoQjcGjo2ETCr3F3Z28QFEG14C9kdHQXKQNPKawzbzoMG40TqXmS9aw4rkIFX/6MtUugUyjmbYBmsHyuVE5AYfYsYY6AqPArCf2x7rFPKhdHZVHcJ1GZgMl0P1Tydg3A==
公钥加密密文(经 BASE64 处理):FBlv0Zzi1LFn5AWRC3yxnfX74/aHhU8EB0viBJJiDbXfGCecahOXh0T48XzX1mbaIWf/MtgJJek2/oUN/+XCZ6CYFkYwcVLZ+H7QSDSARCcjrLyYQtZhhBDkXkrwfqO2VlD5JM+cMOTGiVZWFxTH884ztLzAMWmagNJ7Rg4Ej1F+BFFehGdMK24FZ5qP2s3V3aLppJCI0ssXx0aTRGwyjswEwr2utslcEMlLjp/NBJti9R3o4FoXmz0sR00l64kTRQoB8HCV1d2Su2oDs78SYJ+wOs5t4katFuUDc1XGB/xs+N9sqz3rI8yLfpHqS7pEs33CmBhR6BNg==
私钥解密之后的明文:kldfajdjfjsdf+-**?/calk@1239

Java RSA加解密

代码实现-Java


import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RsaUtil {
    public static final String RSA = "RSA";
    /**
     * 生成密钥对:密钥对中包含公钥和私钥
     * @return 包含 RSA 公钥与私钥的 keyPair
     */
    public static KeyPair getKeyPair() throws Exception {
        // 获得RSA密钥对的生成器实例
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);
        // 一个安全的随机数
        SecureRandom secureRandom = new SecureRandom(String.valueOf(System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8));
        // 这里可以是1024、2048 初始化一个密钥对
        keyPairGenerator.initialize(2048, secureRandom);
        // 获得密钥对
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 获取公钥 (并进行 Base64 编码,返回一个 Base64 编码后的字符串)
     * @param keyPair:RSA 密钥对
     * @return 返回一个 Base64 编码后的公钥字符串
     */
    public static String getPublicKey(KeyPair keyPair){
        PublicKey publicKey = keyPair.getPublic();
        byte[] bytes = publicKey.getEncoded();
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 获取私钥(并进行Base64编码,返回一个 Base64 编码后的字符串)
     * @param keyPair:RSA 密钥对
     * @return 返回一个 Base64 编码后的私钥字符串
     */
    public static String getPrivateKey(KeyPair keyPair){
        PrivateKey privateKey = keyPair.getPrivate();
        byte[] bytes = privateKey.getEncoded();
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 将 Base64 编码后的公钥转换成 PublicKey 对象
     * @param pubStr:Base64 编码后的公钥字符串
     * @return PublicKey
     */
    public static PublicKey string2PublicKey(String pubStr) throws Exception {
        byte[] bytes = Base64.getDecoder().decode(pubStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * 将 Base64 码后的私钥转换成 PrivateKey 对象
     * @param priStr:Base64 编码后的私钥字符串
     * @return PrivateKey
     */
    public static PrivateKey string2PrivateKey(String priStr) throws Exception {
        byte[] bytes = Base64.getDecoder().decode(priStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 公钥加密
     * @param content 待加密的内容 byte[]
     * @param publicKey 加密所需的公钥对象 PublicKey
     * @return 加密后的字节数组 byte[]
     */
    public static byte[] publicKeyEncrypt(byte[] content, PublicKey publicKey) throws Exception {
        return rsa(publicKey,content,Cipher.ENCRYPT_MODE);
    }

    /**
     * 私钥解密
     * @param content   待解密的内容 byte[],这里要注意,由于我们中间过程用的都是 BASE64 ,所以在传入参数前应先进行 BASE64 解析
     * @param privateKey    解密需要的私钥对象 PrivateKey
     * @return 解密后的字节数组 byte[],这里是元数据,需要根据情况自行转码
     */
    public static byte[] privateKeyDecrypt(byte[] content, PrivateKey privateKey) {
        return rsa(privateKey,content,Cipher.DECRYPT_MODE);
    }
    /**
     * rsa 加、解密
     *
     * @param key  key
     * @param data 数据
     * @param mode 模式
     * @return 解密后的数据
     */
    private static byte[] rsa(Key key, byte[] data, int mode) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(mode, key);
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception{
        // 待加密内容
        String content = "ksjalfjksdjfsf+-**?/calk@1239";
        KeyPair keyPair = getKeyPair();
        try{
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            System.out.println("公钥为(BASE64处理后):"+ getPublicKey(keyPair));
            System.out.println("私钥为(BASE64处理后):"+ getPrivateKey(keyPair));
            // 加密
            byte[] publicKeyEncrypt = publicKeyEncrypt(content.getBytes(), publicKey);
            String base64PublicKeyEncrypt = Base64.getEncoder().encodeToString(publicKeyEncrypt);
            System.out.println("公钥加密密文(BASE64 处理之后):"+base64PublicKeyEncrypt);
            // 解密
            byte[] privateKeyDecrypt = privateKeyDecrypt(Base64.getDecoder().decode(base64PublicKeyEncrypt), privateKey);
            String decrypted = new String(privateKeyDecrypt, StandardCharsets.UTF_8);
            System.out.println("私钥解密之后((UTF-8 处理之后):"+ decrypted);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

测试结果

公钥: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtnAl229x43yLJVn26PifLbpflYkwOuYzaOS+ij5hmgjmsjwKsQTvB60WA9ET9qgP1Y4EX1LJoAUfGOg5c4fgDloUz5bq6rRI7+GVfXatOIMfaTdWYxCW/EGUE3IBci8dq14sY3neXALzhwKQLsirt76MAGojDQ3VoL5wEp/ZbE9QxUo4Fj9rLIfjEy+/MWXzUzedh1hqAJdtIydLUdXd4oVordK1UKXAc3g9K3rk3ZbE8wTe7tz5dh0XQr9lbo8+eR4X8b6cJT/bzhZsffZs55ZjvdNIcMdaT1IxycypmmUZCNAVl+KGQ0hiAA1CG/Qk6R0ep1fPwfEk0xgrkdwIDAQAB
私钥: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC2cCXbb3HjfIslWfbo+J8tul+ViTA65jNo5L6KPmGaCOayPAqxBO8HrRYD0RP2qA/VjgRfUsmgBR8Y6Dlzh+AOWhTPlurqtEjv4ZV9dq04gx9pN1ZjEJb8QZQTcgFyLx2rXixjed5cAvOHApAuyKu3vowAaiMNDdWgvnASn9lsT1DFSjgWsh+P+IETL78xZfNTN52HWGoAl20jJ0tR1d3ihWit0rVQpcBzeD0reuTdlsTzBN7u3Pl2HRdCv2Vujz55HhfxvpwlP9vOFmx99mznlmO900hwx1pPUjHJzKmaZRkI0BWX4oZDSGIADUIb9CTpHR6nV8/B8STTGCuR3AgMBAAECggEAbHw11s3ngGnf6F8d9x1Ysn2AN1Cz8GJRMc/MCjLcIRen71bK2MbhSMRBMhXS9Dqt58SijviQ0uoYutvkxfGHZ6FjfKn2mYiJUrPnDAtVKCS4uf1hDlmgHvL0FbgWagrgbrX6WVDvy/EpVSBOErPbgND7OYfjvfB7FzIHPNv3E3L2W2ZMuUaQC+MW8Ui+m7S0hgaCb4TRTEAfaHhYkIIMmy4YMMEweSFwTulZ99dUzDI4U+3MT9W+eMaq/kPk9nLdZJB4I+Q7KHDP6OiUbrU4qQEkGeISkeFO46NP0wgxYHB+jHiXo9Dbr2mnernDAIBltkXSZ4dm5/6Ig2UDzHw6qQKBgQD+TCQ63AOZ4sP4Z171iLFw9zBiH+lvuEqfyTMPP0VKM98pmGSs6m/Co1GSRrzhXNu4g8B+mH4XvxJJoDDoe/4mAUCLomPTOaOMOAzON8HddGhTCu5uKKjsFIYsP7fgL9wleiU8RQpA/HnOIbkR4DgkbHxG8cEyVXSN/lA/p0eSwwKBgQC3qNdsW5osHwNxiN7OGaxH6KCsKHnEDDYACKDVnWAlyePopSq/YWAa9Txnn4SfDDHgnXAsSmJgRciqCYzFFR8CUmKitQcn0g21KwpRgUKEWS8jFcXL1gtzn9T7wyedIWQOks7UztXw5c/ckXosotA+NR0EYNhLN8Gd0tp9QKikPQKBgGVL+1qe7ahNFndK5h60lspDDTjpMVnZdZB02ywFrhpC03CETgY7VM2ZtRKpru+ux6+WfAVxxTi8WhKOBy10xuZotUdqJo2rdiSS5zb2KvmGN1BtGFE1TSTNgCz8LMjXB8C+CTsa3VzJtdOQx2IBiRupQ1AXWTgtpkIxvdG9Vtt1AoGACXGrMi07rk0h9jKdkmeLjl8N9AxYxpYCKNpIuvNOwGKzT4E+w3yUXo6BvqpbPXp5Dh3KaNyiVmUAPo4jKPc/EvV9kPRSG+AXSFugOjS1YOzdGabC9wpYqGdtokyUuuW+qWkvLY49PhfzKlpot+bfYnOEvHbmaGfDkwQGrC4o/dkCgYAJe6Bn4pvnLWXYHaDmARjP/VEGq1i+8OgiJcVi+eNorkDoQjcGjo2ETCr3F3Z28QFEG14C9kdHQXKQNPKawzbzoMG40TqXmS9aw4rkIFX/6MtUugUyjmbYBmsHyuVE5AYfYsYY6AqPArCf2x7rFPKhdHZVHcJ1GZgMl0P1Tydg3A==
公钥加密密文(经 BASE64 处理):FBlv0Zzi1LFn5AWRC3yxnfX74/aHhU8EB0viBJJiDbXfGCecahOXh0T48XzX1mbaIWf/MtgJJek2/oUN/+XCZ6CYFkYwcVLZ+H7QSDSARCcjrLyYQtZhhBDkXkrwfqO2VlD5JM+cMOTGiVZWFxTH884ztLzAMWmagNJ7Rg4Ej1F+BFFehGdMK24FZ5qP2s3V3aLppJCI0ssXx0aTRGwyjswEwr2utslcEMlLjp/NBJti9R3o4FoXmz0sR00l64kTRQoB8HCV1d2Su2oDs78SYJ+wOs5t4katFuUDc1XGB/xs+N9sqz3rI8yLfpHqS7pEs33CmBhR6BNg==
私钥解密之后的明文:kldfajdjfjsdf+-**?/calk@1239

C++ RSA加解密

第三方库

  • openssl
  • 下载openssl-1.1.1k.tar.gz
  • 安装:
    1. 解压文件至任意目录:如 /home/openssl-1.1.1k
    2. cd /home/openssl-1.1.1k
    3. ./config
    4. sudo make install
    5. g++ *.cpp -L /home/openssl-1.1.1k -l ssl -l crypto

代码实现-C++

源代码地址(https://gist.github.com/superwills/5415344)

CMakeLists.txt内容

cmake_minimum_required(VERSION 3.19)
project(c_test)
set(CMAKE_CXX_STANDARD 14)
set(INC_DIR /usr/local/opt/openssl/include)
set(LINK_DIR /usr/local/opt/openssl/lib)
include_directories(${INC_DIR})
link_directories(${LINK_DIR})
link_libraries(ssl crypto)
add_executable(c_test main.cpp base_64.h base_64.cpp base64.h)

base64.h

/*
  https://github.com/superwills/NibbleAndAHalf
  base64.h -- Fast base64 encoding and decoding.
  version 1.0.0, April 17, 2013 143a
  Copyright (C) 2013 William Sherif
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.
  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:
  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
  William Sherif
  will.sherif@gmail.com
  YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
*/
#ifndef BASE64_H
#define BASE64_H

#include <stdio.h>
#include <stdlib.h>

const static char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// maps A=>0,B=>1..
const static unsigned char unb64[] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40
        0, 0, 0, 62, 0, 0, 0, 63, 52, 53, //50
        54, 55, 56, 57, 58, 59, 60, 61, 0, 0, //60
        0, 0, 0, 0, 0, 0, 1, 2, 3, 4, //70
        5, 6, 7, 8, 9, 10, 11, 12, 13, 14, //80
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, //90
        25, 0, 0, 0, 0, 0, 0, 26, 27, 28, //100
        29, 30, 31, 32, 33, 34, 35, 36, 37, 38, //110
        39, 40, 41, 42, 43, 44, 45, 46, 47, 48, //120
        49, 50, 51, 0, 0, 0, 0, 0, 0, 0, //130
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //250
        0, 0, 0, 0, 0, 0,
}; // This array has 256 elements

// Converts binary data of length=len to base64 characters.
// Length of the resultant string is stored in flen
// (you must pass pointer flen).
char *base64(const void *binaryData, int len, int *flen) {
    const unsigned char *bin = (const unsigned char *) binaryData;
    char *res;

    int rc = 0; // result counter
    int byteNo; // I need this after the loop

    int modulusLen = len % 3;
    int pad = ((modulusLen & 1) << 1) + ((modulusLen & 2) >> 1); // 2 gives 1 and 1 gives 2, but 0 gives 0.

    *flen = 4 * (len + pad) / 3;
    res = (char *) malloc(*flen + 1); // and one for the null
    if (!res) {
        puts("ERROR: base64 could not allocate enough memory.");
        puts("I must stop because I could not get enough");
        return 0;
    }

    for (byteNo = 0; byteNo <= len - 3; byteNo += 3) {
        unsigned char BYTE0 = bin[byteNo];
        unsigned char BYTE1 = bin[byteNo + 1];
        unsigned char BYTE2 = bin[byteNo + 2];
        res[rc++] = b64[BYTE0 >> 2];
        res[rc++] = b64[((0x3 & BYTE0) << 4) + (BYTE1 >> 4)];
        res[rc++] = b64[((0x0f & BYTE1) << 2) + (BYTE2 >> 6)];
        res[rc++] = b64[0x3f & BYTE2];
    }

    if (pad == 2) {
        res[rc++] = b64[bin[byteNo] >> 2];
        res[rc++] = b64[(0x3 & bin[byteNo]) << 4];
        res[rc++] = '=';
        res[rc++] = '=';
    } else if (pad == 1) {
        res[rc++] = b64[bin[byteNo] >> 2];
        res[rc++] = b64[((0x3 & bin[byteNo]) << 4) + (bin[byteNo + 1] >> 4)];
        res[rc++] = b64[(0x0f & bin[byteNo + 1]) << 2];
        res[rc++] = '=';
    }

    res[rc] = 0; // NULL TERMINATOR! ;)
    return res;
}

unsigned char *unbase64(const char *ascii, int len, int *flen) {
    const unsigned char *safeAsciiPtr = (const unsigned char *) ascii;
    unsigned char *bin;
    int cb = 0;
    int charNo;
    int pad = 0;

    if (len < 2) { // 2 accesses below would be OOB.
        // catch empty string, return NULL as result.
        puts("ERROR: You passed an invalid base64 string (too short). You get NULL back.");
        *flen = 0;
        return 0;
    }
    if (safeAsciiPtr[len - 1] == '=') ++pad;
    if (safeAsciiPtr[len - 2] == '=') ++pad;

    *flen = 3 * len / 4 - pad;
    bin = (unsigned char *) malloc(*flen);
    if (!bin) {
        puts("ERROR: unbase64 could not allocate enough memory.");
        puts("I must stop because I could not get enough");
        return 0;
    }

    for (charNo = 0; charNo <= len - 4 - pad; charNo += 4) {
        int A = unb64[safeAsciiPtr[charNo]];
        int B = unb64[safeAsciiPtr[charNo + 1]];
        int C = unb64[safeAsciiPtr[charNo + 2]];
        int D = unb64[safeAsciiPtr[charNo + 3]];

        bin[cb++] = (A << 2) | (B >> 4);
        bin[cb++] = (B << 4) | (C >> 2);
        bin[cb++] = (C << 6) | (D);
    }

    if (pad == 1) {
        int A = unb64[safeAsciiPtr[charNo]];
        int B = unb64[safeAsciiPtr[charNo + 1]];
        int C = unb64[safeAsciiPtr[charNo + 2]];

        bin[cb++] = (A << 2) | (B >> 4);
        bin[cb++] = (B << 4) | (C >> 2);
    } else if (pad == 2) {
        int A = unb64[safeAsciiPtr[charNo]];
        int B = unb64[safeAsciiPtr[charNo + 1]];

        bin[cb++] = (A << 2) | (B >> 4);
    }

    return bin;
}

#endif

main.cpp

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/rsa.h>
#include <openssl/engine.h>
#include <openssl/pem.h>

// I'm not using BIO for base64 encoding/decoding.  It is difficult to use.
// Using superwills' Nibble And A Half instead
// https://github.com/superwills/NibbleAndAHalf/blob/master/NibbleAndAHalf/base64.h
#include "base64.h"

// The PADDING parameter means RSA will pad your data for you
// if it is not exactly the right size
//#define PADDING RSA_PKCS1_OAEP_PADDING
#define PADDING RSA_PKCS1_PADDING
//#define PADDING RSA_NO_PADDING

RSA *loadPUBLICKeyFromString(const char *publicKeyStr) {
    // A BIO is an I/O abstraction (Byte I/O?)

    // BIO_new_mem_buf: Create a read-only bio buf with data
    // in string passed. -1 means string is null terminated,
    // so BIO_new_mem_buf can find the dataLen itself.
    // Since BIO_new_mem_buf will be READ ONLY, it's fine that publicKeyStr is const.
    BIO *bio = BIO_new_mem_buf((void *) publicKeyStr, -1); // -1: assume string is null terminated

    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // NO NL

    // Load the RSA key from the BIO
    RSA *rsaPubKey = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
    if (!rsaPubKey)
        printf("ERROR: Could not load PUBLIC KEY!  PEM_read_bio_RSA_PUBKEY FAILED: %s\n",
               ERR_error_string(ERR_get_error(), NULL));

    BIO_free(bio);
    return rsaPubKey;
}

RSA *loadPRIVATEKeyFromString(const char *privateKeyStr) {
    BIO *bio = BIO_new_mem_buf((void *) privateKeyStr, -1);
    //BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL ) ; // NO NL
    RSA *rsaPrivKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);

    if (!rsaPrivKey)
        printf("ERROR: Could not load PRIVATE KEY!  PEM_read_bio_RSAPrivateKey FAILED: %s\n",
               ERR_error_string(ERR_get_error(), NULL));

    BIO_free(bio);
    return rsaPrivKey;
}

unsigned char *rsaEncrypt(RSA *pubKey, const unsigned char *str, int dataSize, int *resultLen) {
    int rsaLen = RSA_size(pubKey);
    unsigned char *ed = (unsigned char *) malloc(rsaLen);

    // RSA_public_encrypt() returns the size of the encrypted data
    // (i.e., RSA_size(rsa)). RSA_private_decrypt()
    // returns the size of the recovered plaintext.
    *resultLen = RSA_public_encrypt(dataSize, (const unsigned char *) str, ed, pubKey, PADDING);
    if (*resultLen == -1)
        printf("ERROR: RSA_public_encrypt: %s\n", ERR_error_string(ERR_get_error(), NULL));

    return ed;
}

unsigned char *rsaDecrypt(RSA *privKey, const unsigned char *encryptedData, int *resultLen) {
    int rsaLen = RSA_size(privKey); // That's how many bytes the decrypted data would be

    unsigned char *decryptedBin = (unsigned char *) malloc(rsaLen);
    *resultLen = RSA_private_decrypt(RSA_size(privKey), encryptedData, decryptedBin, privKey, PADDING);
    if (*resultLen == -1)
        printf("ERROR: RSA_private_decrypt: %s\n", ERR_error_string(ERR_get_error(), NULL));

    return decryptedBin;
}

unsigned char *makeAlphaString(int dataSize) {
    unsigned char *s = (unsigned char *) malloc(dataSize);

    int i;
    for (i = 0; i < dataSize; i++)
        s[i] = 65 + i;
    s[i - 1] = 0;//NULL TERMINATOR ;)

    return s;
}

// You may need to encrypt several blocks of binary data (each has a maximum size
// limited by pubKey).  You shoudn't try to encrypt more than
// RSA_LEN( pubKey ) bytes into some packet.
// returns base64( rsa encrypt( <<binary data>> ) )
// base64OfRsaEncrypted()
// base64StringOfRSAEncrypted
// rsaEncryptThenBase64
char *rsaEncryptThenBase64(RSA *pubKey, unsigned char *binaryData, int binaryDataLen, int *outLen) {
    int encryptedDataLen;

    // RSA encryption with public key
    unsigned char *encrypted = rsaEncrypt(pubKey, binaryData, binaryDataLen, &encryptedDataLen);

    // To base 64
    int asciiBase64EncLen;
    char *asciiBase64Enc = base64(encrypted, encryptedDataLen, &asciiBase64EncLen);

    // Destroy the encrypted data (we are using the base64 version of it)
    free(encrypted);

    // Return the base64 version of the encrypted data
    return asciiBase64Enc;
}

// rsaDecryptOfUnbase64()
// rsaDecryptBase64String()
// unbase64ThenRSADecrypt()
// rsaDecryptThisBase64()
unsigned char *rsaDecryptThisBase64(RSA *privKey, char *base64String, int *outLen) {
    int encBinLen;
    unsigned char *encBin = unbase64(base64String, (int) strlen(base64String), &encBinLen);

    // rsaDecrypt assumes length of encBin based on privKey
    unsigned char *decryptedBin = rsaDecrypt(privKey, encBin, outLen);
    free(encBin);

    return decryptedBin;
}

int main(int argc, const char *argv[]) {
    ERR_load_crypto_strings();

    puts("We are going to: rsa_decrypt( unbase64( base64( rsa_encrypt( <<binary data>> ) ) ) )");
    // public key
    // http://srdevspot.blogspot.ca/2011/08/openssl-error0906d064pem.html
    //1. The file must contain:
    //-----BEGIN CERTIFICATE-----
    //on a separate line (i.e. it must be terminated with a newline).
    //2. Each line of "gibberish" must be 64 characters wide.
    //3. The file must end with:
    //-----END CERTIFICATE-----
    // YOUR PUBLIC KEY MUST CONTAIN NEWLINES.  If it doesn't (ie if you generated it with
    // something like
    // ssh-keygen -t rsa -C "you@example.com"
    // ) THEN YOU MUST INSERT NEWLINES EVERY 64 CHRS (just line it up with how I have it here
    // or with how the ssh-keygen private key is formatted by default)
    const char *b64_pKey = "-----BEGIN PUBLIC KEY-----\n"
                           "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3zEZvnmvHhFMRuRt9XwLdvlrdAMvUJU4AeGUV59MYu4ZmM0drkHcpQR5nOMRq9nmeWtKKxM2oD/RSrGZ6EftfBgUD/vYZHwtstbtYNhM2eT5CQuTEnbSa86LItkjRwELfFnIoEKClitMGy8Nkk0dyJeqqFbF1tO3Xu/xCMqOS/rdK3YPEnpdwKYX07zrCy1MX9gMAirmOwm8h7XfP/WvM6EGT2BcmfTlcajwjZ15EHHl3aTzjCuwxzeMbt39f1p0Bx/zZBIt/yjjuX6FfiWfpKHYRPWqrVnp0wh7xbhIc9AOKU3OsQGNDby828kV6B03tixRJfbejMIJ3gVgTubg2wIDAQAB\n"
                           "-----END PUBLIC KEY-----\n";

    // private key
    const char *b64priv_key = "-----BEGIN RSA PRIVATE KEY-----\n"
                              "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDfMRm+ea8eEUxG5G31fAt2+Wt0Ay9QlTgB4ZRXn0xi7hmYzR2uQdylBHmc4xGr2eZ5a0orEzagP9FKsZnoR+18GBQP+9hkfC2y1u1g2EzZ5PkJC5MSdtJrzosi2SNHAQt8WcigQoKWK0wbLw2STR3Il6qoVsXW07de7/EIyo5L+t0rdg8Sel3AphfTvOsLLUxf2AwCKuY7CbyHtd8/9a8zoQZPYFyZ9OVxqPCNnXkQceXdpPOMK7DHN4xu3f1/WnQHH/NkEi3/KOO5foV+JZ+kodhE9aqtWenTCHvFuEhz0A4pTc6xAY0NvLzbyRXoHTe2LFEl9t6MwgneBWBO5uDbAgMBAAECggEBAJCHlDszQ0I+Q124+Le6DxaJJ5sloNMW/gDB2jTvlCCQPQUk9m5sefxmLHZ62GunnEbOQmYod/xHtzkeTyMKyO/OC9fZ52QSwekIowlOqF2Fl/rSgve2+EYVpyX25JBLTevFnFSjHTsvaIZF7wzpYTsPc9S09EJMMXeJ+1UNDh6GbESknJvGPTpICdWcKbaArlqKFjiCeEZLwej5pIqQ5zgZ3Kk/1Hm6b1ut0EYblDoPFPto1sp+MzzDvyp8g/uwFUW+UvhbK69r73eyM4+S5LH1fqXq2/q+7V9IvOaz6D+BpDGqINia3ko49N4OdaUVt7uO7RGrXFW4D1lGoK0VqQkCgYEA92vErW0qsTRBpNtTHiXmHe6+/HVzqj7jZMkUL187KvioJqRgc2JxuAAT2HhJt1xuEeKtIGXJ/DSISXv3JS191nc9yG7ofWpxWw20T0LjdsZxnIa8I9Zq1pO0t2ir9N8VkJ2mMMxrJAlQTmA9Q3MpOXB6AmGXHN9rLlMdpdk7saUCgYEA5u5DGXZW6aIbTVZxasWklCGOCEb8TFJQN4pY59M7XYcfO9tyfO+K/RneCRsZ0k5Sjcr6IL1tfOPcH5GTBmFdNbsmFHQY6fxVh5NOpyrdwh9gip+dRQG1bMOFna0oIQ8UevrhXB0QBpoCHR1F71qQ7YBIfkj6HsV4wIW8Wed2wH8CgYEAmAfibGZeC1D+3RvfsPC8u7vOIzbQgdXuyCk+e/VaWAc+26+5jGz7T4LMrwEK2t6yy86rbvLc8FofPHgt3uxbHINctWJg/V+Ak+CR4PpK5tr9SJ8J0EOalotgSirJLsg9agVU7ztMwnHupJpCSsXimObiMcBhqF4ob+immRgos/kCgYEAoy9E/S4xtoEV9+2Y0dF+PisD+fdne+tCwiyiLJeIwMumF5835O9E8RuhWh/rfEBad9YyMIaLYxCFUU2GbqDIxBpOi2+K13W3XsmKb/iLogyYJP0rnkLZ2FTGC6j7zXihiVHVhEkJNREJY1d3Pu9psFrRX2PkU3BJpwoaEhHNSbMCgYBWhuOmG9SwLtQQlluFYHy2RUmXA/nCplp/TarVmeGyoPqtKaBQ5dMAYvAJ7YQKm9VDL5ij8/0SQ9hvk7yk9/mdUbcivqj4vQUMSezsgJMEucLrDZwczIv0Fm62CUEk+LdayowALGyLWYBcnXtLIYn5mFv+7Rk/kyuYiMsIIvMBLg==\n"
                              "-----END RSA PRIVATE KEY-----\n";

    // String to encrypt, INCLUDING NULL TERMINATOR:
    int dataSize = 37; // 128 for NO PADDING, __ANY SIZE UNDER 128 B__ for RSA_PKCS1_PADDING
    unsigned char *str = makeAlphaString(dataSize);
    printf("\nThe original data is:\n%s\n\n", (char *) str);

    // LOAD PUBLIC KEY
    RSA *pubKey = loadPUBLICKeyFromString(b64_pKey);

    int asciiB64ELen;
    char *asciiB64E = rsaEncryptThenBase64(pubKey, str, dataSize, &asciiB64ELen);

    RSA_free(pubKey); // free the public key when you are done all your encryption

    printf("Sending base64_encoded ( rsa_encrypted ( <<binary data>> ) ):\n%s\n", asciiB64E);
    puts("<<----------------  SENDING DATA ACROSS INTERWEBS  ---------------->>");

    char *rxOverHTTP = asciiB64E; // Simulate Internet connection by a pointer reference
    printf("\nRECEIVED some base64 string:\n%s\n", rxOverHTTP);
    puts("\n * * * What could it be?");

    // Now decrypt this very string with the private key
    RSA *privKey = loadPRIVATEKeyFromString(b64priv_key);

    // Now we got the data at the server.  Time to decrypt it.
    int rBinLen;
    unsigned char *rBin = rsaDecryptThisBase64(privKey, rxOverHTTP, &rBinLen);
    printf("Decrypted %d bytes, the recovered data is:\n%.*s\n\n", rBinLen, rBinLen,
           rBin); // rBin is not necessarily NULL
    // terminated, so we only print rBinLen chrs

    RSA_free(privKey);

    bool allEq = true;
    for (int i = 0; i < dataSize; i++)
        allEq &= (str[i] == rBin[i]);

    if (allEq) puts("DATA TRANSFERRED INTACT!");
    else puts("ERROR, recovered binary does not match sent binary");
    free(str);
    free(asciiB64E); // rxOverHTTP
    free(rBin);
    ERR_free_strings();
}