iOS MT19937随机数生成,结合AES-CBC加密算法实现。

news/2024/7/20 20:59:30 标签: ios, cocoa, macos

按处理顺序说明:

1. 生成随机数序列字符串函数

生成方法MT19937,初始种子seed,利用C++库方法,生成:

#include <random> //C++ 库头文件引入

NSString * JKJMT19937Seed(uint32_t seed) {
    NSLog(@"MT19937Seed种子:%u",seed);
    NSMutableArray *ranVlues = [NSMutableArray array];
    std::mt19937 rngCPluc2(seed);
    for (int i = 0; i < 64; ++i) {//连续生产64个随机数
        unsigned long ranValue = rngCPluc2();
        NSNumber *rngVal = [NSNumber numberWithUnsignedLong:ranValue];
        [ranVlues addObject:rngVal];
    }
    NSString *ranSeriesStr = [ranVlues componentsJoinedByString:@""];
    NSLog(@"随机数值序列拼接字符串key1 = %@",ranSeriesStr);
    return ranSeriesStr;
}

2. 对第一部中的随机数序列字符串进行sha256加密,得到64字节的一个数据流函数。

#import <CommonCrypto/CommonCrypto.h>//加密头文件

NSString * sha256String(NSString *inputString) {
    const char *str = inputString.UTF8String;
    uint8_t buffer[CC_SHA256_DIGEST_LENGTH];

    CC_SHA256(str, (CC_LONG)strlen(str), buffer);
    NSMutableString *strM = [NSMutableString string];
    for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
        [strM appendFormat:@"%02x", buffer[i]];
    }
    return [strM copy];
}

 3. AES-CBC加密解密方法

/*
    CCCrypt方法提供了CBC 和 ECB 两种AES加密模式,
    如果不传参数,kCCOptionECBMode,则默认即使CBC模式加密。
    ECB模式不是一种可靠安全的加密模式,推荐使用CBC模式。
    另外也可以通过其他的库或者方法实现GCM等方式的AES加密。在后面的代码块中翻入方法。
*/
NSData *aesCBCEncrypt(NSData *inputData, NSData *keyData, NSData *ivData) {
    NSData *key = keyData;
    // 准备一个初始化向量(IV),IV 的长度需要与 AES 加密模式匹配
    // 在 AES-CBC 模式下,IV 的长度通常为 AES 块大小(16 字节)
    NSData *iv = ivData;

    // 创建一个用于存储加密后数据的 NSMutableData 对象
    NSMutableData *encryptedData = [NSMutableData dataWithLength:inputData.length + kCCBlockSizeAES128];
    size_t encryptedDataLength = 0;
    
    // 使用 AES-256 加密(CBC)
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES,
                                          kCCOptionPKCS7Padding,
                                          key.bytes, key.length,
                                          iv.bytes, // 无需传入 IV
                                          inputData.bytes, inputData.length,
                                          encryptedData.mutableBytes, encryptedData.length,
                                          &encryptedDataLength);
    
    if (cryptStatus == kCCSuccess) {
        encryptedData.length = encryptedDataLength;
        return encryptedData;
    } else {
        NSLog(@"AES-256 加密失败");
        return nil;
    }
}

NSData *aesCBCDecrypt(NSData *encryptedData, NSData *keyData, NSData *ivData) {
    // 准备一个初始化向量(IV)
    // 在 AES-CBC 模式下,IV 的长度通常为 AES 块大小(16 字节)
    NSData *iv = ivData;
    
    // 创建一个用于存储解密后数据的 NSMutableData 对象
    NSMutableData *decryptedData = [NSMutableData dataWithLength:encryptedData.length];
    size_t decryptedDataLength = 0;
    
    // 进行 AES 解密
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES,
                                          kCCOptionPKCS7Padding,
                                          keyData.bytes, keyData.length,
                                          iv.bytes, // 传入 IV
                                          encryptedData.bytes, encryptedData.length,
                                          decryptedData.mutableBytes, decryptedData.length,
                                          &decryptedDataLength);
    
    if (cryptStatus == kCCSuccess) {
        decryptedData.length = decryptedDataLength;
        return decryptedData;
    } else {
        NSLog(@"AES 解密失败");
        return nil;
    }
}

/*
    CCCrypt方法提供了CBC 和 ECB 两种AES加密模式,
    如果不传参数,kCCOptionECBMode,则默认即使CBC模式加密。
    ECB模式不是一种可靠安全的加密模式,推荐使用CBC模式。
    另外也可以通过其他的库或者方法实现GCM等方式的AES加密。在后面的代码块中翻入方法。
*/

