微信 MMTLS 协议详解(五):加密实现

常用的解密算法,对称非对称 加密,密钥协商, 带消息认证的加解密

#生成RSA 密钥对

cpp 复制代码
void GenerateRsaKeypair(std::string& public_key,
  std::string& private_key)
{
  RSA* rsa = RSA_new();
  BIGNUM* bn = BN_new();

  // 生成 RSA 密钥对
  BN_set_word(bn, RSA_F4);
  RSA_generate_key_ex(rsa, 2048, bn, nullptr);

  // 创建内存 BIO 用于存储密钥
  BIO* bio_private = BIO_new(BIO_s_mem());
  BIO* bio_public = BIO_new(BIO_s_mem());

  // 将私钥写入 BIO(PKCS#8 格式)
  PEM_write_bio_RSAPrivateKey(bio_private, rsa, nullptr, nullptr, 0, nullptr,
    nullptr);

  // 将公钥写入 BIO(X.509 格式)
  PEM_write_bio_RSAPublicKey(bio_public, rsa);

  // 从 BIO 提取字符串
  char* priv_data = nullptr;
  char* pub_data = nullptr;
  long priv_len = BIO_get_mem_data(bio_private, &priv_data);
  long pub_len = BIO_get_mem_data(bio_public, &pub_data);

  private_key = std::string(priv_data, priv_len);
  public_key = std::string(pub_data, pub_len);

  // 释放资源
  BIO_free_all(bio_private);
  BIO_free_all(bio_public);
  RSA_free(rsa);
  BN_free(bn);
}

RSA 公钥加密

cpp 复制代码
// 公钥加密
std::string EncryptWithPublic(const std::string& plaintext,
  const std::string& public_key) {
  BIO* bio = BIO_new_mem_buf(public_key.c_str(), -1);
  RSA* rsa = PEM_read_bio_RSAPublicKey(bio, nullptr, nullptr, nullptr);
  BIO_free_all(bio);

  if (!rsa) {
    return "";
  }

  int rsa_size = RSA_size(rsa);
  int max_plaintext_len =
    rsa_size - 42;  // RSA_PKCS1_OAEP_PADDING 填充后最大明文长度
  std::string encrypted_str;

  for (size_t i = 0; i < plaintext.length(); i += max_plaintext_len) {
    size_t len =
      std::min(max_plaintext_len, static_cast<int>(plaintext.length() - i));
    unsigned char* encrypted = new unsigned char[rsa_size];
    int result = RSA_public_encrypt(
      len, reinterpret_cast<const unsigned char*>(plaintext.c_str() + i),
      encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
    if (result == -1) {
      RSA_free(rsa);
      delete[] encrypted;
      return "";
    }
    encrypted_str.append(reinterpret_cast<char*>(encrypted), result);
    delete[] encrypted;
  }

  RSA_free(rsa);
  return encrypted_str;
}

RSA 私钥解密

cpp 复制代码
// 私钥解密
std::string DecryptWithPrivate(const std::string& ciphertext,
  const std::string& private_key) {
  BIO* bio = BIO_new_mem_buf(private_key.c_str(), -1);
  RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, nullptr);
  BIO_free_all(bio);

  if (!rsa) {
    return "";
  }

  int rsa_size = RSA_size(rsa);
  std::string decrypted_str;

  for (size_t i = 0; i < ciphertext.length(); i += rsa_size) {
    unsigned char* decrypted = new unsigned char[rsa_size];
    int result = RSA_private_decrypt(
      rsa_size,
      reinterpret_cast<const unsigned char*>(ciphertext.c_str() + i),
      decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
    if (result == -1) {
      RSA_free(rsa);
      delete[] decrypted;
      return "";
    }
    decrypted_str.append(reinterpret_cast<char*>(decrypted), result);
    delete[] decrypted;
  }

  RSA_free(rsa);
  return decrypted_str;
}

生成ECDH 密钥对

cpp 复制代码
#include <openssl/digest.h>
#include <openssl/ecdh.h>
#include <openssl/ec_key.h>
#include <openssl/hmac.h>
#include <openssl/mem.h>
#include <openssl/sha.h>
#include <openssl/cipher.h>
#include <openssl/bio.h>
#include <openssl/md5.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/aes.h>
bool GenECDHKeypair(int nid,
                                std::string& public_key,
                                std::string& private_key) {
  bool ret = false;
  EC_KEY* ec_key = nullptr;
  unsigned char* pub_key_buf = nullptr;
  unsigned char* pri_key_buf = nullptr;
  do {
    ec_key = EC_KEY_new_by_curve_name(nid);
    if (!ec_key) {
      break;
    }

    EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);

    ret = EC_KEY_generate_key(ec_key);
    if (ret != 1) {
      break;
    }

    int pub_key_size = i2o_ECPublicKey(ec_key, &pub_key_buf);
    if (pub_key_size == 0 || !pub_key_buf) {
      break;
    }

    int pri_key_size = i2d_ECPrivateKey(ec_key, &pri_key_buf);
    if (pri_key_size == 0 || !pri_key_buf) {
      break;
    }
    
    public_key.assign((const char*)pub_key_buf, pub_key_size);
    private_key.assign((const char*)pri_key_buf, pri_key_size);

    ret = true;
  } while (false);

  if (ec_key) {
    EC_KEY_free(ec_key);
  }

  if (pub_key_buf) {
    OPENSSL_free(pub_key_buf);
  }

  if (pri_key_buf) {
    OPENSSL_free(pri_key_buf);
  }
  return ret;
}

