基于GmSSL实现HTTPS网络请求

国密算法是国家商用密码算法的简称。自2012年以来,国家密码管理局以《中华人民共和国密码行业标准》的方式,陆续公布了SM2/SM3/SM4等密码算法标准及其应用规范。其中“SM”代表“商密”,即用于商用的、不涉及国家秘密的密码技术。其中SM2为基于椭圆曲线密码的公钥密码算法标准,包含数字签名、密钥交换和公钥加密,用于替换RSA/Diffie-Hellman/ECDSA/ECDH等国际算法;SM3为密码哈希算法,用于替代MD5/SHA-1/SHA-256等国际算法;SM4为分组密码,用于替代DES/AES等国际算法;SM9为基于身份的密码算法,可以替代基于数字证书的PKI/CA体系。通过部署国密算法,可以降低由弱密码和错误实现带来的安全风险和部署PKI/CA带来的开销。

基于安全性考虑,有些项目(特别涉及到银行方面)会需要发起网络请求使用支持GmSSL的请求,这就涉及到传统的https请求无法达到需求,如有需要可以去深入了解HTTPS、TLS、SSL、HTTP区别和关系,此处主要记录如何实现功能。

一、准备工作(如何编译,可以参考本人另一编文章GmSSL 编译

  1. 编译GmSSL库,并将导出的libcrypto.a 及libssl.a导入项目中
    image.png
  2. Build Settings ->Header Search Paths 中导入头文件路径(include文件夹)
    image.png

二、C语言基于BIO HTTPS网络请求(此处仅放部分代码)

void *bioSSLHttpsOnThread( void *manager){
struct HttpsBioMgr *mgr = (struct HttpsBioMgr*)manager;
    char *hostName = malloc(mgr->hostLength + mgr->portLength + 2);
    char *postData = malloc(1024 * 10);
    BIO *out = NULL;
    SSL_CTX *ssl_ctx = NULL;
    SSL *ssl;
    BIO *ssl_bio;
    int i, len, off, ret = 1;
    if (checkNull(mgr->host)) {
        fprintf(stderr, "hostport error\n"); err;
    }
    
    //超时处理
    time_t currentTime;
    time_t timeOut = time(&currentTime) + mgr->timeOut; //开启定时器
    
    switch (mgr->clientType) {
        case GMTLS:
            ssl_ctx = SSL_CTX_new(GMTLS_client_method());
            break;
        case SSLV23:
            ssl_ctx = SSL_CTX_new(SSLv23_client_method());
            break;
        default:
            fprintf(stderr, "SSL_CTX_new error\n");
            goto err;
    }
    
    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);    
    
    ssl = SSL_new(ssl_ctx);
    SSL_set_connect_state(ssl);
    
    printf("Enable peername verification \n");
        
    sprintf(hostName, "%s", mgr->host);
    sprintf(hostName, "%s:", hostName);
    sprintf(hostName, "%s%s", hostName,mgr->port);
    if (SSL_set1_host(ssl, hostName) <= 0){
        goto err;
    }
    ssl_bio = BIO_new(BIO_f_ssl());
    BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
    
    out = BIO_new(BIO_s_connect());
    BIO_set_conn_hostname(out, hostName);
    BIO_set_nbio(out, 1);
    out = BIO_push(ssl_bio, out);
    len = (int)configureRequestData(mgr, postData);
    off = 0;
    
    for (;;) {
        i = BIO_write(out, &(postData[off]), len);
        if (timeOut - currentTime >= 0) {
            time(&currentTime);
        }else{
            goto timeOut;
        }
        if (i <= 0) {
            if (BIO_should_retry(out)) {
                sleep(0.1);
                continue;
            } else {
                goto err;
            }
        }
        off += i;
        len -= i;
        if (len <= 0)
            break;
    }
    for (;;) {
        i = BIO_read(out, mgr->responseData, (int)mgr->completeLength);
        if (timeOut - currentTime >= 0) {
            time(&currentTime);
        }else{
            goto timeOut;
        }
        if (i == 0)
            break;
        if (i <= 0) {
            if (BIO_should_retry(out)) {
                sleep(0.1);
                continue;
            }
            goto err;
        }
        print("接收回调 %s",mgr->responseData);
    }
    ret = 1;
    goto done;

timeOut:
    
err:
    if (ERR_peek_last_error() == 0) { /* system call error */
        fprintf(stderr, "errno=%d ", errno);
        perror("error");
    } else
        ERR_print_errors_fp(stderr);
    
done:
    BIO_free_all(out);
    SSL_CTX_free(ssl_ctx);
    free(postData);
    free(hostName);
    freeHttpsBioMgr(mgr);
    return ((void *)0);
}

//处理请求数据,以满足HTTPS协议格式
 long configureRequestData(struct HttpsBioMgr* mgr, char *responseData){
   unsigned long re_len = 128 + mgr->pathLength + mgr->hostLength + mgr->portLength + mgr->headersLength + mgr->requestBodyLength;
    char *post = NULL;
    post = malloc(re_len);
    
    char *model;
    switch (mgr->httpsReqType) {
        case GET:
            model = "GET";
            break;
        case POST:
            model = "POST";
            break;
        default:
            return -1;
            break;
    }
    if (!checkNull(mgr->path)) {
        sprintf(post, "%s /%s HTTP/1.0\r\n",model, mgr->path);
    }
    if (!checkNull(mgr->host)) {
        sprintf(post, "%sHost: %s:%s\r\n",post, mgr->host, mgr->port);
    }
    if (!checkNull(mgr->headers)) {
        sprintf(post, "%s%s", post, mgr->headers);
    }
    if (!checkNull(mgr->requestBody)) {
        sprintf(post, "%sContent-Length: %lu\r\n\r\n", post, mgr->requestBodyLength);  //作为请求头结束标志,需要有两对\r\n
        sprintf(post, "%s%s\r\n", post, mgr->requestBody);     // 当业务需要上传非字符串数据的时候, 会造成数据传输丢失或失败
    }
    sprintf(post, "%s\r\n", post); //添加请求结束标志
    memset(responseData, 0, re_len);
    memcpy(responseData, post, re_len);
    free(post);
    return re_len;
}

三、OC封装C语言请求(POST),此处需要注意,使用UTF8String进行传参,而且需要计算好数据的长度传入C方法中,保证C中分配内存正确

- (void)BIOPOST:(NSString *)url params:(NSDictionary *)params completionHandler:(void (^)(NSData *data, NSError *error))completionHandler{
    self.completionHandler = completionHandler;
    const long len = 1024 * 1024; //接收数据长度
    NSString *apiUrl = url;
    NSString *paramsStr = [self postReqBodyStrWithParams:params];
    
    NSData *apiUrlData = [apiUrl dataUsingEncoding:NSUTF8StringEncoding];
    NSData *headerStrData = [self.headerStr dataUsingEncoding:NSUTF8StringEncoding];
    NSData *paramsData = [paramsStr dataUsingEncoding:NSUTF8StringEncoding];
    //OC对象转C指针
    void *requester = (__bridge_retained void*)self;
    
    bioGmsslPOST(requester, [self.headerStr UTF8String], headerStrData.length, [apiUrl UTF8String], apiUrlData.length, [paramsStr UTF8String], paramsData.length, len, self.timeOut <= 0 ? 30:(int)self.timeOut, resposeHandler);
}
POST请求成功

由于些文章仅为了记录重点,因此代码并不完全,后续会附上Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容