在IOS工程中使用OC调用C语言国密算法SM4(来替换DES算法)

我为什么写这篇博客呢,在网上可以搜到关于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函数,需要改名
  1. sm4test.c里面的main()改名为mainTest4()
  2. sms4.c里面的main()改名为mainSms4()

现在工程可以正常的运行起来了,为了调用上面这两个函数,为其添加两个对应的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

在控制台可以看到运行以上代码输出的是:
这里写图片描述

通过查看官方文档,可以确定:

  1. ECB模式下的明文和秘钥的值都是:01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
  2. 密文是:68 1e df 34 d2 06 96 5e 86 b3 e9 4f 53 6e 42 46
  3. 对一段明文反复加密100万次:59 52 98 c7 c6 fd 27 1f 04 02 f8 04 c3 3d 3f 66

当时做SM4的加解密时,遇到的问题现在分享给大家:

  1. SM4加密分为ECB和CBC模式,选用的是ECB
  2. SM4的明文必须是16的倍数,查了好多才查到,如果不是16的倍数要做数据填充Padding,不填充的话每次加密出来的数据都不一样,同样解密后也要去掉填充。(秘钥相同,明文相同,填充方式相同则密文一定相同)
  3. 加密时输入的是16位的秘钥和明文,输出密文
  4. 解密时输入的是16位的秘钥和密文,输出明文
  5. 明文和密文的长度是一样的(若不是16的倍数要算填充后的)
  6. 加解密调用的函数都是C的,接收和产生的数据类型都是char数组:unsigned char[],数组内容是16进制的。
  7. 在iOS中函数的返回值可以直接return
    -(NSString *)add:(int)a andb:(int)b{ return [NSString stringWithFormat:@"%d",a+b]; }
    在c语言中函数返回值不可以是char[],可以像这样写
    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

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
iOS使用OpenSSL进行3DES CBC加解密,你可以使用OpenSSL的C语言接口来实现。以下是一个示例代码,演示了如何在Objective-C使用OpenSSL进行3DES CBC加解密: ```objc // 导入OpenSSL头文件 #import <openssl/des.h> // 加密函数 NSString *encrypt3DESCBC(NSString *plainText, NSString *key, NSString *iv) { const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding]; const char *cIV = [iv cStringUsingEncoding:NSUTF8StringEncoding]; const char *cPlainText = [plainText cStringUsingEncoding:NSUTF8StringEncoding]; int plainTextLength = strlen(cPlainText); // 初始化3DES上下文 DES_cblock desKey; memset(desKey, 0, sizeof(DES_cblock)); memcpy(desKey, cKey, 24); DES_key_schedule keySchedule; DES_set_key_unchecked(&desKey, &keySchedule); // 加密 size_t bufferSize = plainTextLength + 8; unsigned char *buffer = malloc(bufferSize); memset(buffer, 0, bufferSize); DES_ncbc_encrypt((unsigned char *)cPlainText, buffer, plainTextLength, &keySchedule, (DES_cblock *)cIV, DES_ENCRYPT); // 将加密后的数据转换为十六进制字符串 NSMutableString *encryptedHex = [NSMutableString stringWithCapacity:bufferSize * 2]; for (int i = 0; i < bufferSize; i++) { [encryptedHex appendFormat:@"%02x", buffer[i]]; } free(buffer); return encryptedHex; } // 解密函数 NSString *decrypt3DESCBC(NSString *encryptedText, NSString *key, NSString *iv) { const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding]; const char *cIV = [iv cStringUsingEncoding:NSUTF8StringEncoding]; const char *cEncryptedText = [encryptedText cStringUsingEncoding:NSUTF8StringEncoding]; int encryptedTextLength = (int)[encryptedText length] / 2; // 初始化3DES上下文 DES_cblock desKey; memset(desKey, 0, sizeof(DES_cblock)); memcpy(desKey, cKey, 24); DES_key_schedule keySchedule; DES_set_key_unchecked(&desKey, &keySchedule); // 将十六进制字符串转换为加密后的数据 unsigned char *buffer = malloc(encryptedTextLength); memset(buffer, 0, encryptedTextLength); for (int i = 0; i < encryptedTextLength; i++) { sscanf(cEncryptedText + i * 2, "%2hhx", &buffer[i]); } // 解密 size_t bufferSize = encryptedTextLength; unsigned char *plainText = malloc(bufferSize); memset(plainText, 0, bufferSize); DES_ncbc_encrypt(buffer, plainText, bufferSize, &keySchedule, (DES_cblock *)cIV, DES_DECRYPT); free(buffer); return [[NSString alloc] initWithBytes:plainText length:bufferSize encoding:NSUTF8StringEncoding]; } ``` 使用示例: ```objc NSString *plainText = @"This is a secret message."; NSString *key = @"123456781234567812345678"; NSString *iv = @"12345678"; NSString *encryptedText = encrypt3DESCBC(plainText, key, iv); NSLog(@"Encrypted Text: %@", encryptedText); NSString *decryptedText = decrypt3DESCBC(encryptedText, key, iv); NSLog(@"Decrypted Text: %@", decryptedText); ``` 请确保你已经正确导入OpenSSL库,并且在编译设置设置了正确的头文件搜索路径和链接库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值