C++(CrytpoPP) 与 CI(CodeIgniter PHP) 实现相同加解密算法(Crypt AES-128-CBC/HMAC SHA512),方便交互

前言

  • 如果服务器端数据需要与其它应用程序交互,很可能需要进行加密与解密
  • 很久前用CI做的服务器,CI自带一个Encryption库,默认使用使用AES-128-CBC/HMAC SHA512算法,我在VC客户端请求服务器数据时数据是加密的,需要实现相同算法来进行解密。

CI libraries\Encryption.php 算法简述

加密:
$this->encryption->encrypt($plain_text)

  • 通过 HKDF 和 SHA-512 摘要算法,从你配置的 encryption_key 参数中获取两个密钥:加密密钥 和 HMAC 密钥。
  • 生成一个随机的初始向量(IV)。
  • 使用上面的加密密钥和 IV ,通过 AES-128 算法的 CBC 模式(或其他你配置的算法和模式)对数据进行加密。
  • 将 IV 附加到密文后。
  • 对结果进行 Base64 编码,这样就可以安全的保存和传输它,而不用担心字符集问题。
  • 使用 HMAC 密钥生成一个 SHA-512 HMAC 认证消息,附加到 Base64 字符串后,以保证数据的完整性。

解密:
$this->encryption->decrypt($ciphertext)

  • 通过 HKDF 和 SHA-512 摘要算法,从你配置的 encryption_key 参数中获取两个密钥:加密密钥 和 HMAC 密钥。 由于 encryption_key 不变,所以生成的结果和上面 encrypt() 方法生成的结果是一样的,否则你没办法解密。
  • 检查字符串的长度是否足够长,并从字符串中分离出 HMAC ,然后验证是否一致(这可以防止时序攻击), 如果验证失败,返回 FALSE 。
  • 进行 Base64 解码。
  • 从密文中分离出 IV ,并使用 IV 和 加密密钥对数据进行解密。

源码分析

// 原始key在配置文件 $config['encryption_key'] = hex2bin('3b9bddd542ce5b50f425a83ab44e812b');

    // 加密部分
    public function encrypt($data, array $params = NULL)
    {
        //...参数验证

        // hkdf函数,根据原始key, 生成加密key与散列key, 需要VC实现
        isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
        // 根据配置,调用相关库 (crypt或openssl)加密核心算法, 本文实现crypt
        if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
        {
            return FALSE;
        }
        // base64转码
        $params['base64'] && $data = base64_encode($data);
        // 生成hmac sha512 hash
        if (isset($params['hmac_digest']))
        {
            isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
            return hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data;
        }

        return $data;
    }

    // Crypt加密核心算法
    protected function _mcrypt_encrypt($data, $params)
    {
        // 'handle' = mcrypt_module_open($this->_cipher, '', $this->_mode, '')
        if ( ! is_resource($params['handle']))
        {
            return FALSE;
        }

        // 生成IV
        $iv = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
            ? $this->create_key($iv_size)
            : NULL;
        // 初始化算法
        if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
        {
            if ($params['handle'] !== $this->_handle)
            {
                mcrypt_module_close($params['handle']);
            }

            return FALSE;
        }

        // CBC/EBC 原始数据长度fix
        if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
        {
            $block_size = mcrypt_enc_get_block_size($params['handle']);
            $pad = $block_size - (self::strlen($data) % $block_size);
            $data .= str_repeat(chr($pad), $pad);
        }
        // 生成密文,默认使用AES-128 CBC加密, 并将IV附在密文前面
        $data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
            ? $iv.mcrypt_generic($params['handle'], $data)
            : mcrypt_generic($params['handle'], $data);

        mcrypt_generic_deinit($params['handle']);
        if ($params['handle'] !== $this->_handle)
        {
            mcrypt_module_close($params['handle']);
        }

        return $data;
    }

    // hkdf SHA512 散列算法
    public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
    {
        //... 参数合法性判断,略..

        // 长度填充
        self::strlen($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);

        // 注意 加密Key的盐为“encryption”, 散列Key的盐为“authentication”
        $prk = hash_hmac($digest, $key, $salt, TRUE);
        $key = '';
        for ($key_block = '', $block_index = 1; self::strlen($key) < $length; $block_index++)
        {
            $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
            $key .= $key_block;
        }

        return self::substr($key, 0, $length);
    }
    // 解密略

C++ 实现

  • 使用CryptoPP库,自行下载编译
  • 源文件无其它依赖,可直接编译

// 使用方法:
Encryption enc;
// 加密
std::string result = enc.Encrypt(text);
// 解密
std::string result = enc.Decrypt(text);

  • 源代码
// file ci_encryption.h

#pragma once

#include <iostream>
#include <secblock.h>

using namespace CryptoPP;
using namespace std;


// 仅处理AES-128-CBC + HMAC SHA512
class Encryption{
protected:
    SecByteBlock _key;          // 保存的二进制原始Key bin
    size_t _digest_size;    // [sha512=64位], 384=>48, 256=>32, 224=>28
public:
    Encryption();
    ~Encryption(){};

