美文网首页
iOS逆向开发:密码学 RSA

iOS逆向开发:密码学 RSA

作者: 秃头少女_ | 来源:发表于2021-10-13 21:51 被阅读0次

    1. 密码学发展简介

    密码学是指研究信息加密,破解密码的技术科学。密码学的起源可追溯到2000年前。而密码学是以数字为基础的。

    发展历史

    • 密码学的历史大致可以追溯到两千年前,相传古罗马名将凯撒大帝为了防止敌方截获情报,用密码传送情报。凯撒的做法很简单,就是对二十几个罗马字母建立一张对应表。这样,如果不知道密码本,即使截获一段信息也看不懂。


      image.png
    • 从凯撒大帝时代到上世纪70年代这段很长的时间里,密码学的发展非常的缓慢,因为设计者基本上靠经验。没有运用数学原理

    • 在1976年以前,所有的加密方法都是同一种模式:加密、解密使用同一种算法。在交互数据的时候,彼此通信的双方就必须将规则告诉对方,否则没法解密。那么加密和解密的规则(简称密钥),它保护就显得尤其重要。传递密钥就成为了最大的隐患。这种加密方式被成为对称加密算法(symmetric encryption algorithm)

    • 1976年,两位美国计算机学家 迪菲(W.Diffie)、赫尔曼(M.Hellman) 提出了一种崭新构思,可以在不直接传递密钥的情况下,完成密钥交换。这被称为“迪菲赫尔曼密钥交换”算法。开创了密码学研究的新方向


      image.png

    2. 非对称加密RSA产生过程

    • 上世纪70年代产生的一种加密算法。其加密方式比较特殊,需要两个密钥:公开密钥简称公钥(publickey)和私有密钥简称私钥(privatekey)。公钥加密,私钥解密;私钥加密,公钥解密。这个加密算法就是伟大的RSA
    • 这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长 RSA密钥是 768 个二进制位。也就是说,长度超过 768 位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024 位的 RSA 密钥基本安全,2048 位的密钥极其安全。
      ( 当然 RSA 的缺陷也很容易想到 : 效率相对较低 , 字节长度限制等 . 因此实际应用中我们往往会结合对称性加密一起使用 , 关键内容使用 RSA )

    3. RSA 数学原理

    3.1 离散对数问题

    3.1.1 原根

    • 先从一个问题开始:三的多少次方模 17 等于 12?


      image.png
    • 显然 , 对于离散对数问题 , 其正向计算得到右边 12 很简单. 但是反向运算时 , 就无从下手. 只能穷举 .
    • 而且当模数使用质数 17 时 , 结果固定在 1 ~ 17 之间. 而当 17 这个模数足够大时 , 就算知道采用的是这个算法 , 也知道 17 这个质数和答案 , 想要再计算出来上图中这个问号值 , 可以想象到其难度和计算量有多大 .
    • 如下图:


      image.png
    • 从上图我们可以看出,3的N次方取模17的结果是范围:从1到16的任意一个数字。这样3称为17的原根。
    • 这样我们得出一个规律用来加密,3 的 N次方,也就是上面图的


      image.png

      的问号,我们可以用作N(公式中的?号)作为明文,得到密文 12 。这样即使黑客得到我们在网络中传输的密文12 ,就是他知道这个公式,他也很难反算出我们的明文 N。特别是我们把被模数17改的更大一些,如改为几百位的数字,那么黑客基本上是不可能通过这个公式反算出我们的明文的。他只能通过不断试错的暴力破解方式。

    • 通过上面这个公式反算,计算出明文N的问题叫做离散对数问题

    3.2 欧拉函数Φ

    先了解一些概念

    • 关于互质关系:如果两个正整数,除了1以外,没有其他公因数,我们就称这两个数是互质关系(coprime)。

    如果一个数N是质数,那么小于N的数都会与N 这个数字互为质数。如N=5,那么1,2,3,4都与5构成互质关系,那么 Φ(5) = 4,表示有4个数与5构成互质关系。

    • 任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?计算这个值的方式叫做欧拉函数,使用:Φ(n)表示
    1. 如: 计算8的欧拉函数,和8互质的 1、2、3、4、5、6、7、8,Φ(8) = 4
    2. 计算7的欧拉函数,和7互质的 1、2、3、4、5、6、7,Φ(7) = 6
    3. 计算56的欧拉函数,Φ(56) = Φ(8) * Φ(7) = 4 * 6 = 24

    通过上面的一些推理,我们不难发现欧拉函数的特点:

    • 欧拉函数特点
    1. 当n是质数的时候,Φ(n)=n-1。
    2. 如果n可以分解成两个互质的整数之积,如n=AB则:Φ(AB)=Φ(A)*Φ(B)
    3. 根据以上两点得到:如果N是两个质数P1 和 P2的乘积则: Φ(N)=Φ(P1)Φ(P2)=(P1-1)(P2-1)

    例如 15 = 3 * 5 ,Φ(53)=Φ(5)Φ(3) , 而 Φ(5) = 4,Φ(3) = 2, 则Φ(53)=Φ(5)Φ(3) = 4 * 2 = 8, 也就是15有8个数与它构成互质关系。

    3.3 欧拉定理

    • 欧拉定理:如果两个正整数m和n互质,那么m的Φ(n)次方减去1,可以被n整除。(m^Φ(n)-1)/n = K(整数)
      image.png
    • 费马小定理:欧拉定理的特殊情况:如果两个正整数m和n互质,而且n为质数!那么Φ(n)结果就是n-1。(m^(n-1)-1)/n = K(整数)
      image.png

    3.4 公式转换

    模反元素:如果两个正整数e和x互质,那么一定可以找到整数d,使得 ed-1 被x整除。那么d就是e对于x的“模反元素”


    image.png

    如上图所示,转换过程5步即可:

    1. 首先根据欧拉定理


      image.png
    2. 由于 1 的 k 次方恒等于 1 , 那么


      image.png
    3. 由于 1*m ≡ m , 那么


      image.png
    4. 用模反元素转换,那么换算成公式 就是:


      image.png
    5. 转换一下写法


      image.png
    6. 比较第五步和第三步中红框部分. 也就是说当 x 等于 Φ(n) 时 :


      image.png

    d 是 e 相对于 φ(n) 的模反元素
    注意 : 公式推导第一步时 我们欧拉定理的前提是 m 和 n 互质 , 但是由于模反元素的关系 , 其实只要满足 m < n 上述结果依然成立.

    如果上面的这个公式可以拆分为两次,就可以用来加密。

    • 我们可以在终端使用python来验证一下:

    M = 4, N = 15, φ(n) = 8, e = 3,
    d ? 3d -1 = 8
    d = (8k+1)/3 -> ( 3, 11)
    这里我们可以取d = 11


    image.png

    上面验证知道,m,n不一定要,只需要m < n即可。

    image.png

    从终端打印结构可以看出:n = 15 只要 m < n 也就是 m <= 14 无论是否是质数,公式:


    image.png

    都成立。

    • 然而科学家们一直停留在这个公式阶段,直到迪菲赫尔曼密钥交换出现,通过拆分这个公式实现。

    3.5 迪菲赫尔曼密钥交换

    • 实际场景来看下迪菲赫尔曼密钥交换过程如下图:


      image.png
    • 客户端先选一个随机数13 ,这个数除了客户端知道,没有其他任何人知道。
    • 服务器选一个随机数15, 这个数字除了服务器端,没有任何知道。
    • 这两个数字13,15分别只有客户端和服务器自己知道,不会在网络上传输,所以不会被泄密。
    • 客户端用3作为根原, 3 的13次方 然后取模 17 (3^13mod 17 = 12),得到12,发给服务器端。
    • 服务器端拿到12后,先将12保存起来,服务器端用同客户端一样的算法(3^15mod17 = 6),得到数字6,发给客户端。
    • 这样客户端和服务器端就完成了彼此的密钥交换。
    • 然后客户端和服务器分别做如下一次运算:
    • 客户端拿到服务器发过来的数字6,用同样的算法,(6^13mod17 = 10), 服务器端用从客户端拿到的数字12,用同样的算法(12^ 15mod 17 = 10)同样也是得到10,这个10 就是客户端和服务器交换的秘钥。
    • 这样网络上从来就没有传输过秘钥10,而客户端和服务器却通过同样的算法,计算两次就得到了密钥。

    3.5.1 数学原理

    • 上面讲解的迪菲赫尔曼密钥交换的数学原理如下图:


      image.png
    • 实际上客户端和服务器都做了两次运算,
    • 客户端的两次运算:
    1. 第一次是服务器端做的运算:3^15mod 17 = 6
    2. 第二次是客户端自己拿到服务器端的6继续做的一次运算:6^13 mod17 = 10
    3. 第二次运算的6 用第一次的315替换就实际上得到:315^13 mod17 = 10
    • 服务器端的两次运算:
    1. 第一次是在客户端做的运算:3^13mod17 = 12
    2. 第二次是拿到客户端的12继续做一次运算:12^15mod17 = 10
    3. 第二次运算的12实际上是用313代替:313^15 mod 17 = 10
    • 这样我们可以清楚的看到:客户端(31513 mod17 = 10)= 服务器(31315 mod 17 = 10)
    • 那我们把上面的计算过程总结出来就是如下的公式:


      image.png

    上面的计算套用公式:
    如上面服务器端的计算: m=3, e=13, n=17, C=12 (运算公式:3^13mod17 = 12)
    实际上就是:m^e mod n = C
    然后由于d = 15, (运算公式:12^15 mod 17 = 10)
    实际上就是: C^d mod n = m ,由于 C = m ^ e mod n,可以得到 m ^ e ^ d mod n = m, 也就是:m ^ (ed) mod n = m
    实际上就是对ed 进行了拆分,拆分成了两次运算。

    • 结合我们刚刚第五步之后得出的


      image.png
    • 拆分公式,可以用来加密,解密还原数据:


      image.png

    其中 d 是 e 相对于 φ(n) 的模反元素 , 因为 x = Φ(n) , 那么同样 , e 和 φ(n) 是互质关系

    • 举例验证:例如: m = 3 , n = 15 , φ(n) = 8 , e = 3 , d = 11
      通过终端python3验证:
    image.png
    • 总结如图:


      image.png

    3.7 RSA的诞生

    • 由上面的迪菲赫尔曼密钥交换 和我们得出的公式:m ^ (e*d) mod n = m ,两者结合换算,可以得到加密和解密的公式:
    1. 加密: m ^ e mod n = c, (c 加密的结果,m是明文, e和n就是公钥,d和n就是私钥)
    2. 解密:c ^ d mod n = m
    • 公式换算如下图:


      image.png
    1. n 会非常大,长度一般为 1024 个二进制位。(目前人类已经分解的最大整数,232 个十进制位,768 个二进制位)
    2. 由于需要求出 φ(n),所以根据欧函数特点,最简单的方式 n 由两个质数相乘得到: 质数:p1、p2 . 那么
      Φ(n) = (p1 -1) * (p2 - 1)
    3. 最终由 φ(n) 得到 e 和 d 。
      总共生成 6 个数字:p1、p2、n、φ(n)、e、d
      其中 n 和 e 组成公钥 .
      n 和 d 组成私钥 .
      m 为明文 .
      c为密文 .
    4. 除了公钥用到了 n 和 e 其余的 4 个数字是不公开的。

    3.8 RSA算法

    • 只要满足d是e相对于Φ(n)的模反元素

    • m小于n


      image.png
    • 下面我们通过python来验证一下:

    m ^ e mod n = c 加密
    c ^ d mod n = m 解密
    我们假设 n = 15 则 φ(n) = φ(15) = 8,
    假设 e = 3
    假设 d= 19
    假设明文 m = 7
    先来计算出加密:c = 7 ^ 3 mod 15 = 13
    然后解密:13 ^ 19 mod 15 = 7

    image.png
    • RSA算法的特点:
    1. 总共生成 6 个数字:p1、p2、n、φ(n)、e、d
      其中 n 和 e 组成公钥 .
      n 和 d 组成私钥 .
      m 为明文 .
      c为密文 .
    2. 除了公钥用到了 n 和 e 其余的 4 个数字是不公开的。
    3. 黑客要破解实际上就是根据n, 去求φ(n), 而当n比较大时,是很难算出φ(n),φ(n)只能通过试错的方式去暴力破解(用因式分解方式)。
    4. 要求出φ(n) 目前最大只能计算到232个十进制位,只是运算时间的问题,如果量子计算机真的出来了,因为量子计算理论上运算量是无穷大的,所以可以破解这个φ(n),由于银行等很多大公司都是用的RSA加密方式,所以量子计算的问世,将会对密码学产生很大的影响。

    3.9 终端演练RSA加密算法

    • Mac的终端可以直接使用OpenSSL进行RSA的命令运行。


      image.png
    • OpenSSL使用RSA

    1. 生成RSA私钥,秘钥长度为1024bit
      终端输入命令:openssl genrsa -out private.pem 1024
      image.png
    2. 从私钥中提取公钥
      终端输入命令:openssl rsa -in private.pem -pubout -out public.pem
      image.png
    3. 通过上面两步分别已经生成了公钥,私钥文件


      image.png
    4. 我们查看一下生成的公钥,私钥是什么东东


      image.png
    5. 查看一下公钥内容:


      image.png
    6. 实际上公钥,私钥都是经过base64加密的,我们接下来将私钥转换成明文查看:
      终端输入命令:openssl rsa -in private.pem -text -out private.txt
      image.png
    7. 我们查看一下私钥的明文:
      终端输入:cat private.txt


      image.png
    3.9.1 openssl实现rsa加密 ,解密
    • 打开终端,新建一个message.txt文件:vi message.txt
    • 输入hello,保存


      image.png
    • 通过公钥进行加密:终端输入:
    openssl rsautl -encrypt -in message.txt -inkey public.pem -pubin -out enc.txt
    
    image.png

    加密后的内容hello变成了乱码了。

    • 通过私钥进行解密,终端输入:
    penssl rsautl -decrypt -in enc.txt -inkey private.pem -out dec.txt
    
    image.png

    解密后在dec.txt输出了原来的明文hello

    • 此外我们还可以用私钥进行加密,公钥进行解密。
    • 私钥通过sign进行私钥加密
    • 终端输入命令:
    penssl rsautl -sign -in message.txt -inkey private.pem -out enc.bin
    
    image.png
    • 然后我们用公钥进行解密,终端输入:
    openssl rsautl -verify -in enc.bin -inkey public.pem -pubin -out dec.txt
    

    解密到dec.txt ,我们可以看到解密后的明文也还原了hello


    image.png
    3.9.2 openssl 提取证书p12文件
    • rsa 由于效率不高,不太适合大的数据加密,一般用来加密关键数据,如交换秘钥用rsa加密,rsa也经常用于加密hash值,也就是我们所说的签名。
    • 在代码里面加密我们一般不会直接使用pem文件,一般要提前证书文件
    • 在终端输入:
    openssl req -new -key private.pem -out rsacert.csr
    

    会生成一个.csr文件
    其中按提示输入一些信息,如邮箱,密码等


    image.png
    • csr文件实际上会去请求一个证书文件,向证书颁发机构颁发一个证书。
    • 颁发证书终端命令:
    openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
    
    image.png
    • 这样我们就得到了颁发的证书rsacert.crt文件:


      image.png
    • 这个颁发(官方认证,证书结构盖章的)的证书是要收费的,机构一般要收5千元一年,上面我们写的有效期是10年,意味着要交5万元,o my gad.

    • 这个证书我们不会直接使用,还需要提前

    • 终端输入命令:

    openssl x509 -outform der -in rsacert.crt -out rsacert.der
    
    image.png
    • 提取到文件rsacert.der


      image.png
    • 这个文件主要包含公钥和一些必要信息,后面我们就通过这个der生成一个p12文件,
    • p12文件实际上就包含公钥和私钥。
    • 接下来,我们到处p12文件。
    • 终端输入命令
    openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
    
    • 这个时候会提示我们输入密码,如下图:


      image.png
    • 输入密码后(需要确认两次密码)


      image.png
    • 这样我们就提前到了p12文件


      image.png
    • 实际上我们就可以用p.p12 和 rsacert.der进行加密和解密


      image.png
    3.9.3 终端base64编码
    • 我们在rsa文件夹下面有一张kyl.jpg图片,现在通过终端进行base64编码
    image.png
    • 先终端cd 到rsa这个目录


      image.png
    • 终端输入编码命令:

    base64 kyl.jpg -o pic.txt
    
    image.png
    • 现在我们可以用终端进行base64解码:
    base64 pic.txt -o 123.png -D
    
    image.png

    解码后我们得到123.png图片


    image.png
    3.9.4 base64编码代码实现
    //给一个字符 编码
    -(NSString *)base64Endcode:(NSString *)str{
       NSData * data = [str dataUsingEncoding:NSUTF8StringEncoding];
        return [data base64EncodedStringWithOptions:0];
    }
    
    
    //给一个编码我对其进行解密
    -(NSString *)base64Decode:(NSString *)str{
        NSData * data = [[NSData alloc] initWithBase64EncodedString:str options:0];
        return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }
    
    
    

    4. RSA加密代码实现

    4.1 RSA加密代码下载

    4.2加密代码讲解

    4.2.1 新建一个KRSACryptor单例类

    KRSACryptor.h文件如下:

    //
    //  KRSACryptor.h
    //  001-KylAppEncrypt
    //
    //  Created by 孔雨露 on 2019/12/14.
    //  Copyright © 2019 Apple. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface KRSACryptor : NSObject
    
    + (instancetype)shared;
        
        /**
         *  生成密钥对
         *
         *  @param keySize 密钥尺寸,可选数值(512/1024/2048)
         */
    - (void)generateKeyPair:(NSUInteger)keySize;
        
        /**
         *  加载公钥
         *
         *  @param publicKeyPath 公钥路径
         *
         @code
         # 生成证书
         $ openssl genrsa -out ca.key 1024
         # 创建证书请求
         $ openssl req -new -key ca.key -out rsacert.csr
         # 生成证书并签名
         $ openssl x509 -req -days 3650 -in rsacert.csr -signkey ca.key -out rsacert.crt
         # 转换格式
         $ openssl x509 -outform der -in rsacert.crt -out rsacert.der
         @endcode
         */
    - (void)loadPublicKey:(NSString *)publicKeyPath;
        
        /**
         *  加载私钥
         *
         *  @param privateKeyPath p12文件路径
         *  @param password       p12文件密码
         *
         @code
         openssl pkcs12 -export -out p.p12 -inkey ca.key -in rsacert.crt
         @endcode
         */
    - (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password;
        
        /**
         *  加密数据
         *
         *  @param plainData 明文数据
         *
         *  @return 密文数据
         */
    - (NSData *)encryptData:(NSData *)plainData;
        
        /**
         *  解密数据
         *
         *  @param cipherData 密文数据
         *
         *  @return 明文数据
         */
    - (NSData *)decryptData:(NSData *)cipherData;
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    KRSACryptor.m文件如下:

    //
    //  KRSACryptor.m
    //  001-KylAppEncrypt
    //
    //  Created by 孔雨露 on 2019/12/14.
    //  Copyright © 2019 Apple. All rights reserved.
    //
    
    #import "KRSACryptor.h"
    
    // 填充模式
    #define kTypeOfWrapPadding        kSecPaddingPKCS1
    
    // 公钥/私钥标签
    #define kPublicKeyTag            "com.logic.EncryptDemo.publickey"
    #define kPrivateKeyTag            "com.logic.EncryptDemo.privatekey"
    
    static const uint8_t publicKeyIdentifier[]        = kPublicKeyTag;
    static const uint8_t privateKeyIdentifier[]        = kPrivateKeyTag;
    
    @interface KRSACryptor() {
        SecKeyRef publicKeyRef;                             // 公钥引用
        SecKeyRef privateKeyRef;                            // 私钥引用
    }
        
        @property (nonatomic, retain) NSData *publicTag;        // 公钥标签
        @property (nonatomic, retain) NSData *privateTag;       // 私钥标签
        
        @end
    
    @implementation KRSACryptor
    
        
    + (instancetype)shared {
        static id instance;
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[self alloc] init];
        });
        return instance;
    }
        
    - (instancetype)init {
        self = [super init];
        if (self) {
            // 查询密钥的标签
            _privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
            _publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
        }
        return self;
    }
        
    #pragma mark - 加密 & 解密数据
    - (NSData *)encryptData:(NSData *)plainData {
        OSStatus sanityCheck = noErr;
        size_t cipherBufferSize = 0;
        size_t keyBufferSize = 0;
        
        NSAssert(plainData != nil, @"明文数据为空");
        NSAssert(publicKeyRef != nil, @"公钥为空");
        
        NSData *cipher = nil;
        uint8_t *cipherBuffer = NULL;
        
        // 计算缓冲区大小
        cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
        keyBufferSize = [plainData length];
        
        if (kTypeOfWrapPadding == kSecPaddingNone) {
            NSAssert(keyBufferSize <= cipherBufferSize, @"加密内容太大");
        } else {
            NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密内容太大");
        }
        
        // 分配缓冲区
        cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
        memset((void *)cipherBuffer, 0x0, cipherBufferSize);
        
        // 使用公钥加密
        sanityCheck = SecKeyEncrypt(publicKeyRef,
                                    kTypeOfWrapPadding,
                                    (const uint8_t *)[plainData bytes],
                                    keyBufferSize,
                                    cipherBuffer,
                                    &cipherBufferSize
                                    );
        
        NSAssert(sanityCheck == noErr, @"加密错误,OSStatus == %d", sanityCheck);
        
        // 生成密文数据
        cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
        
        if (cipherBuffer) free(cipherBuffer);
        
        return cipher;
    }
        
    - (NSData *)decryptData:(NSData *)cipherData {
        OSStatus sanityCheck = noErr;
        size_t cipherBufferSize = 0;
        size_t keyBufferSize = 0;
        
        NSData *key = nil;
        uint8_t *keyBuffer = NULL;
        
        SecKeyRef privateKey = NULL;
        
        privateKey = [self getPrivateKeyRef];
        NSAssert(privateKey != NULL, @"私钥不存在");
        
        // 计算缓冲区大小
        cipherBufferSize = SecKeyGetBlockSize(privateKey);
        keyBufferSize = [cipherData length];
        
        NSAssert(keyBufferSize <= cipherBufferSize, @"解密内容太大");
        
        // 分配缓冲区
        keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
        memset((void *)keyBuffer, 0x0, keyBufferSize);
        
        // 使用私钥解密
        sanityCheck = SecKeyDecrypt(privateKey,
                                    kTypeOfWrapPadding,
                                    (const uint8_t *)[cipherData bytes],
                                    cipherBufferSize,
                                    keyBuffer,
                                    &keyBufferSize
                                    );
        
        NSAssert1(sanityCheck == noErr, @"解密错误,OSStatus == %d", sanityCheck);
        
        // 生成明文数据
        key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
        
        if (keyBuffer) free(keyBuffer);
        
        return key;
    }
        
    #pragma mark - 密钥处理
        /**
         *  生成密钥对
         */
    - (void)generateKeyPair:(NSUInteger)keySize {
        OSStatus sanityCheck = noErr;
        publicKeyRef = NULL;
        privateKeyRef = NULL;
        
        NSAssert1((keySize == 512 || keySize == 1024 || keySize == 2048), @"密钥尺寸无效 %tu", keySize);
        
        // 删除当前密钥对
        [self deleteAsymmetricKeys];
        
        // 容器字典
        NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
        NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
        NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
        
        // 设置密钥对的顶级字典
        [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
        [keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];
        
        // 设置私钥字典
        [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
        [privateKeyAttr setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
        
        // 设置公钥字典
        [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
        [publicKeyAttr setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
        
        // 设置顶级字典属性
        [keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
        [keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
        
        // SecKeyGeneratePair 返回密钥对引用
        sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
        NSAssert((sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL), @"生成密钥对失败");
    }
        
        /**
         *  加载公钥
         */
    - (void)loadPublicKey:(NSString *)publicKeyPath {
        
        NSAssert(publicKeyPath.length != 0, @"公钥路径为空");
        
        // 删除当前公钥
        if (publicKeyRef) CFRelease(publicKeyRef);
        
        // 从一个 DER 表示的证书创建一个证书对象
        NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
        SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
        NSAssert(certificateRef != NULL, @"公钥文件错误");
        
        // 返回一个默认 X509 策略的公钥对象,使用之后需要调用 CFRelease 释放
        SecPolicyRef policyRef = SecPolicyCreateBasicX509();
        // 包含信任管理信息的结构体
        SecTrustRef trustRef;
        
        // 基于证书和策略创建一个信任管理对象
        OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
        NSAssert(status == errSecSuccess, @"创建信任管理对象失败");
        
        // 信任结果
        SecTrustResultType trustResult;
        // 评估指定证书和策略的信任管理是否有效
        status = SecTrustEvaluate(trustRef, &trustResult);
        NSAssert(status == errSecSuccess, @"信任评估失败");
        
        // 评估之后返回公钥子证书
        publicKeyRef = SecTrustCopyPublicKey(trustRef);
        NSAssert(publicKeyRef != NULL, @"公钥创建失败");
        
        if (certificateRef) CFRelease(certificateRef);
        if (policyRef) CFRelease(policyRef);
        if (trustRef) CFRelease(trustRef);
    }
        
        /**
         *  加载私钥
         */
    - (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password {
        
        NSAssert(privateKeyPath.length != 0, @"私钥路径为空");
        
        // 删除当前私钥
        if (privateKeyRef) CFRelease(privateKeyRef);
        
        NSData *PKCS12Data = [NSData dataWithContentsOfFile:privateKeyPath];
        CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
        CFStringRef passwordRef = (__bridge CFStringRef)password;
        
        // 从 PKCS #12 证书中提取标示和证书
        SecIdentityRef myIdentity;
        SecTrustRef myTrust;
        const void *keys[] =   {kSecImportExportPassphrase};
        const void *values[] = {passwordRef};
        CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        
        // 返回 PKCS #12 格式数据中的标示和证书
        OSStatus status = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
        
        if (status == noErr) {
            CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
            myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
            myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
        }
        
        if (optionsDictionary) CFRelease(optionsDictionary);
        
        NSAssert(status == noErr, @"提取身份和信任失败");
        
        SecTrustResultType trustResult;
        // 评估指定证书和策略的信任管理是否有效
        status = SecTrustEvaluate(myTrust, &trustResult);
        NSAssert(status == errSecSuccess, @"信任评估失败");
        
        // 提取私钥
        status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef);
        NSAssert(status == errSecSuccess, @"私钥创建失败");
    }
        
        /**
         *  删除非对称密钥
         */
    - (void)deleteAsymmetricKeys {
        OSStatus sanityCheck = noErr;
        NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
        NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
        
        // 设置公钥查询字典
        [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
        [queryPublicKey setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
        [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
        
        // 设置私钥查询字典
        [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
        [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
        [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
        
        // 删除私钥
        sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPrivateKey);
        NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"删除私钥错误,OSStatus == %d", sanityCheck);
        
        // 删除公钥
        sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPublicKey);
        NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"删除公钥错误,OSStatus == %d", sanityCheck);
        
        if (publicKeyRef) CFRelease(publicKeyRef);
        if (privateKeyRef) CFRelease(privateKeyRef);
    }
        
        /**
         *  获得私钥引用
         */
    - (SecKeyRef)getPrivateKeyRef {
        OSStatus sanityCheck = noErr;
        SecKeyRef privateKeyReference = NULL;
        
        if (privateKeyRef == NULL) {
            NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
            
            // 设置私钥查询字典
            [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
            [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
            [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
            [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
            
            // 获得密钥
            sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference);
            
            if (sanityCheck != noErr) {
                privateKeyReference = NULL;
            }
        } else {
            privateKeyReference = privateKeyRef;
        }
        
        return privateKeyReference;
    }
    @end
    
    
    4.2.2 测试验证
    • 工程目录如下:


      image.png
    • 测试代码如下:

    - (void) testRSAEncrpt {
        //1.加载公钥
        [[KRSACryptor shared] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]];
        //2.加载私钥
        [[KRSACryptor shared] loadPrivateKey: [[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"];
    }
    
    static void my_encrypt(){
        NSData * result = [[KRSACryptor shared] encryptData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
        //base64编码
        NSString * base64 = [result base64EncodedStringWithOptions:0];
        NSLog(@"加密之后:%@\n",base64);
        
        //解密
        NSData * dcStr = [[KRSACryptor shared] decryptData:result];
        NSLog(@"%@",[[NSString alloc] initWithData:dcStr encoding:NSUTF8StringEncoding]);
    }
    
    
    • 打印结果如下:


      image.png

    相关文章

      网友评论

          本文标题:iOS逆向开发:密码学 RSA

          本文链接:https://www.haomeiwen.com/subject/ccdrnltx.html