生成ECDSA 密钥对

cpp 复制代码
bool GenECDSAKeypair(int nid,
                                 std::string& public_key,
                                 std::string& private_key) {
  bool result = false;
  EC_KEY* ec_key = nullptr;
  BIO* bio = nullptr;
  do {
    ec_key = EC_KEY_new_by_curve_name(nid);
    if (!ec_key) {
      break;
    }

    EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);

    int ret = EC_KEY_generate_key(ec_key);
    if (ret != 1) {
      break;
    }

    ret = EC_KEY_check_key(ec_key);
    if (ret != 1) {
      break;
    }

    bio = BIO_new(BIO_s_mem());
    ret = PEM_write_bio_EC_PUBKEY(bio, ec_key);
    if (ret != 1 || BIO_flush(bio) != 1) {
      break;
    }
    char* ptr = nullptr;
    long size = BIO_get_mem_data(bio, &ptr);
    public_key.assign(ptr, size);
    BIO_free(bio);

    bio = BIO_new(BIO_s_mem());
    ret = PEM_write_bio_ECPrivateKey(bio, ec_key, nullptr, nullptr, 0, nullptr,
                                     nullptr);
    if (ret != 1 || BIO_flush(bio) != 1) {
      break;
    }
    ptr = nullptr;
    size = BIO_get_mem_data(bio, &ptr);
    private_key.assign(ptr, size);

    result = true;
  } while (false);

  if (nullptr != bio) {
    BIO_free(bio);
  }

  if (nullptr != ec_key) {
    EC_KEY_free(ec_key);
  }

  return result;
}

生成RSA 密钥对