4. 前三个步骤已经完成了加密的基本算法代码,接下来直接示例实现一个传参加密:

- (NSString *)EncryptStringFromtServiceStartMap:(NSDictionary *)json {
    //补充ts字段。
    NSMutableDictionary *muJson = [NSMutableDictionary dictionaryWithDictionary:json];
    NSTimeInterval tsVal =  [[NSDate date] timeIntervalSince1970];
    [muJson setObject:[NSNumber numberWithInteger:(NSUInteger)(1000* tsVal)] forKey:@"ts"];
    NSString *jsonString = nil;
    NSError  *error;
    NSData   *jsonData = [NSJSONSerialization dataWithJSONObject:muJson
                                                         options:NSJSONWritingPrettyPrinted
                                                           error:&error];
    [self loadAESKeyAndIVData];//获取key和iv。
    //进行AES加密。
    NSData *aesData =  aesCBCEncrypt(jsonData, self.aesSHA256keyData, self.aesIVData);
    
    //base64再次加密AES-CBC加密后的数据流,返回值作为start_service的参数
    NSString *base64 =  [aesData base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
    NSString *encryptStr = base64;
    NSLog(@"jsonContent:%@",muJson);
    NSLog(@"start_service参数encryptStr = %@",encryptStr);
    NSData *uncodeAESData = aesCBCDecrypt(aesData, self.aesSHA256keyData, self.aesIVData);
    if(uncodeAESData){
        NSError *error = nil;
        NSDictionary *uncodeDict = [NSJSONSerialization JSONObjectWithData:uncodeAESData options:kNilOptions error:&error];
        if (error) {
            NSString *uncodeJSON = [[NSString alloc] initWithData:uncodeAESData encoding:NSUTF8StringEncoding];
            if(uncodeJSON){
                NSLog(@"验证解密ServiceMap:%@", uncodeJSON);
            }else{
                NSLog(@"验证解密ServiceMap: 解析失败:%@", error);
            }
        } else {
            NSLog(@"验证解密ServiceMap:%@",uncodeDict);
        }
    }
    return encryptStr;
}

//初始化AES加密的Key和iv向量数据。
- (void)loadAESKeyAndIVData {
    // 设置随机种子为 4728423
    NSString *mt19937Str = JKJMT19937Seed(4728423);
    NSString *sha256Str = sha256String(mt19937Str);
    
    /*
     密钥,截取sha256的64个字节前面32个字节
     IV:截取sha256的64个字节前面16个字节
     */
    NSString *keyString = substringWithLength(sha256Str, 32);
    NSString *ivString  = substringWithLength(sha256Str, 16);
    
    //获取AES加密Key
    _aesSHA256keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    
    //获取AES加密的IV向量
    _aesIVData = [ivString dataUsingEncoding:NSUTF8StringEncoding];
}

5. 归纳,完成4的操作,用到了一下两个库,a. C++中的random库,实现mt19937随机数;b. CommonCrypto库,实现AES-CBC对称加密,实现SHA256加密。c. 另外Base64是OC中的NSData(NSDataBase64Encoding)分类方法提供。

#include <random>

#import <CommonCrypto/CommonCrypto.h>

 6. 补充, AES加密如果需要GCM或者其它(非CBC、非ECB)模式的,可能需要用到如下的方法去实现:a. 使用iOS13之后的库, b. 使用第三方库(libsodium-ios为例)

  • a.使用iOS13之后的库代码如下:
//
//  AESEncryptor.swift
//
//  Created by xw.long on 2024/4/7.
//

import Foundation
import CryptoKit
import CommonCrypto


@available(iOS 13.0, *)
@objc class AESEncryptor: NSObject {
    
    
    func encryptDataUsingCBCMode(data: Data, key: Data, iv: Data) -> Data? {
        let bufferSize = data.count + kCCBlockSizeAES128
        var buffer = [UInt8](repeating: 0, count: bufferSize)
        var numBytesEncrypted: size_t = 0
        
        let cryptStatus = data.withUnsafeBytes { dataBytes in
            key.withUnsafeBytes { keyBytes in
                iv.withUnsafeBytes { ivBytes in
                    CCCrypt(CCOperation(kCCEncrypt),
                            CCAlgorithm(kCCAlgorithmAES),
                            CCOptions(kCCOptionPKCS7Padding),
                            keyBytes.baseAddress, key.count,
                            ivBytes.baseAddress,
                            dataBytes.baseAddress, data.count,
                            &buffer, bufferSize,
                            &numBytesEncrypted)
                }
            }
        }
        
        if cryptStatus == kCCSuccess {
            return Data(buffer.prefix(Int(numBytesEncrypted)))
        } else {
            print("Error: \(cryptStatus)")
            return nil
        }
    }



    
    
    
    
    
    // 加密方法
    @objc func encrypt(content: String, key: String, iv: String) -> Data? {
        guard let keyData = Data(hexString: key),
              let ivData = Data(hexString: iv),
              let contentData = content.data(using: .utf8) else {
            print("AESEncryptor 无法将输入转换为Data")
            return nil
        }
        do {
            // 创建AES密钥
            let aesKey = SymmetricKey(data: keyData)
            
            // 使用AES-256-GCM加密
            let sealedBox = try AES.GCM.seal(contentData, using: aesKey, nonce: AES.GCM.Nonce(data: ivData))
            
            // 返回加密后的数据
            return sealedBox.combined
        } catch {
            print("AESEncryptor 加密失败: \(error)")
            return nil
        }
    }
    
    // 解密方法
    @objc func decrypt(encryptedData: Data, key: String, iv: String) -> String? {
        guard let keyData = Data(hexString: key),
              let ivData = Data(hexString: iv) else {
            print("AESEncryptor 无法将输入转换为Data")
            return nil
        }
        
        do {
            // 创建AES密钥
            let aesKey = SymmetricKey(data: keyData)
            
            // 解密
            let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
            let decryptedData = try AES.GCM.open(sealedBox, using: aesKey)
            
            // 将解密后的数据转换为字符串
            guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
                print("解密后的数据无法转换为字符串")
                return nil
            }
            
            return decryptedString
        } catch {
            print("AESEncryptor 解密失败: \(error)")
            return nil
        }
    }
    
    // 测试方法
    @objc static func test() {
        let content = "hello world"
        let key = "3891346e92151849d58e70de02a05c596b48afe1ae2bdeedf3e69c661c2ea2ae"
        let iv = "3891346e9215"
        
         if let encryptedData = AESEncryptor().encrypt(content: content, key: key, iv: iv) {
            print("AESEncryptor 加密后的数据: \(encryptedData.base64EncodedString())")
            
            if let decryptedString = AESEncryptor().decrypt(encryptedData: encryptedData, key: key, iv: iv) {
                print("AESEncryptor 解密后的字符串: \(decryptedString)")
            }
        }
    }

}