    // HKDF摘要算法,同ci的hkdf()函数
    int DeriveKeys(SecByteBlock& encrypt_key, SecByteBlock& hmac_key);

    // 与CI相同算法的加密与解密
    string Encrypt(const string& data);
    string Decrypt(const string& data);

};







// file ci_encryption.cpp

#pragma warning (disable:4996)
#include "encryption.h"

#include <hex.h>
#include <modes.h>
#include <randpool.h>
#include <hkdf.h>
#include <sha.h>
#include <base64.h>


#pragma comment (lib, "cryptlib.lib")

using namespace std;

namespace{
// 原始加密Key
static const char* __encryption_key = "3b9bddd542ce5b50f425a83ab44e812b";
};


Encryption::Encryption() : _digest_size(64){
    SSGet(StringSource(__encryption_key, true, new HexDecoder), _key);
}


// 使用hdkf 生成加密 key 和 散列(HMAC) Key
int Encryption::DeriveKeys(SecByteBlock& encrypt_key, SecByteBlock& hmac_key){
    //string key;
    //StringSource(_key, true, new HexDecoder(new StringSink(key)));
    string info("encryption");
    HKDF<SHA512> hkdf;
    encrypt_key.resize(_key.size());    // 与原始Key长度一致 (16位)
    int ret1 = hkdf.DeriveKey(encrypt_key, encrypt_key.size(), (byte*)_key.data(), _key.size(), 0, 0, (byte*)info.data(), info.size());

    info.assign("authentication");
    hmac_key.resize(_digest_size);
    int ret2 = hkdf.DeriveKey(hmac_key, hmac_key.size(), (byte*)_key.data(), _key.size(), 0, 0, (byte*)info.data(), info.size());
    return (ret1 == encrypt_key.size() && ret2 == _digest_size);
}

string Encryption::Encrypt(const string& data){

    // 取得加密key与散列Key
    SecByteBlock encrypt_key, hmac_key;
    if (!DeriveKeys(encrypt_key, hmac_key)){
        return "";
    }

    // 加密, 生成随机IV
    SecByteBlock iv(AES::BLOCKSIZE);
    RandomPool().GenerateBlock(iv.data(), iv.size());

    // CBC AES-128 加密
    SecByteBlock cipher;
    CBC_Mode<AES>::Encryption aes(encrypt_key, encrypt_key.size(), iv); 
    //StringSource(data, true, new StreamTransformationFilter(aes, new StringSink(cipher)));
    SSGet(StringSource(data, true, new StreamTransformationFilter(aes)), cipher);

    // 合并 iv 与 cipher
    cipher = iv + cipher;

    // base64 转码
    string cipher_base64;
    StringSource(cipher, cipher.size(), true, new Base64Encoder(new StringSink(cipher_base64), false));

    // 生成 HMAC SHA512 摘要 (对base64进行摘要)
    SecByteBlock hash;
    HMAC<SHA512> mac(hmac_key, hmac_key.size());
    SSGet(StringSource(cipher_base64, true, new HashFilter(mac)), hash);

    // hash 转码 (hexEncode)
    string hash_hex_encode;
    StringSource(hash, hash.size(), true, new HexEncoder(new StringSink(hash_hex_encode)));

    // 合并hash, 密文
    return hash_hex_encode + cipher_base64;
}

string Encryption::Decrypt(const string& data){
    string result;
    // 分离出hash (hexencodieng), 原始密文 (base64encoding)
    if (data.size() < _digest_size * 2){
        return result;
    }

    // 取得Key用于解密和哈希
    SecByteBlock encrypt_key, hmac_key;
    if (!DeriveKeys(encrypt_key, hmac_key)){
        return result;
    }

    string str_hash(data, 0, _digest_size * 2); // 从0起始,取N位. 或 (data.c_str(), size)
    string str_cipher(data, str_hash.size());   // 从size起始取子串

    // 生成Hash
    SecByteBlock hash;
    HMAC<SHA512> mac(hmac_key, hmac_key.size());
    SSGet(StringSource(str_cipher, true, new HashFilter(mac)), hash);

    string hash_hex_encode; // HexEncode 字符串
    StringSource(hash, hash.size(), true, new HexEncoder(new StringSink(hash_hex_encode)));

    // 验证Hash
    if (strcmpi(hash_hex_encode.c_str(), str_hash.c_str()) != 0){
        return result;
    }

    // 对密文进行 base64 解码
    SecByteBlock cipher;
    SSGet(StringSource(str_cipher, true, new Base64Decoder), cipher);
    if (cipher.size() <= AES::BLOCKSIZE){
        return result;
    }

    // 分离出IV, 密文     (iv在前面)
    SecByteBlock iv(cipher, AES::BLOCKSIZE);
    SecByteBlock pure_cipher(cipher.data() + iv.size(), cipher.size() - iv.size());

    // 解密
    CBC_Mode<AES>::Decryption aes(encrypt_key, encrypt_key.size(), iv);
    StringSource(pure_cipher, pure_cipher.size(), true, new StreamTransformationFilter(aes, new StringSink(result)));
    return result;
}

源码下载: https://download.csdn.net/download/ijiabao520/10655886

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值