cpp 复制代码
bool GenRsaKeypair(std::string& public_key, std::string& private_key)
{
  // 产生RSA密钥
  RSA* rsa = RSA_new();
  BIGNUM* bn = BN_new();
  BN_set_word(bn, RSA_F4);
  RSA_generate_key_ex(rsa, 1024, bn, NULL);

  // 提取私钥
  BIO* bio_private = BIO_new(BIO_s_mem());
  PEM_write_bio_RSAPrivateKey(bio_private, rsa, NULL, NULL, 0, NULL, NULL);
  int private_key_len = BIO_pending(bio_private);
  char* pem_private_key = (char*)calloc(private_key_len + 1, 1);
  BIO_read(bio_private, pem_private_key, private_key_len);
  private_key.assign(pem_private_key, private_key_len);
  free(pem_private_key);
  BIO_free(bio_private);

  // 提取公钥
  BIO* bio_public = BIO_new(BIO_s_mem());
  PEM_write_bio_RSA_PUBKEY(bio_public, rsa);
  int public_key_len = BIO_pending(bio_public);
  char* pem_public_key = (char*)calloc(public_key_len + 1, 1);
  BIO_read(bio_public, pem_public_key, public_key_len);
  public_key.assign(pem_public_key, public_key_len);
  free(pem_public_key);
  BIO_free(bio_public);

  // 释放资源
  RSA_free(rsa);
  BN_free(bn);

  return true;
}

SHA256 ECDH 密钥协商

cpp 复制代码
inline void* Sha256(const void* in,
                    size_t in_len,
                    void* out,
                    size_t* out_len) {
  *out_len = SHA256_DIGEST_LENGTH;
  return SHA256((const uint8_t*)in, in_len, (uint8_t*)out);
}

bool SHA256ECDH(int nid,
  const std::string& public_key,
  const std::string& private_key,
  std::string& result) {
  bool ret = false;
  EC_KEY* pub_ec_key = nullptr;
  EC_KEY* pri_ec_key = nullptr;

  do {
    pub_ec_key = EC_KEY_new_by_curve_name(nid);
    if (!pub_ec_key) {
      break;
    }
    auto uint8_pubkey = (const uint8_t*)public_key.data();
    pub_ec_key = o2i_ECPublicKey(&pub_ec_key, &uint8_pubkey, public_key.size());
    if (!pub_ec_key) {
      break;
    }

    pri_ec_key = EC_KEY_new_by_curve_name(nid);
    if (!pri_ec_key) {
      break;
    }

    auto uint8_privkey = (const uint8_t*)private_key.data();
    pri_ec_key = d2i_ECPrivateKey(&pri_ec_key, &uint8_privkey, private_key.size());
    if (!pri_ec_key) {
      break;
    }
   result.resize(SHA256_DIGEST_LENGTH);
   ECDH_compute_key(result.data(), SHA256_DIGEST_LENGTH,
   EC_KEY_get0_public_key(pub_ec_key), pri_ec_key, Sha256);
   
    ret = true;

  } while (false);

  // free memory
  if (pub_ec_key) {
    EC_KEY_free(pub_ec_key);
  }

  if (pri_ec_key) {
    EC_KEY_free(pri_ec_key);
  }
  return ret;
}

MD5 ECDH 密钥协商

cpp 复制代码
inline void* MD5(const void* in,
  size_t in_len,
  void* out,
  size_t* out_len) {
  *out_len = MD5_DIGEST_LENGTH;
  MD5_CTX ctx;
  MD5_Init(&ctx);
  MD5_Update(&ctx, in, in_len);
  MD5_Final((uint8_t*)out, &ctx);
  return out;
}

bool MD5ECDH(int nid,
  const std::string& public_key,
  const std::string& private_key,
  std::string& result) {
  bool ret = false;
  EC_KEY* pub_ec_key = nullptr;
  EC_KEY* pri_ec_key = nullptr;

  do {
    pub_ec_key = EC_KEY_new_by_curve_name(nid);
    if (!pub_ec_key) {
      break;
    }
    auto uint8_pubkey = (const uint8_t*)public_key.data();
    pub_ec_key = o2i_ECPublicKey(&pub_ec_key, &uint8_pubkey, public_key.size());
    if (!pub_ec_key) {
      break;
    }

    pri_ec_key = EC_KEY_new_by_curve_name(nid);
    if (!pri_ec_key) {
      break;
    }

    auto uint8_privkey = (const uint8_t*)private_key.data();
    pri_ec_key = d2i_ECPrivateKey(&pri_ec_key, &uint8_privkey, private_key.size());
    if (!pri_ec_key) {
      break;
    }
 result.resize(MD5_DIGEST_LENGTH);
 ECDH_compute_key(result.data(), MD5_DIGEST_LENGTH,
   EC_KEY_get0_public_key(pub_ec_key), pri_ec_key, MD5);   
    ret = true;
  } while (false);

  // free memory
  if (pub_ec_key) {
    EC_KEY_free(pub_ec_key);
  }

  if (pri_ec_key) {
    EC_KEY_free(pri_ec_key);
  }
  return ret;
}