// 十六进制字符串转换为Data扩展
extension Data {
    init?(hexString: String) {
        var hexString = hexString
        var data = Data()
        
        while hexString.count > 0 {
            let subIndex = hexString.index(hexString.startIndex, offsetBy: 2)
            let hexChar = String(hexString[..<subIndex])
            hexString = String(hexString[subIndex...])
            
            guard let byte = UInt8(hexChar, radix: 16) else {
                return nil
            }
            
            data.append(byte)
        }
        
        self = data
    }
}


+ (void)swiftTest {
    NSString *originalString = @"3891346e92151849d89070de02a05c596b48a123ae2bdeedf3e69c661c2ea2ae";
    
    // 截取新的key和iv
    NSString *key = [originalString substringToIndex:32];
    NSString *iv = [originalString substringToIndex:12];
    
    // 待加密的内容
    NSString *content = @"hello world";
    
    // 调用加密方法
    if (@available(iOS 13.0, *)) {
        NSData *encryptedData = [[AESEncryptor new] encryptWithContent:content key:key iv:iv];
        // 将加密后的数据转换为Base64字符串
        NSString *encryptedString = [encryptedData base64EncodedStringWithOptions:0];
        NSLog(@"加密后的字符串: %@", encryptedString);
    } else {
        // Fallback on earlier versions
    }
}


  • b. 使用第三方库(libsodium-ios为例)

库引用可以通过cocoa-pod方法,在Podfile文件中加入如下代码,并在对应文件目录下执行【pod install】。

 platform :ios, '12.0'

也可以通过github 下载:https://github.com/mochtu/libsodium-ios

# Uncomment the next line to define a global platform for your project

 platform :ios, '12.0'

target 'MYPROJECT' do

  # Comment the next line if you don't want to use dynamic frameworks

  use_frameworks!

  # Pods for MYPROJECT

  pod 'libsodium-ios'

end

post_install do |pi|

    pi.pods_project.targets.each do |t|

      t.build_configurations.each do |config|

        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'

      end

    end

end

下面是库引入之后得代码示例:

#include <sodium.h>


