前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TOB服务部署安全模块

TOB服务部署安全模块

作者头像
radaren
发布2018-08-28 15:00:59
1.3K0
发布2018-08-28 15:00:59
举报

在TOB业务中部署在服务器中的程序可能会被窃取.对此设计一套安全模块,通过设备信息, 有效期,业务信息的确认来实现业务安全, 主要使用openssl进行加密, upx进行加壳。 为精简服务, 使用模块化方式设计.

  • 优点: 体量较小, 易于内嵌和扩展
  • 缺点: 暂未提供对外生成私钥的接口

基本思路

  1. RSA2048加密授权信息(依据NIAT SP800-57要求, 2011年-2030年业务至少使用RSA2048): 硬件信息(MAC/CPU), 有效期, 服务版本号, 业务信息
  2. 公钥代码写死,随版本更新, 私钥不对外发布暂时放到编译机上, 使用脚本生成授权信息.
  3. 主要流程: 生成公钥私钥->生成licence->服务启动时校验

RSA简介

  • 由于介绍RSA算法的文章实在很多,涉及到一些较复杂的数学, 而且openssl里面实现的方式与传统算法又有一些差异.于是就只用一句话介绍一下使用到的核心算法:
  • RSA是一种公私钥加密解密算法, 使用公钥a和私钥b, 能实现:
    • 原文^a mod N = 密文
    • 密文^b mod N = 原文
  • 2048指作为两个大素数乘积N的比特位数, 有一个RSA-challenge可以知道当前全世界被破解的最大比特位数
  • 由于RSA的秘钥生成过程是N->L=lcm(p-1,q-1)->E->D = E mod L - 1,
  • 加密的核心是通过公钥e和N找到私钥d的难度超出计算力.因此,谁知道私钥d,谁就能分解整数n。所以我们不能对不同的用户使用相同的n,否则这两个用户可以分别互相算出对方的私钥。
  • 工程上对于私钥的破解难度要高于公钥, 所以是用管理私钥, 公开公钥.一般接收信息加密,任何人都可以使用公钥进行加密,解密时,用户使用对应的私钥解密。本业务而言, 私钥存放理论安全的开发机(公钥写死业务代码, 所以版本更新时候复杂度并没有增加), 公钥二进制向外发布, 也就是重要的是私钥禁止发布而不是谁来加密谁来解密.
  • 值得注意的是, 使用RSA加密算法, 明文长度小于N/8, 除8的原因是bit/byte的转换
  • 在openssl.pem文件中, 公钥.pem包含公钥指数e和模数N, 私钥.pem包含版本号,模数N,公钥指数e,私钥指数d,素数p,q和中间数,所以公钥可以发布,私钥要求随源代码存放, 不进行发布

环境部署

  1. 安装openssl1.1, 注意版本可以不同, 但是由于openssl之前版本有重大安全风险,虽然这里只是用了加密部分,但是其他业务模块可能用到对应涉及风险的包,所以建议保持版本更新: 1./config shared zlib --prefix=/usr/local/openssl && make && make install
  2. gcc ../main.cpp -I ../include/openssl/include -L../include/openssl/libs/libcrypto.a -lssl -lcrypto -ldl -lpthread链接库, 不然编译的时候会有一系列的undefine问题, 在网上没有找到CMakeLists.txt, 故将文件粘贴在这里: 1 2 3 4 5 6 7 8 9 10 11cmake_minimum_required(VERSION 3.10) project(rsa) set(CMAKE_CXX_STANDARD 11) set(OPENSSL_USE_STATIC_LIBS TRUE) set(CMAKE_CXX_FLAGS "-O4 -msse2 -msse3 -msse4 -std=c++11") include_directories(./include) add_executable(rsa main.cpp) target_link_libraries(rsa crypto)
  3. 可以使用@calvinshao分享的RSA C++加解密 或者这篇测试, 注意.pem文件需要自己生成一下(这里也可以进入openssl里面再生成, 不过进入后退格符号用不了很麻烦..): 1 2openssl genrsa -out priv_key.pem 2048 # 先生成私钥 openssl rsa -in priv_key.pem -pubout -out pub_key.pem #从私钥提取公钥
  4. (*)命令行利用秘钥加密/解密文件 1 2 3 4# 加密, 使用公钥/私钥加密均可(由openssl.pem数据结构, 私钥文件包含公钥) openssl rsautl -encrypt -in file.txt -inkey pub_key.pem -pubin -out fileEncrypd.txt # 解密, 命令行只能使用私钥解密, 所以命令行格式在本业务不适用 openssl rsautl -decrypt -in fileEncrypd.txt -inkey priv_key.pem -out fileDecrypd.txt

openssl RSA

