在ios中调用C语言的国密算法SM2以替换RSA
来源:互联网 发布:查询域名信息 编辑:程序博客网 时间:2024/06/08 08:02
SM2是一种非对称秘钥加密算法。用最明白的话说:
- 从一个私钥可以生成唯一一个公钥(不考虑随机数,在这里把随机数固定),所以测试工具里先输入私钥再点击生成秘钥对
- 一个公钥可以找出很多私钥
- 加密时输入的参数是:公钥和明文,输出:密文
- 解密时输入参数是:私钥和密文,输出:明文
- 选择一样的曲线,在官网的示例中有两条曲线,最后推荐的又是另一种曲线,网上的很多测试工具都是基于推荐曲线做的。官网链接http://www.oscca.gov.cn/News/201012/News_1198.htm%20%E5%AE%98%E7%BD%91
- 公钥是坐标点:P(Px,Py)
- 网上的测试工具有的输入输出都是16进制的,有的输入输出都是10进制的,要一致
- 加密时也会用随机数,如果随机数固定,则公钥也是固定的,密文也是固定的
- SM2分为秘钥交换,签名验证,公钥加密,网上的好多代码都是前两个,没有公钥加密,本文写的就是公钥加密
- 我用的sm2的c语言代码的下载地址是:http://download.csdn.net/detail/jk0o0/7834347#comment
- 密文分为C1,C2,C3,三部分,C1长度是64,C2是明文的长度,C3是32位
- C1 || C2 || C3 的意思就是拼在一起,而不是做什么或运算
现在把第10点的代码集合到自己的工程:
1.将不需要的文件删除,即下图红方框里的文件
注意:这些文件的编码格式不是utf8的,在xcode里面中文会显示成乱码,这些中文注释一定要看,在Windows系统下看。
2.导入到ios工程里,编译报错,没有openssl/ec.h这个文件
3.查一下什么是openssl,是关于密码的第三方开源库,然后需要把它集成到我们工程里。
3.1到https://github.com/x2on/OpenSSL-for-iPhone下载下来,是1.0.2版本的。下载后的文件是:
3.2只看build-libssl.sh文件,打开mac电脑的终端程序,将这个sh文件直接拖到终端里
3.3点回车,它会自动下载openssl的源代码并生成多个指令集的静态库。当然需要10分钟左右时间
3.4 openssl的源代码和编译好的静态库在mac电脑的根目录下: /Users/用户名/OpenSSL_1_0_2h.tar.gz
3.5在3.4的bin目录下就是编译好的静态库,包括
3.6 以iPhoneOS9.3-arm64.sdk文件夹为例:
3.7 lib目录下的libcrypto.a和libssl.a是生成的静态库,include目录下的openssl文件夹是对应的头文件。
3.8这个是arm64的库,如果同时需要支持armv7 arm64 则要将两个静态库合成一个,用命令:lipo -create /Users/yyy/Desktop/合到一起/libcrypto7.a /Users/yyy/Desktop/合到一起/libcrypto64.a -output /Users/yyy/Desktop/合到一起/libcrypto.a
3.9将这三个文件导入到我们工程里,编译一下,报错还是和之前一样:’openssl/ec.h’ file not found。点击xcode工程的搜索和替换,填写下面信息,点全部替换
4编译一下,报错 duplicate symbol _main in:
SM2.o
main.o
5.将sm2.c里面的main函数改名为mianSM2, 现在编译通过。
上面的中文注释很重要!只看part4是SM2公钥加密,
这四个是官网的示例曲线sm2_param_fp_192, sm2_param_fp_256, sm2_param_f2m_193,sm2_param_f2m_257,这个是官网推荐曲线,用这个sm2_param_recommand
在工程里需要的地方调用
test_part4(sm2_param_recommand, TYPE_GFp, 256);
这个方法。
5在编译可能会报错
把RSA改成RSA_Y
6.这是控制台的输出:
key_B->d:1649AB77 A00637BD 5E2EFE28 3FBF3535 34AA7F7C B89463F2 08DDBC29 20BB0DA0 key_B->P->x:191BFF81 48006EEA 72D857CB 974DB9F4 903B3CA3 655D8D59 7AD4663F 5044DCB1 key_B->P->y:E2F7888A F1FCD8C6 53A8059C D2F37985 5389F71A 7709E2C1 EE1E914C 855EF119 (BYTE *)H:B2054BCB 433B430C F6141BCF 2C98F617 7C78C6E5 ED5F953E E92B1F70 AAF70233 encrypt: message_data->C_2:D76B28B9 3A4B3765 997A3BBC 58F99873 1D0AA2d:1649AB77 A00637BD 5E2EFE28 3FBF3535 34AA7F7C B89463F2 08DDBC29 20BB0DA0 xy2->x:B18FE085 4DAF664D 357BD2DA 38714F02 026CF4A7 62BEFF0C DEFEE1AF 002DA0EE xy2->y:38ED9760 EF652F28 B81732B9 6247E135 87642E30 D9DFA9B3 C307A092 E415B07F (BYTE *)H:B2054BCB 433B430C F6141BCF 2C98F617 7C78C6E5 ED5F953E E92B1F70 AAF70233 decrypt: len: 19encryption standard
key_B->d:私钥
key_B->P->x:公钥x
key_B->P->y:公钥y
(BYTE *)H:t
message_data->C_2:C2
有几个问题:
1.输出的长度不全(输出C,C1时)
2.这个方法加密解密是放在一起的
3.明文输入的是字符串,而不是16进制的char数组
7.把这个方法分成加密和解密两个方法
void sm2JiaMi(char **sm2_param, int type, int point_bit_length , char *mingwen,char *miwen){ ec_param *ecp; sm2_ec_key *key_B; message_st message_data; ecp = ec_param_new(); ec_param_init(ecp, sm2_param, type, point_bit_length); key_B = sm2_ec_key_new(ecp);//用私钥和随机数导出一个公钥,实际应用时没有私钥,也就是没有这行代码,直接设置下面的公钥 sm2_ec_key_init(key_B, sm2_param_d_B[ecp->type], ecp);//把中间的值给key_b的b memset(&message_data, 0, sizeof(message_data));//设置明文 这里输入一个字符串 如果输入char[]需要稍微改动 message_data.message = (BYTE *)mingwen; message_data.message_byte_length = (int)strlen((char *)message_data.message); message_data.klen_bit = message_data.message_byte_length * 8;//随机数 拷贝到message_data.k,实际使用时应该随机生成这个数 sm2_hex2bin((BYTE *)sm2_param_k[ecp->type], message_data.k, ecp->point_byte_length);//设置公钥 sm2_bn2bin(key_B->P->x, message_data.public_key.x, ecp->point_byte_length); sm2_bn2bin(key_B->P->y, message_data.public_key.y, ecp->point_byte_length); DEFINE_SHOW_BIGNUM(key_B->P->x);//公钥PB =(xB ,yB ): 坐标xB : DEFINE_SHOW_BIGNUM(key_B->P->y);//坐标yB ://加密 sm2_encrypt(ecp, &message_data); memcpy(miwen, message_data.C, sizeof(message_data.C)); sm2_ec_key_free(key_B); ec_param_free(ecp);}
void sm2Jiemi(char **sm2_param, int type, int point_bit_length , char *miwen ,char output[] ){ ec_param *ecp; sm2_ec_key *key_B; message_st message_data; //ecp的开辟空间p a b n ecp = ec_param_new(); //ecp 给 pabn设置标准值 ec_param_init(ecp, sm2_param, type, point_bit_length); //给dp开辟空间 key_B = sm2_ec_key_new(ecp); //设置私钥,把中间的值给key_b的b sm2_ec_key_init(key_B, sm2_param_d_B[ecp->type], ecp); memset(&message_data, 0, sizeof(message_data)); //明文的长度,这个长度应该根据密文计算,这里固定写6 message_data.message_byte_length = 6; //k的比特长度是明文长度*8 message_data.klen_bit = message_data.message_byte_length * 8; //设置私钥,解密和公钥和随机数无关 sm2_bn2bin(key_B->d, message_data.private_key, ecp->point_byte_length); //私钥dB : DEFINE_SHOW_BIGNUM(key_B->d); //给解密后的明文开辟空间 message_data.decrypt = (BYTE *)OPENSSL_malloc(message_data.message_byte_length + 1); memset(message_data.decrypt, 0, message_data.message_byte_length+1);//置为0 //设置密文 for (int i = 0; i < 256; i++) { message_data.C[ i] = miwen[i]; } DEFINE_SHOW_STRING(message_data.C, 256); sm2_decrypt(ecp, &message_data); memcpy(output, message_data.decrypt, 100); OPENSSL_free(message_data.decrypt); sm2_ec_key_free(key_B); ec_param_free(ecp);}
这是如何在ios工程调用上面两个方法 NSString *mingwen = @"123456"; char miwen[1024]; sm2JiaMi(sm2_param_recommand, TYPE_GFp, 256, [mingwen UTF8String], miwen); //密文前面多个04 在用其他工具对密文解密时需要去掉 NSData *miwendata = [[NSData alloc]initWithBytes:miwen length: mingwen.length+32+64 +2]; NSLog(@"密文data=%@", miwendata ); //解密和加密类似将char数组转成nsdata再转成nsstring char output[100]; sm2Jiemi(sm2_param_recommand, TYPE_GFp, 256, miwen,output); NSString *mingwenout = [[NSString alloc]initWithCString:output encoding:NSUTF8StringEncoding]; NSLog(@"---解密后%@---",mingwenout);
如果需要传入自己公钥加密,则加密方法要相应改一下
//使用传入的公钥加密void sm2JiaMiWithPublicKey(char **sm2_param, int type, int point_bit_length , char mingwen[],char *miwen,unsigned char px[],unsigned char py[]){ ec_param *ecp; sm2_ec_key *key_B; message_st message_data; ecp = ec_param_new(); ec_param_init(ecp, sm2_param, type, point_bit_length); key_B = sm2_ec_key_new(ecp); sm2_ec_key_init(key_B, sm2_param_d_B[ecp->type], ecp); memset(&message_data, 0, sizeof(message_data)); message_data.message = (BYTE*)mingwen;// memcpy(message_data.message, mingwen,strlen(mingwen) ); message_data.message_byte_length = 8; message_data.klen_bit = message_data.message_byte_length * 8; //这个是固定的随机数 //sm2_hex2bin((BYTE *)sm2_param_k[ecp->type], message_data.k, ecp->point_byte_length); //随机数种子 static const char rnd_seed[] = "random num c random num seed random num c random num seed"; RAND_seed(rnd_seed, sizeof rnd_seed); unsigned char suijishu[32]; //生成随机数 RAND_pseudo_bytes(suijishu,32); for( int i=0;i<sizeof suijishu;i++){ //printf("%02x", suijishu[i]); message_data.k[i]=suijishu[i]; } printf("\n"); DEFINE_SHOW_STRING(message_data.k, sizeof(message_data.k)); //设置px //printf("px\n"); for( int i=0;i<32;i++){ //printf("%02x", px[i]); message_data.public_key.x[i]=px[i]; } //printf("\n"); //设置py //printf("py\n"); for( int i=0;i<32;i++){ //printf("%02x", py[i]); message_data.public_key.y[i]=py[i]; } //printf("\n"); DEFINE_SHOW_BIGNUM(key_B->P->x);//公钥PB =(xB ,yB ): 坐标xB : DEFINE_SHOW_BIGNUM(key_B->P->y);//坐标yB : DEFINE_SHOW_STRING(message_data.public_key.x, 32); DEFINE_SHOW_STRING(message_data.public_key.y, 32); sm2_encrypt(ecp, &message_data); memcpy(miwen, message_data.C, sizeof(message_data.C)); sm2_ec_key_free(key_B); ec_param_free(ecp);}
调用方法是
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //使用固定的公钥加密// NSString *mingwen = @"123456";// char miwen[1024];// sm2JiaMi(sm2_param_recommand, TYPE_GFp, 256, [mingwen UTF8String], miwen);// //密文前面多个04 在用其他工具对密文解密时需要去掉// NSData *miwendata = [[NSData alloc]initWithBytes:miwen length: mingwen.length+32+64 +2];// NSLog(@"密文data=%@", miwendata ); //使用自己已知的公钥加密 NSString *mingwen = @"123456"; char miwen[1024]; NSString *px_ = [@"F5AB4BCC 007AF4C3 862CF413 57C035AE 090B39B3 A7204E2D E888753E 99EC507A" stringByReplacingOccurrencesOfString:@" " withString:@""]; NSString *py_ = [@"BE394FC1 0F50FC59 F6586DF7 B493150E 5DF7F575 BC1214FE D849E967 D15993FF" stringByReplacingOccurrencesOfString:@" " withString:@""]; NSData *px_data = [self dataFromHexString:px_]; NSData *py_data = [self dataFromHexString:py_]; sm2JiaMiWithPublicKey(sm2_param_recommand, TYPE_GFp, 256, [mingwen UTF8String], miwen, px_data.bytes,py_data.bytes); //密文前面多个04 在用其他工具对密文解密时需要去掉 NSData *miwendata = [[NSData alloc]initWithBytes:miwen length: mingwen.length+32+64 +2]; NSLog(@"密文data=%@", miwendata ); //解密和加密类似将char数组转成nsdata再转成nsstring char output[100]; sm2Jiemi(sm2_param_recommand, TYPE_GFp, 256, miwen,output); NSString *mingwenout = [[NSString alloc]initWithCString:output encoding:NSUTF8StringEncoding]; NSLog(@"---解密后%@---",mingwenout); return YES;}- (NSData *)dataFromHexString:(NSString *)input { const char *chars = [input UTF8String]; int i = 0; NSUInteger len = input.length; NSMutableData *data = [NSMutableData dataWithCapacity:len / 2]; char byteChars[3] = {'\0','\0','\0'}; unsigned long wholeByte; while (i < len) { byteChars[0] = chars[i++]; byteChars[1] = chars[i++]; wholeByte = strtoul(byteChars, NULL, 16); [data appendBytes:&wholeByte length:1]; } return data;}
发现有崩溃,把char miwen[100] 改成 char miwen[1024]即可,文章中已修改(20170112)
最新代码已上传 :
http://download.csdn.net/detail/qq_15509071/9736057 这是修正后的代码地址(20170113)
//最新代码 增加使用自定义私钥解密,加解密时04的处理 明文不限制位数
http://download.csdn.net/detail/qq_15509071/9784753 (20170317)
- 在ios中调用C语言的国密算法SM2以替换RSA
- 在IOS工程中使用OC调用C语言国密算法SM4(来替换DES算法)
- SM2算法第二十四篇:谈谈PBOC3.0中使用的国密SM2算法
- 谈谈PBOC3.0中使用的国密SM2算法
- 谈谈PBOC3.0中使用的国密SM2算法
- PBOC3.0中使用的国密SM2算法
- 谈谈PBOC3.0中使用的国密SM2算法
- SM2算法第十三篇:SM2密钥协商协议的C语言实现
- 【国密算法那点事儿】解读DES和SM4、RSA和SM2及SM3
- 【国密算法那点事儿】解读DES和SM4、RSA和SM2及SM3
- 【国密算法那点事儿】解读DES和SM4、RSA和SM2及SM3
- C语言实现的SM2数字签名验证
- SM2算法第十五篇:ECDSA数字签名算法的C语言实现
- C语言实现的RSA算法程序
- JS版SM2国密算法的签名验证
- 关于国密算法 SM1,SM2,SM3,SM4 的笔记
- 关于国密算法 SM1,SM2,SM3,SM4 的笔记
- 关于国密算法 SM1,SM2,SM3,SM4 的笔记
- CocoaPods还算完整的教程(安装、使用、项目管理、注意事项)
- 解决php的“It is not safe to rely on the system’s time
- linux上安装nginx
- Mysql优化查询速度进行中
- activity的4种启动模式
- 在ios中调用C语言的国密算法SM2以替换RSA
- 对象资源管理及智能指针的简单用法
- 将一个从大到小的数组,用以下排序方法排序成从小到大的,()最快。----阿里巴巴2015实习生笔试题
- java synchronized详解
- 实现WEB压缩的三种途径:Web服务器(Nginx/Apache)、Php扩展、Php代码
- CSS--position属性
- HDU 5372 线段树
- Objective-C——Category、Extension、Protocol
- CSS3属性之多栏布局column