我为什么写这篇博客呢,在网上可以搜到关于SM4的信息很多,代码基本都是c和java语言的,可是在ios里面和OC语言相互调用的网上还没有搜到。
关于SM4算法的官方文档和源代码,见[中国互联网络信息中心官网](http://www.cnnic.net.cn/jscx/mixbz/sm4/)。
提示:在上面网站的页面中点击“详见SM4算法”,可能有时打不开,可过几天再试。
在实际调试时遇到很多问题,比如进制转换,有哪些输入的参数,输入的参数有什么格式要求,输出的是什么。
在网上找到的源代码包含这四个文件:
sm4.c sm4.h sm4test.c sms4.c
先集成到ios工程中,直接运行报错:
duplicate symbol _main in:
/Users/....../sm4test.o
/Users/....../sms4.o
在sm4test.o和sms4.o中都定义了main函数,需要改名
现在工程可以正常的运行起来了,为了调用上面这两个函数,为其添加两个对应的sm4test.h和sms4.h
//
// sm4test.h
// keyboardYU
//
// Created by yfc on 16/7/3.
// Copyright © 2016年 yfc. All rights reserved.
//
#ifndef sm4test_h
#define sm4test_h
int mainTest4();
#endif /* sm4test_h */
//
// sms4.h
// keyboardYU
//
// Created by yfc on 16/7/3.
// Copyright © 2016年 yfc. All rights reserved.
//
#ifndef sms4_h
#define sms4_h
int mainSms4();
#endif /* sms4_h */
在工程里需要的地方引入这两个头文件,这里以AppDelegate.m为例
在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中调用:
//
// AppDelegate.m
// keyboardYU
//
// Created by yfc on 16/6/27.
// Copyright © 2016年 yfc. All rights reserved.
//
#import "AppDelegate.h"
#import "sm4test.h"
#import "sms4.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
mainTest4();
mainSms4();
return YES;
}
@end
通过查看官方文档,可以确定:
当时做SM4的加解密时,遇到的问题现在分享给大家:
-(NSString *)add:(int)a andb:(int)b{ return [NSString stringWithFormat:@"%d",a+b]; }
void sm4_crypt_ecb( sm4_context *ctx, int mode, int length, unsigned char *input, unsigned char *output)
input是输入,output是输出,函数内部对output赋值,函数执行后,可以得到output的值。-----下面对源代码进行改造来和OC相互调用-----
// 1.将NSString类型的数据转成char[]类型
NSString *plainIn = @"this is plain text";
NSData *plainInData =[plainIn dataUsingEncoding:NSUTF8StringEncoding];
int dataLength =plainInData.length; ;
unsigned char plainInChar[dataLength];
// memcpy函数:将plainInData数据dataLength长度的部分复制到plainInChar里面
memcpy(plainInChar, plainInData.bytes, dataLength);
// 说明:1.dataLength这个长度很重要,刚开始时候用过strlen和sizeof,,plainIn.length发现英文情况下没问题,中文的话数据会有缺失
// 2.进制转换,要将平时的10进制数据转成16进制,在这里使用NSData它默认就是以16进制保存的,这里省略进制转换的步骤
// 2.对明文数据进行填充来保证位数是16的倍数,为了少声明一个变量在这里把填充和第1步放在一起
NSString *plainIn = @"this is plain text";
NSData *plainInData =[plainIn dataUsingEncoding:NSUTF8StringEncoding];
int plainInDataLength =plainInData.length; ;
// p是需要填充的数据也是填充的位数
int p = 16 - plainInDataLength % 16;
unsigned char plainInChar[plainInDataLength + p];
memcpy(plainInChar, plainInData.bytes, plainInDataLength);
// 进行数据填充
for (int i = 0; i < p; i++)
{
plainInChar[plainInDataLength + i] = p;
}
//3.验证一下填充后的char[]是不是最开始的明文数据
NSLog(@"plainInData=%@",plainInData);
NSData *data = [[NSData alloc]initWithBytes:plainInChar length:sizeof(plainInChar)-p];
NSLog(@"data=%@",data);
NSLog(@"填充后的char[]转成NSString=%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
//4.对plainInChar加密,由于源代码中加解密是放在一起的,现在在sm4test.c中新添加两个方法把加密和解密分开,由于计算length总出问题,所以直接把length作为参数传进去
void testEncodejiami(unsigned long lenght,unsigned char in[], unsigned char output[]){
unsigned char key[16] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
sm4_context ctx;
//设置上下文和密钥
sm4_setkey_enc(&ctx,key);
//加密
sm4_crypt_ecb(&ctx,1,lenght,in,output);
}
void testDecodejiemi(unsigned long lenght, unsigned char in[], unsigned char output[]){
unsigned char key[16] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
sm4_context ctx;
//设置上下文和密钥
sm4_setkey_dec(&ctx,key);
//解密
sm4_crypt_ecb(&ctx,0,lenght,in,output);
}
//5.调用刚才添加的方法加密
//定义输出密文的变量
unsigned char cipherOutChar[plainInDataLength + p];
testEncodejiami(plainInDataLength + p, plainInChar, cipherOutChar);
//对加密的数据输出
NSData *cipherTextData = [[NSData alloc]initWithBytes:cipherOutChar length:sizeof(cipherOutChar)];
NSLog(@"密文NSData=%@",cipherTextData);
NSLog(@"密文转成NSString=%@",[[NSString alloc]initWithData:cipherTextData encoding:NSUTF8StringEncoding]);
输出结果是:
由于是加密数据,只有解密后才能转成NSString打印,所以第二行打印是null
//6将cipherTextData作为输入,调用第4步的解密方法,进行解密
//将data拷贝到字符数组中
unsigned char cipherTextChar[cipherTextData.length];
memcpy(cipherTextChar, cipherTextData.bytes, cipherTextData.length);
//调用解密方法,输出是明文plainOutChar
unsigned char plainOutChar[cipherTextData.length];
testDecodejiemi(cipherTextData.length, cipherTextChar, plainOutChar);
//由于明文是填充过的,解密时候要去填充,去填充要在解密后才可以,在解密前是去不了的
int p2 = plainOutChar[sizeof(plainOutChar) - 1];//p2是填充的数据,也是填充的长度
int outLength = cipherTextData.length-p2;//明文的长度
//去掉填充得到明文
unsigned char plainOutWithoutPadding[outLength];
memcpy(plainOutWithoutPadding, plainOutChar, outLength);
//明文转成NSData 再转成NSString打印
NSData *outData = [[NSData alloc]initWithBytes:plainOutWithoutPadding length:sizeof(plainOutWithoutPadding)];
NSString *str =[[NSString alloc]initWithData:outData encoding:NSUTF8StringEncoding];
NSLog(@"解密得到的明文是:%@",str);
到此解密成功,源代码已上传http://download.csdn.net/detail/qq_15509071/9607224
gihub https://github.com/XiaoHeHe1/sm4_decode_and_encode