AES256GCM 加密

cpp 复制代码
std::string Aes256GcmEncrypt(const void* once,
                                      int32_t once_len,
                                      const void* key,
                                      int32_t key_len,
                                      const void* aad,
                                      int32_t aad_len,
                                      const void* data,
                                      int32_t data_len,
                                      int tag_size,
                                      std::string& tag) {
  std::string result;
  EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
  EVP_CIPHER_CTX_init(ctx);
  EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
  do {
    // set iv size
    int ret =
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, once_len, nullptr);
    if (ret != 1) {
      break;
    }

    ret = EVP_EncryptInit_ex(ctx, nullptr, nullptr, (const uint8_t*)key,
                             (const uint8_t*)once);
    if (ret != 1) {
      break;
    }
    int out_len = 0;
    if (aad_len != 0) {
      ret = EVP_EncryptUpdate(ctx, nullptr, &out_len,
                              (const uint8_t*)aad, aad_len);
      if (ret != 1) {
        break;
      }
    }

    result.resize(EVP_CIPHER_CTX_block_size(ctx) + data_len);
    int encrypt_len = 0;
    if (data_len != 0) {
      ret = EVP_EncryptUpdate(ctx, (uint8_t*)result.data(), &out_len,
                              (const uint8_t*)data, data_len);
      if (ret != 1) {
        break;
      }

      encrypt_len = out_len;
    }

    ret = EVP_EncryptFinal_ex(ctx, (uint8_t*)result.data() + encrypt_len,
                              &out_len);
    if (ret != 1) {
      break;
    }

    encrypt_len += out_len;
    result.resize(encrypt_len);

    if (tag_size != 0) {
      tag.resize(tag_size);
      ret =
          EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_size, tag.data());
      if (ret != 1) {
        break;
      }
    }
  } while (false);
  EVP_CIPHER_CTX_free(ctx);
  return result;
}

AES256 GCM 解密

cpp 复制代码
std::string AesGcmDecrypt(const void* once,
                                      int32_t once_len,
                                      const void* key,
                                      int32_t key_len,
                                      const void* aad,
                                      int32_t aad_len,
                                      const void* tag,
                                      int32_t tag_len,
                                      const void* data,
                                      int32_t data_len) {
  std::string result;
  EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
  EVP_CIPHER_CTX_init(ctx);
  
  EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
  do {
    int ret =
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, once_len, nullptr);
    if (ret != 1) {
      break;
    }

    ret = EVP_DecryptInit_ex(ctx, nullptr, nullptr, (const uint8_t*)key,
                             (const uint8_t*)once);
    if (ret != 1) {
      break;
    }
    int out_len = 0;
    // set aad
    if (aad_len != 0) {
      ret = EVP_DecryptUpdate(ctx, nullptr, &out_len,
                              (const uint8_t*)aad, aad_len);
      if (ret != 1) {
        break;
      }
    }
    result.resize(EVP_CIPHER_CTX_block_size(ctx) + data_len);
    int decrypt_len = 0;
    if (data_len != 0) {
      ret = EVP_DecryptUpdate(ctx, (uint8_t*)result.data(), &out_len,
                              (const uint8_t*)data, data_len);
      if (ret != 1) {
        break;
      }

      decrypt_len = out_len;
    }
    ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len,
                              (void*)tag);
    if (ret == 1) {
      ret = EVP_DecryptFinal_ex(ctx, (uint8_t*)result.data() + decrypt_len,
                                &out_len);
      if (ret == 1) {
        decrypt_len += out_len;
        result.resize(decrypt_len);
      } else {
        result.clear();
      }
    } else {
      result.clear();
    }
  } while (false);
  EVP_CIPHER_CTX_free(ctx);
  return result;
}