RSA秘钥生成

  • 秘钥生成网上流传的RSA_generate_key版本不建议使用,调用RSA_generate_key_ex即可: 1 2 3 4 5 6 7RSA * rsa = RSA_new(); BIGNUM* bne = BN_new(); if(bne == NULL){ printf("bne null!"); } BN_set_word(bne, RSA_F4); //65537, 标准会推荐素数(公钥) int ret = RSA_generate_key_ex(rsa, kBits, bne, NULL);
  • 在于输出函数使用, 笔者尝试多种方法, 最终使用这种可以在命令行中通过命令检测通过(另外openssl支持直接二次加密私钥): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18// 公钥输出 BIO *pub_key = BIO_new(BIO_s_file()); if(PEM_write_bio_RSA_PUBKEY(pub_key, rsa) != 1){ printf("Public Key File Write Error\n"); return; } BIO_free_all(pub_key); // 私钥输出 BIO *priv_key = BIO_new_file(fout_priv_key, "w"); if(PEM_write_bio_RSAPrivateKey(priv_key, rsa, NULL, NULL, NULL, NULL, NULL) != 1){ printf("Private Key File Write Error\n"); return; } // 二次加密私钥输出 std::string password = "TTS_ANS"; if(PEM_write_bio_RSAPrivateKey(priv_key, rsa, EVP_des_ede3_cbc(), (unsigned char*)password.c_str(), password.size(), NULL, NULL) != 1){

RSA使用

具体数据结构

