其实 系统SecGenerateKeyPair生成 或者openssl生成 密钥对 是一样的 用硬件 也就是 Mac系统生成.der和p12文件 植入到代码中 这样生成的密钥对也是用的openssl 和用代码生成的是一样的 而且每次生成密钥对 都是随机的串码 每次都不同 所以主要问题在于存储 因为我们的touchID等登录 需要用同一对密钥对 公钥在注册touchID的时候发给了server 密钥要在将来用touchID登录的时候用来签名 所以问题在于 如何存储这个密钥
SecGenerateKeyPair生成 可以使用Secure Enclave存储到keychain
但是这样存储 TouchID的逻辑整个就变化 不再是点击按钮 弹出TouchID 而是 点击按钮 从keychain去获取密钥 这时候 因为密钥存储是被TouchID保护的 会自动弹出TouchID 验证通过才能拿到密钥 然后做签名验证 包括对TouchID的验证都可以设置
这种情况 失败三次五次的逻辑没法控制 弹出的信息也没法控制 点击按钮获取密钥 弹出touchID验证 三次后 消失 再次点击 再弹出 验证两次 这时候 会弹出锁屏框 再次点击还能继续验证touchID 也就是五次的时候 没法停止了
使用SecureEnclave生成密钥对并存储 将公钥交给server 然后用密钥来签名 看似拿到了密钥 其实并没有 只是一个引用
经过测试 发现 好像生成的时候 不需要验证touchID 拿出来用的时候 需要验证touchID
苹果在这边举了个简单例子,如何利用Touch ID进行登录。客户端生成一对公私钥,公钥发给服务器,客户端在通过Touch ID校验后,加密一段内容(私钥签名操作),将内容和结果发送给服务器,服务器取出公钥进行验签。如果一致,则通过验证。
之前openssl生成的 存储在keychain 没有保护 越狱就能拿到
系统生成 通过Secure Enclave存储在keychain 被TouchID保护 需要验证TouchID才能拿到
经过测试 发现 只要带上kSecAttrTokenIDSecureEnclave 就不能生成 密钥对 应该是必须配合TouchID才能生成吧
或者尝试在真机测试吧 也就是说 如果想存储密钥到SecureEnclave 就必须带上TouchID保护
切换到keypair 能否实现同样的 处理三次五次的逻辑 这个逻辑应该是可以实现的 但是不能确定
能否生成和openssl同样的 公钥密钥 不影响使用的 当然可以
现在简单的方法是 加混淆 加密后再存储 例如 将密钥分成三段 以不同的名字存起来 混淆 给密钥中 比如所有字母a后添加字母b 类似这样的混淆 然后可以做RSA Base64加密在存储
目前来看 Secure Enclav 生成的公钥 发送给server 私钥存储 受TouchID保护 取出密钥的时候 需要验证TouchID 并且取出的只是一种映射 不是真正的密钥 这里的TouchID我应该是不能控制的 但是三次五次的逻辑应该可以保证 验证三次 消失 再点击按钮 继续验证两次 消失 再点击 没反应 所以 建议是在三次的时候 就直接修改页面 改到用账户密码登录 message应该是不能控制 而且只能生成256位的ECC算法的密钥对
kSecAttrKeyTypeEC ECC
kSecAttrKeyTypeECSECPrimeRandom ECC椭圆曲线 椭圆曲线是iOS10之后的 包括一些新的方法 都是iOS10之后 具体看官方demo
都是256位
还有类似 指纹有变更 这种情况 现在能不能拿到 也不清楚 只是知道 可以设置成 指纹有变更 就密钥取不出来 这个是可以设置的 但是能不能具体拿到 是因为这个原因 然后对应的给message 这个还不确定
麻烦的点很多
注意在产生新的公私钥对前需要调用 deleteKeyAsync ,如果私钥已经存在调用 SecKeyGeneratePair 是会产生新的公私钥对,但是keychian中已经存在的私钥是不会被替换的
其实还有一个想法 就是还是用openssl生成RSA的密钥对 然后 通过keychain存储起来 但是受touchID保护 拿出来的时候 需要验证touchID 也就是逻辑差不多 但是不用Enclav去存储 也就不用生成ECC密钥 而且这种方法 应该可以控制touchID的回调 应该可以控制 。。。 暂时了解这些
但是越狱后的手机可以破解TouchID 那么这个逻辑也并不安全
现在最大的问题是 怎么能配合TouchID使用 也就是获取密钥的时候 弹出的TouchID验证 怎么监听他的回调 从而实现 三次五次message 且刷新页面的需求
而且记得要加版本判断 iOS版本>=11.3的时候 就是kSecAccessControlBiometryAny 因为包含了FaceID 而不是
kSecAccessControlTouchIDAny
在Apple《Certificate, Key, and Trust Services Programming Guide》一文中提到:将私钥保存在钥匙串(keychain)中,仍然有遭到破坏的风险。Apple建议把私钥存储在Secure Enclave中,并且只能存储256位的椭圆曲线私钥。
经过一天的研究 又得出一个小结论 ECC算法密钥 只可以用来签名 不能用来加密 tnnd费劲尝试加密了一整天 因为它是专用的 DSA 算法 它比 RSA 要快很多另外 更小的密钥也带来了更小的计算量 这些特性对于减少 Server 负担非常重要
但是iOS10之后推出的SecKeyCreateEncryptedData 这个方法 经过测试又好像是可以用ECC来加密 丈二和尚摸不着头脑(挠头???)
有没有大神解答一下。。。
好吧 经过研究发现 iOS10之后的加密是通过 使用ECC的密钥进行AES的加密 所以其实ECDSA就是不能加密呗?
iOS10之后的加密 比如说 用kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM来加密
看了他的头文件解释 好像iOS10之后的这个加密 是通过AES的加密 那么这应该就在iOS10之前没法解决了 因为苹果是可以拿到这个密钥的具体值的 但是存在Secure Enclave中 我们是拿不到的 只能拿到一个引用 想做AES加密 天方夜谭
iOS10之后 的密钥交换方法 其实是 把ECC的密钥和公钥 做两套 然后通过椭圆曲线密钥交换算法 做成一个新的 密钥 用来做AES的加密
但是iOS10之前 怎么办呢 有替代SecKeyCopyKeyExchangeResult方法的吗 这个方法是iOS10之后的啊
那么iOS10之前的kSecAttrKeyTypeEC密钥 要怎么才能做密钥交换 然后去做AES加密呢
我看到openssl里有ec.h和ecdh.h里面提供了 交换方法
但是这个ECC密钥是openssl生成的 虽然也是ECC密钥 但是就没有Secure Enclave的安全性了啊。。
唉
//哎哎哎 可以不带TouchID的保护??????经过测试 确实可以不带 TouchID保护 只要记得 每次生成前 把旧的密钥删除掉就OK
//可以实现 不带TouchID保护的ECC密钥存储 那么 逻辑好多了 还是使用之前的自己控制TouchID 验证通过后 去拿出密钥给数据签名 传给Server Server验证签名 通过就API成功 登录成功 这样就能控制失败三次五次的逻辑了 前提是Server支持256位ECC
经过测试2048位的 RSA密钥 密钥的模数大小是256字节 其实就是签名后块长度256字节 也就是说 最后输送给OpenAM的签名后数据是256字节的
那么 256位的ECC密钥 模数大小为32字节 给32位的数据签名 还是32字节???这不对吧 之前写死的256字节 或者 128都能签名成功 32好像不成功啊
看到那几个网上的例子里 都是128???但是要签名的数据 是12 16???
//RSA的时候 可以直接这样去获取输出数据 和 最大字节数 获取到的密钥的字节数 RSA做PKCS1填充的时候 填充数据字节数 小于等于这个字节数-11就好 例如2046位的RSA密钥 是256字节 那么32字节就完全可以 64也没问题 128都行
// size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);
// uint8_t* signedHashBytes = malloc(signedHashBytesSize);
// 支持的RSA 填充方式有三种:NOPadding,PKCS1,OAEP 三种方式 ,填充方式影响最大分组加密数据块的大小
// 签名使用的填充方式PKCS1, 支持的签名算法有 sha1,sha256,sha224,sha384,sha512
// PKCS1 填充方式最大数据为 SecKeyGetBlockSize大小 减去11
//RSA的签名中 要求是填充数据的大小是密钥长度-11 但是发现ECC就算按照密钥长度直接填充32字节的 也没问题 可以成功加签 好像ECC对填充数据大小没要求
//签名没要求 RSA的签名好像也没有字节数的要求 RSA的加密是有这样的要求
//网上查到的资料 有用12字节的 有16字节 有20字节的 都比密钥字节的32少超过11 但是测试用32字节都可以 只是输出的字节数 经过测试 设置成128或者256 输出都是70字节的
//所以目前和后台沟通的时候 可以沟通为 填充数据32字节 输出数据70字节 应该可以成功验签 如果还是不成功 那可能就是ECC和RSA的密钥有区别了 那就需要后台同事研究一下ECC。。。
//签名后输出的是DER编码的签名数据,长度并非固定,但是是70到72字节,之所以会这样是因为,签名输出实际是两个32字节的大整数(r和s),在转换成byte[]的时候,如果为负数,那么会添加前导位0,如果当r和s都是正数,那么就是64个字节,都是负数,就是66字节。而DER编码还会包含类型以及长度字段,因此总长度就会到70到72字节
Demo中 有四种生成方式 与 不同的加密解密 签名验签 并且加了注释 还有疑问看Demo
有个想法 越狱设备可以装支付宝吗?支付宝如果是通过TouchID登录的 那不就可以绕过TouchID直接登录成功了?转账需要验证TouchID 也可以跳过 然后转账成功???支付宝是怎么处理的这部分?
1.16更新 生成的公钥 经过base64传递给后台 是65位的数据 但是后台说只能处理32位的公钥 生成ECC的时候 确实公钥和私钥都是256字节的 但是经过一个存取 公钥存起来再取出来 就变成65位了 不知道是经过了什么算法或者添加字符 如果确实需要32位的 可以在公钥刚生成的时候 通过[NSData dataWithBytes:(SecKeyRef)publicKey length:32];去生成32位的公钥数据 给server
最后的解决办法是 server处理了65位的公钥 也就是这套逻辑是没问题的 65位的公钥也没有问题
参考资料 链接
https://www.jianshu.com/p/5ba276c6cd87
https://panxianyue.github.io/2016/01/12/RSA-SHA/
https://www.cnblogs.com/junhuawang/p/8194484.html
https://blog.csdn.net/idivines/article/details/51911449
https://www.jianshu.com/p/4a0bec497ebd?utm_source=oschina-app
http://newdocx.appcan.cn/plugin-API/system/uexKeyChain
https://blog.csdn.net/Timeless_recall/article/details/80773309
https://onevcat.com/2018/12/jose-3/