国密算法是国家商用密码算法的简称。自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 编译)
-
编译GmSSL库,并将导出的libcrypto.a 及libssl.a导入项目中
-
Build Settings ->Header Search Paths 中导入头文件路径(include文件夹)
二、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(¤tTime) + 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(¤tTime);
}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(¤tTime);
}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);
}
由于些文章仅为了记录重点,因此代码并不完全,后续会附上Demo