美文网首页
基于GmSSL实现HTTPS网络请求

基于GmSSL实现HTTPS网络请求

作者: 4c8fd9ddcb74 | 来源:发表于2021-08-27 16:06 被阅读0次

国密算法是国家商用密码算法的简称。自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

相关文章

网友评论

      本文标题:基于GmSSL实现HTTPS网络请求

      本文链接:https://www.haomeiwen.com/subject/lizpiltx.html