NSData *encryptStringWithAES256EStream(NSString *inputString, const unsigned char *nonce, const unsigned char *key) {
    // 转换输入字符串为NSData
    NSData *inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
    
    // 获取输入数据的长度
    NSUInteger inputLength = inputData.length;
    
    // 准备输出缓冲区
    NSMutableData *encryptedData = [NSMutableData dataWithLength:inputLength];
    
    // 产生AES-256-ESTREAM流密码
    unsigned char stream[inputLength];
    crypto_stream_aes256estream(stream, inputLength, nonce, key);
    
    // 对输入数据进行异或运算,实现加密
    unsigned char *inputBytes = (unsigned char *)inputData.bytes;
    unsigned char *encryptedBytes = (unsigned char *)encryptedData.mutableBytes;
    for (NSUInteger i = 0; i < inputLength; i++) {
        encryptedBytes[i] = inputBytes[i] ^ stream[i];
    }
    
    return encryptedData;
}

+ (void)cryptoTest {
    // 初始化libsodium库
    if (sodium_init() == -1) {
        NSLog(@"libsodium初始化失败");
        return;
    }
    
    // 长度为16字节的nonce
    unsigned char nonce[crypto_stream_aes256estream_NONCEBYTES];
    randombytes_buf(nonce, sizeof(nonce));
    
    // 长度为32字节的密钥
    unsigned char key[crypto_stream_aes256estream_KEYBYTES];
    randombytes_buf(key, sizeof(key));
    
    // 要加密的字符串
    NSString *inputString = @"hello world";
    
    // 使用AES-256-ESTREAM密钥流对字符串进行加密
    NSData *encryptedData = encryptStringWithAES256EStream(inputString, nonce, key);

    // 打印加密后的数据
    NSLog(@"加密后的数据:%@", encryptedData);
    
  }


http://www.niftyadmin.cn/n/5475881.html

相关文章

4.6java学习总结

内部类(补充) 局部内部类 如果在外界定义会检测不到你所定义的局部内部类,直接报错. public class Main {public static void print(int age,int b){class person{int age;public person(int age) {this.age age;}void show(){System.out.println("内部类输出: "a…

基于RTThread的学习(三):正点原子潘多拉 QSPI 通信 W25Q128 实验

1、基于芯片创建工程 2、QSPI配置 2.1、RTThing_setting 设置组件 2.2、配置board.h 文件 2.3、cubemx生成QSPI的硬件初始化代码&#xff1b;HAL_QSPI_MapInit; 这里注意&#xff1a;你所买的开发板对应的qspi 连接的是否是cubemx 上边显示的&#xff0c;如果不是你需要将引脚…

第32篇:实时时钟

Q&#xff1a;本期我们使用BCD计数器设计实现分--秒--厘秒实时时钟。 A&#xff1a;实时时钟设计&#xff1a;在2个七段数码管上显示分钟&#xff08;从0到59&#xff09;&#xff0c;在2个七段数码管显示秒&#xff08;从0到59&#xff09;&#xff0c;在2个七段数码管上显示…

【论文复现|智能算法改进】融合多策略的黄金正弦灰狼优化算法

目录 1.算法原理2.改进点3.结果展示4.参考文献 1.算法原理 【智能算法】灰狼算法&#xff08;GWO&#xff09;原理及实现 2.改进点 收敛因子非线性调整策略 GWO 算法的探索和开发能力很大程度上依赖于A 的取值, 当|A| >1 时, 灰狼群体通过扩大搜索范 围继续搜寻猎物, 即…

web蓝桥杯真题:图片水印生成

代码及注释&#xff1a; function createWatermark(text, color, deg, opacity, count) {// 创建水印容器const container document.createElement("div");container.className "watermark";// TODO: 根据输入参数创建文字水印for(let i 0; i < cou…

酱油行业市场需求及广阔前景分析

随着居民消费升级&#xff0c;对高品质生活的向往使得酱油市场需求持续增长。消费者对酱油的功能性需求日益细分&#xff0c;追求健康、天然与个性化的产品特性&#xff0c;从而推动了市场的多元化进步。 同时&#xff0c;餐饮业的蓬勃发展以及外卖市场的扩大&#xff0c;均为酱…

Notion 开源替代品 AFFINE 部署和使用教程

AFFiNE 是一款完全开源的 Notion Miro 替代品&#xff0c;与 Notion 相比&#xff0c;AFFiNE 更注重隐私安全&#xff0c;优先将笔记内容保存到本地。 GitHub 地址&#xff1a;https://github.com/toeverything/AFFiNE AFFiNE 使用 Rust 和 Typescript 构建&#xff0c;只需…

【华为OD机试】游戏分组、王者荣耀【C卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述 部门准备举办一场王者荣耀表演赛,有 10 名游戏爱好者参与, 分 5 为两队,每队 5 人。 每位参与者都有一个评分,代表着他的游戏水平。 为了表演赛尽可能精彩,我们需要把 10 名参赛者分为…