发现openssl的结果体定义有个规律, 就是小写_st. 这样的好处是全局搜索的时候可以很快找到 这里列举一下最主要用到的两个结构体RSABIO

  • struct RSA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38struct rsa_st { /* * The first parameter is used to pickup errors where this is passed * instead of aEVP_PKEY, it is set to 0 */ int pad; // padding, 保证明文c的位数不大于N long version; // const RSA_METHOD *meth; /* functional reference if 'meth' is ENGINE-provided */ ENGINE *engine; // crypto/engine/eng_int.h //BIGNUM: bit chunks 数组实现的大数 BIGNUM *n; // 模数 BIGNUM *e; // public exponent 公钥 BIGNUM *d; // private exponent 私钥 BIGNUM *p; // 生成RSA的大素数p BIGNUM *q; // 生成RSA的大素数q BIGNUM *dmp1; // e^dmp1 = 1 mod (p-1) BIGNUM *dmq1; // e^dmq1 = 1 mod (q-1) BIGNUM *iqmp; // q^iqmq = 1 mod p /* be careful using this if the RSA structure is shared */ CRYPTO_EX_DATA ex_data; int references; int flags; /* Used to cache montgomery values */ BN_MONT_CTX *_method_mod_n; BN_MONT_CTX *_method_mod_p; BN_MONT_CTX *_method_mod_q; /* * all BIGNUM values are actually in the following data, if it is not * NULL */ char *bignum_data; BN_BLINDING *blinding; BN_BLINDING *mt_blinding; CRYPTO_RWLOCK *lock; }; typedef struct rsa_st RSA;
  • struct BIO openssl用于进行内定结果体与外界抽象的类, 封装文件,内存,日志,stdin/stdout,socket,加解密,摘要 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19struct bio_st { const BIO_METHOD *method; /* bio, mode, argp, argi, argl, ret */ long (*callback) (struct bio_st *, int, const char *, int, long, long); char *cb_arg; /* first argument for the callback */ int init; // 初始化标记, 初始化后为1 int shutdown; // 关闭标记, 不为0释放资源 int flags; /* extra storage: 控制函数行为 */ int retry_reason; // socket/ ssl异步阻塞 int num; void *ptr; // 文件句柄/内存地址 struct bio_st *next_bio; /* used by filter BIOs */ struct bio_st *prev_bio; /* used by filter BIOs */ int references; // 被引用数量 uint64_t num_read; // 已读取字节 uint64_t num_write; // 已写入字节 CRYPTO_EX_DATA ex_data; CRYPTO_RWLOCK *lock; };

配置加密(linux upx加壳)

项目配置文件独立程序体发布, 对于配置文件, 我们使用RSA2048加密由于明文长度需要小于(kBits/8-11)有以下两个问题

  1. 加密速度慢, 破解要求位数高, 256位AES破解强度相当于15360位RSA
  2. RSA对外发布的是公钥, 即使写死程序, 也面临潜在攻击(前文讲了公钥破解难度相当于程序破解的原因)

于是业界现有解决方案是混合加密, 也就是RSA2048加密AES秘钥, AES秘钥加密配置文件

TOB业务配置文件加密的权衡

  1. 可行性: RSA秘钥发布一定是只能发布公钥, 公钥实现过程中往往使用常用素数{3, 5, 7, 65535}. 指数和N一旦发布便可以被业务部署方得到.进一步, 被部署方得到的公钥可以解密得到AES, 从而加密配置文件可以在程序中得到.
  2. 安全性: 由于公钥的原因, 通过gdb调试可以破解得到AES秘钥.从而在本地得到配置文件明文.所以一方面需要内存进行解密操作, 另一方面需要尽量禁止程序gdb调试.
  3. 对于公钥和AES加密后的信息, 通过二进制破解可以检索到. 一方面需要进行代码明文混淆, 程序加壳处理, 另一方面可以考虑会话形式发布有有效期的AES秘钥.
  4. 最后, 需要依赖licence进行关键执行点检测宿主机是否被扩散.

AES

  1. 一般对于原文有两种加密模式
    • ECB模式:并发对多个分组进行加密
    • CBC模式:串行加密, 下一个加密块与上一个密文相关
  2. 对于AES加密解密, km上面文章很多, 这里就不复制粘贴咯~参考这篇即可,api很多,先用简单的api调通,比如cfb需要整除这些坑以后再解决就好~

AES加密

代码语言:javascript
复制
/* data structure that contains the key itself */AES_KEY key;/* set the encryption key */AES_set_encrypt_key(ckey, 128, &key);/* set where on the 128 bit encrypted block to begin encryption*/int num = 0;while (1) {    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, ifp);    AES_encrypt(indata, outdata, &key);    // AES_encrypt(indata, outdata, bytes_read, &key, ivec, &num,    //         AES_ENCRYPT);    printf("encode------\n%s\n-----\n%s\n\n", indata, outdata);    bytes_written = fwrite(outdata, 1, bytes_read, ofp);    if (bytes_read < AES_BLOCK_SIZE)        break;}

AES解密

代码语言:javascript
复制
/* data structure that contains the key itself */AES_KEY key;/* set the encryption key */AES_set_encrypt_key(ckey, 128, &key);/* set where on the 128 bit encrypted block to begin encryption*/int num = 0;while (1) {    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, ifp);    AES_encrypt(indata, outdata, &key);    // AES_encrypt(indata, outdata, bytes_read, &key, ivec, &num,    //         AES_ENCRYPT);    printf("encode------\n%s\n-----\n%s\n\n", indata, outdata);    bytes_written = fwrite(outdata, 1, bytes_read, ofp);    if (bytes_read < AES_BLOCK_SIZE)        break;}

upx加壳

  • 对于写死在代码里面的秘钥, 在编译出来的程序是长成这样的:
没有加壳的文件可以轻易找到写死秘钥
没有加壳的文件可以轻易找到写死秘钥

是不是看完之后就只想说一句woc… 也就是发布出去秘钥无论如何都是不安全的!!! 如果是直接发布AES秘钥可以直接找到 如果发布被RSA2048私钥加密的AES秘钥, 公钥暴露之后也就直接找到AES了.. 甚至可以直接替换公钥伪造license

  • 对于这种问题可通过联机验证来解决, 但是本题要求单机验证..所以只能发包过程中混淆+校验秘钥MD5处理.
  • 本工程使用基础的加壳软件upx进行实践

下载upx

  • 官网下载最新版就好了..平台支持多

加壳

upx加壳真的方便, upx ./exename 就可以了..

加壳出来文件可以看到是找不到原始字符串的啦!! 而且和源程序跑的结果一样呐 而且包体也变小啦!!

但是加壳容易, 解壳也分分钟哇!!!

破坏壳使得无法直接解密

这时候我们需要修改加壳程序, 只要改了一个字节, upx就不能顺畅的解密

  • 胆子小的可以修改(增删改)末尾的含有upx的一段, 和源程序肯定无关
  • windows据说copy个目录就能直接改变了一个字节
  • 胆子肥的可以在别的地方改一点东西, 这样破解难度高, 但是代码有潜在风险.

tricks

  1. 对于本文实现的加密程序, 没有必要base64存储.直接二进制存储,读取即可

项目遇到的坑

  1. include\openssl\rsa.h:13:34: fatal error: openssl/opensslconf.h: No such file or directory # include <openssl/opensslconf.h>: <openssl/opensslconf.h> 是由OpenSSL的Configure命令创建的, 源代码中只有.in文件
  2. 一开始我make之后链接编译文件夹里面的libcrypto和libssl, 发现仍然有undefined报错, 然后选择在gcc里面静态链接set(OPENSSL_USE_STATIC_LIBS TRUE)即可.

reference

Last But Not Least

  1. RSA算法好, 但是长度限制十分严格, 可以作为licence加密, 但是对于配置文件加密建议使用RSA对AES秘钥加密从而混合加密.
  2. 欢迎批评指正:P
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本思路
  • RSA简介
  • 环境部署
  • openssl RSA
    • RSA秘钥生成
      • RSA使用
        • 具体数据结构
        • 配置加密(linux upx加壳)
        • TOB业务配置文件加密的权衡
        • AES
          • AES加密
            • AES解密
            • upx加壳
              • 下载upx
                • 加壳
                  • 破坏壳使得无法直接解密
                  • tricks
                    • 项目遇到的坑
                    • reference
                    • Last But Not Least
                    相关产品与服务
                    专用宿主机
                    专用宿主机(CVM Dedicated Host,CDH)提供用户独享的物理服务器资源,满足您资源独享、资源物理隔离、安全、合规需求。专用宿主机搭载了腾讯云虚拟化系统,购买之后,您可在其上灵活创建、管理多个自定义规格的云服务器实例,自主规划物理资源的使用。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档