AES 加密

cpp 复制代码
std::string AESEncrypt(const std::string& key,
  const void* data,
  int32_t data_len) {
  if ( 0 == data_len)
  {
    return "";
  }

  // 计算padding
  int padding = 16 - data_len % 16;
  std::string padding_data(data_len + padding, (char)padding);
  memcpy(padding_data.data(), data, data_len);

  uint8_t tmp_key[16] = {};
  uint8_t* ptmp_key = nullptr;
  if (key.length() == 16) {
    ptmp_key = (uint8_t*)key.data();
  }
  else {
    ptmp_key = tmp_key;
    memcpy(tmp_key, key.data(), key.length() > 16 ? 16 : key.length());
  }

  uint8_t iv[16] = {};
  memcpy(iv, ptmp_key, 16);

  std::string result(padding_data.size(), 0);
  AES_KEY aes_key;
  AES_set_encrypt_key(ptmp_key, 128, &aes_key);
  AES_cbc_encrypt((uint8_t*)padding_data.data(), (uint8_t*)result.data(),
    padding_data.length(), &aes_key, iv, AES_ENCRYPT);

  return result;
}

AES 解密

cpp 复制代码
std::string AESDecrypt(const std::string& key, const void* data, int32_t len)
{
  if (key.empty() || len == 0) {
    return "";
  }

  uint8_t tmp_key[16] = {};
  uint8_t* ptmp_key = nullptr;
  if (key.length() == 16)
  {
    ptmp_key = (uint8_t*)key.data();
  }
  else
  {
    ptmp_key = tmp_key;
    memcpy(tmp_key, key.data(), key.length() > 16 ? 16 : key.length());
  }

  uint8_t iv[16] = {};
  memcpy(iv, ptmp_key, 16);


  std::string result(len, 0);
  AES_KEY aes_key;
  AES_set_decrypt_key(ptmp_key, 128, &aes_key);
  AES_cbc_encrypt((uint8_t*)data, (uint8_t*)result.data(), len,
    &aes_key, iv, AES_DECRYPT);
  // 去掉padding
  char padding = result[result.length() - 1];
  if (padding > 0 && padding <= 16)
  {
    result.resize(result.length() - padding);
  }
  return result;
}
相关推荐
说私域24 分钟前
开源链动2+1模式与AI智能名片赋能的S2B2C共享经济新生态
人工智能·微信·小程序·开源
增强6 小时前
Flutter 实现Android,IOS 微信登录
flutter·ios·微信
Stanford_11061 天前
关于大数据的基础知识(四)——大数据的意义与趋势
大数据·人工智能·物联网·微信·微信公众平台·微信开放平台
说私域1 天前
开源AI大模型赋能私域流量:S2B2C场景下品牌文化建构的智能路径研究
人工智能·微信·小程序·开源
程序员荒生2 天前
基于 Next.js 搞定个人公众号登陆流程
前端·微信·开源
TSINGSEE3 天前
EasyRTC轻量级Webrtc音视频通话SDK,助力带屏IPC在嵌入式设备中的应用
arm开发·微信·架构·音视频·webrtc
说私域4 天前
私域电商的进化逻辑与技术赋能:基于开源AI大模型与S2B2C商城的创新融合研究
人工智能·微信·小程序·开源
TSINGSEE5 天前
EasyRTC嵌入式音视频通话SDK:微信生态支持、轻量化架构与跨平台兼容性(Linix/Windows/ARM/Android/iOS/LiteOS)
arm开发·网络协议·微信·架构·音视频·webrtc·智能硬件
龙之吻5 天前
IOS接入微信方法
ios·微信