版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.08.01 星期六 |
前言
CryptoKit
是iOS13
的新的SDK,接下来几篇我们就一起看一下这个专题。感兴趣的可以看下面几篇文章。
1. CryptoKit框架详细解析(一) —— 基本概览(一)
开始
首先看下主要内容:
密码学
(Cryptography)
是保护用户数据的关键。 本教程向您展示如何使用Apple
的新CryptoKit API
来验证或加密应用程序的数据,内容来自翻译。
接着就是下面的写作环境:
Swift 5, iOS 13, Xcode 11
下面就是正文了
如果您的应用程序需要的内容超过Apple
的默认硬件和软件保护,则Apple的新CryptoKit API使您能够对应用程序发送和接收的数据进行身份验证和加密。
在本文中,您将使用对称密钥(symmetric key)
和公用密钥加密技术(public key cryptography)
对数据进行身份验证和加密。 您将了解何时以及如何使用:
Cryptographic hashing
Hash-based Message Authentication Code (HMAC)
Authenticated Encryption with Associated Data (AEAD) ciphers
Public key cryptography with elliptic curves (ECC)
Digital signatures
Key agreement
继续阅读以解锁所有秘密!
本教程假定您熟悉J.K撰写的《哈利波特》系列。 罗琳还是没有兴趣阅读它。 它讨论了最近几本书中揭示的秘密。
在启动starter
文件夹中打开,构建和运行SecretKeeper
项目。
这个应用程式列出了伏地魔的魂器 Voldemort’s horcruxes。只要他的灵魂的一部分在这些物品之一中是安全的,伏地魔(Voldemort)
就不会被杀死。因此,他必须对所有人,尤其是阿不思·邓布利多和哈利·波特,保持自己身份和位置的秘密。
Protecting Users’ Data
就像Voldemort
的秘密一样,您的用户数据也需要保护,以防止意外或恶意破坏以及未经授权的使用。 iOS在Foundation / NSData,Security / Keychain
和CloudKit
中已经具有很多内置或易于使用的安全性。如果您的应用访问HTTPS
服务器,则可以免费获得TLS
加密和数字签名。如果您的应用需要验证数据或验证发件人,那么现在有了CryptoKit
。
您需要在以下三种状态的每一种中保护数据:
- Data in motion - 动态数据:传输中,通常通过网络
- Data in use - 正在使用的数据:在内存中
- Data at rest - 静态数据:存储在磁盘上,未使用
传输数据(Data in motion)
是进行加密的最初原因,可以追溯到凯撒密码 Caesar cipher向凯撒将军发送书面命令之前。如今,如果您的应用程序使用TLS 1.3
与服务器通信,则加密由传输层安全性Transport Layer Security(TLS)
处理。但是您可能仍然需要使用加密哈希和键签名对数据和发送方进行身份验证。本教程的大部分内容与这些主题有关。
使用中的数据(Data in use)
必须受到用户身份验证的保护。要限制何时可以执行操作,请使用本地身份验证(Local Authentication )
来检查用户何时以及如何进行身份验证。例如,您可能要求用户使用生物识别技术(Face ID or Touch ID)
进行身份验证,以确保用户在场。
静态数据(Data at rest )
通过Data Protection API
在设备上受到保护。设置设备密码后,用户即可获得自动软件数据保护。每个新文件都会获得一个新的256位加密密钥,该密钥特定于文件的保护类型。共有三种文件保护类型:
- Protected Until First User Authentication - 在首次用户身份验证之前受保护:默认文件保护类型。设备启动时无法访问该文件,但是直到设备重新启动后,即使设备已锁定,也可以访问该文件。
- Complete - 已完成:仅当设备解锁后才能访问该文件。
- Complete Unless Open - 除非打开,否则完成:如果设备锁定时文件已打开,则仍然可以访问。如果设备锁定时文件未打开,则只有在设备解锁后才能访问该文件。
当设备锁定以允许网络传输继续时,您可能需要允许访问文件。
您的应用程序还可以将加密数据存储在私有CloudKit
数据库中,该数据库可在所有用户设备上使用。
1. Increasing Protection for Voldemort’s Secrets
SecretKeeper
从文件读取以创建魂器(horcruxes)
列表。 该应用程序写入此文件时没有文件保护选项:
try data.write(to: secretsURL)
这意味着秘密文件仅具有默认的文件保护,因此在应用程序处于后台甚至锁定状态下都可以访问该文件。
Voldemort
的机密需要更高级别的文件保护。 在ContentView.swift
的writeFile(items :)
中,用以下这一行替换data.write(to :)
语句:
try data.write(to: secretsURL, options: .completeFileProtection)
使用此级别的保护,仅当设备解锁后才能访问该文件。
2. Keychain and Secure Enclave
您的应用程序应在本地或iCloud
钥匙串中存储加密密钥,身份验证令牌和密码(encryption keys, authentication tokens and passwords)
。苹果公司的示例应用程序在钥匙串中存储CryptoKit密钥Storing CryptoKit Keys in the Keychain显示了如何将CryptoKit
密钥类型P256
,P384
和P521
转换为SecKey
,并将其他密钥类型转换为通用密码。
自2013
年底以来发布的iOS设备(iPhone 5S,iPad Air
和更高型号)均采用Secure Enclave芯片。 Secure Enclave
是一个专用的加密引擎,与处理器分开,并在工厂融合了设备的Unique IDentifier (UID)
256
位加密密钥。 Secure Enclave
还存储TouchID
和FaceID
的生物特征数据。
Secure Enclave
中的密钥和数据永不离开。它们永远不会加载到内存或写入磁盘,因此受到了完全保护。您的应用程序通过邮箱与Secure Enclave
通信,您可以在其中存储要加密或解密的数据,然后检索结果。
CryptoKit
使您的应用程序可以直接在Secure Enclave
中创建用于公共密钥加密的密钥。本教程结尾处有示例代码。
Using CryptoKit
所有这些内置的数据保护可能会满足您的应用需求。或者,如果您的应用程序使用第三方加密的数据或验证文件传输或财务交易,则可能需要使用CryptoKit
。
1. Rolling Your Own Crypto: Don’t
数据安全是密码学家和攻击者之间的战场。除非您是密码学家,否则编写自己的密码算法会对用户数据造成巨大风险。有许多针对不同平台的加密框架。现在,您有了Apple的CryptoKit
,可以非常轻松地在应用程序中使用加密。
2. Performance: Don’t Worry
性能如何? CryptoKit
建立在Apple本地加密库corecrypto
的基础上。 Corecrypto
的手动调整汇编代码可以高效利用硬件。
3. Cryptographic Hashing
现在介绍密码学(cryptography)
的最基本形式。您可能熟悉对字典项或数据库记录进行哈希处理以提高检索效率的想法。许多标准类型都符合Hashable协议conform to the Hashable protocol。将这些类型的对象哈希到哈希器Hasher
中会产生具有合理唯一性的整数哈希值。输入的较小更改会导致哈希值发生较大更改。
Hasher
使用随机生成的种子值,因此每次您的应用运行时,它都会产生不同的哈希值。亲自看看这个。
在起始文件夹中打开CryptoKitTutorial.playground
。第一部分,Hashable Protocol
,具有以下hashItem(item :)
函数:
func hashItem(item: String) -> Int {
var hasher = Hasher()
item.hash(into: &hasher)
return hasher.finalize()
}
通过在下面添加以下行来进行尝试:
let hashValue = hashItem(item: "the quick brown fox")
运行playground
。 哈希值显示在结果侧栏中,或单击Show Result
按钮以在playground
上查看它:
您的值与我的不同。再次运行playground
,您将获得不同的值。
与Hasher
不同,加密哈希(cryptographic hashing)
每次都会产生相同的哈希值。像Hasher
算法一样,密码哈希算法(cryptographic hashing algorithm)
会产生几乎唯一的哈希值,而对输入的较小更改会导致哈希值发生较大变化。差异是,即产生可以可靠地验证数据完整性的哈希值(称为摘要或校验和)所需的计算量不同。
密码哈希算法(Cryptographic hashing algorithms)
为输入数据创建一个小的,固定长度的,几乎唯一的数据值(摘要)。最常见的摘要大小是256
位和512
位。 512
位摘要可以截断为384
位。找到两个产生相同摘要的输入是不太可能但并非不可能,因为256
位或512
位摘要分别只有2^256
或2^512
个可能的值。
散列算法(Hashing algorithms)
是单向的,并且包含非线性运算,因此攻击者无法反转运算来从摘要中计算原始数据。输出的每一位都取决于输入的每一位,因此攻击者无法尝试根据摘要的一部分来计算部分输入。即使更改输入中的单个位也会产生完全不同的摘要,因此攻击者无法找到输入和输出之间的关系。
这些特征使您可以通过计算摘要来查看两个数据集是否不同。 例如,Git
计算摘要以标识每个提交。 您可以传输或存储数据及其摘要,以检测随后的更改,例如数据损坏。 软件下载通常提供摘要,称为校验和(checksum)
。 下载器使用相同的哈希算法(hashing algorithm)
为下载的数据计算摘要。 如果此摘要与文件的校验和不匹配,则表明文件已损坏或不完整。
CryptoKit
提供了Secure Hash Algorithm 2 (SHA-2)算法SHA-256
,SHA-384
和SHA-512
。 数字表示摘要大小。 其不安全(Insecure)
的容器提供SHA-1(160
位)和MD5(128
位),以便与较早的服务向后兼容。 SHA-2由美国国家标准技术研究院(U.S. National Institute of Standards and Technology)(NIST)
发布,法律要求在某些美国政府应用中使用。
在playground
上,向下滚动至以下语句:
UIImage(data: data)
在结果侧边栏中,单击w 714 h 900
旁边的Show Result
,以查看数据是一岁的Harry Potter
的图像:
当伏地魔没能杀死哈利时,他不小心把他变成了魂器。 但是伏地魔不知道这一点。 Albus Dumbledore
知道,他想与哈利分享这个秘密。
首先,您作为Dumbledore
创建此数据的摘要。 添加此行:
let digest = SHA256.hash(data: data)
这就是您要做的! SHA256
产生256
位摘要。 使用其他两种哈希算法之一 SHA384
或SHA512
一样容易。 而且,如果Apple将它们更新为使用SHA-3而不是SHA-2
,则您无需更改代码。
然后Dumbledore
通过网络连接将数据和摘要发送给Harry
。 因此,现在,作为Harry
,添加以下代码来检查摘要匹配情况:
let receivedDataDigest = SHA256.hash(data: data)
if digest == receivedDataDigest {
print("Data received == data sent.")
}
您对数据使用相同的哈希算法,然后检查两个摘要值是否相等。
运行playground
。 消息打印。 不足为奇,因为data
没有机会被破坏。
现在,单击receivedDataDigest
旁边的Show Result
按钮以很好地了解摘要:
这是一个元组。 不太可读。
键入以下行以显示其description
:
String(describing: digest)
注意:
Apple
的文档不建议直接调用.description
。
运行playground
。 点击显示结果:
摘要description
包含一个字符串String
。 这更易于理解,看起来像一个Git
哈希值,但长度是它的两倍。 它也比Hasher
哈希值长得多。
最后,要查看数据的微小变化如何产生完全不同的摘要,请对两条相同的代码行之一进行很小的更改。 例如:
String(describing: SHA256.hash(data: "Harry is a horcrux".data(using: .utf8)!))
String(describing: SHA256.hash(data: "Harry's a horcrux".data(using: .utf8)!))
运行playground
。 单击两行的显示结果:
请注意,您得到的哈希值与我完全一样。
4. Signing the Digest
散列数据(Hashing data)
并不能保护您免受恶意更改的侵害,因为恶意攻击者只会为他们更改后的数据发送正确的摘要。
基于哈希的消息身份验证代码(Hash-based Message Authentication Code - HMAC)
通过使用对称加密密钥(symmetric cryptographic key)
对摘要进行签名来防止恶意更改。 一个常见的用例是对文件的摘要签名,以便应用程序的服务器可以检查您是否有权上传文件。
“Symmetric key”
是指发送者和接收者都知道秘密密钥。 HMAC
使用秘密密钥派生内部和外部密钥。 然后,它根据数据和内部键创建内部哈希。 最后,它根据内部哈希和外部密钥创建签名。
这就是算法的作用。 但是,要使用CryptoKit
创建对称密钥(symmetric key)
,只需键入以下行:
let key256 = SymmetricKey(size: .bits256)
与Apple
的旧C
加密API相比,它的工作量要少得多,后者必须转换位和字节,并记得在完成操作后从内存中删除密钥!
对HMAC
的最常见攻击是蛮力发现密钥,因此HMAC
的安全性取决于密钥的大小。 CryptoKit
允许三种密钥大小:.bits128
,.bits192
和.bits256
。
接下来,添加以下两个代码语句:
let sha512MAC = HMAC<SHA512>.authenticationCode(
for: data, using: key256)
String(describing: sha512MAC)
您正在使用对称密钥(symmetric key)
为数据data
创建摘要,因此现在将摘要称为身份验证代码或签名(authentication code or signature)
。
运行playground
。 点击显示结果以查看签名的描述:
您已经为婴儿图片生成了512
位(64字节)签名。
要通过网络发送此签名,请将其转换为Data
:
let authenticationCodeData = Data(sha512MAC)
现在,Dumbledore
将签名的数据发送给您,即哈里。 您作为哈利已经拥有相同的密钥key256
。 输入以下代码以验证您收到的信息:
if HMAC<SHA512>.isValidAuthenticationCode(authenticationCodeData,
authenticating: data, using: key256) {
print("The message authentication code is validating the data: \(data))")
UIImage(data: data)
}
else { print("not valid") }
如果签名有效,您即哈利查看数据,就知道这确实是Dumbledore
发送的。
运行playground
。 单击print
语句旁边和UIImage
旁边的显示结果:
HMAC
可让您验证发件人的身份和数据的完整性,但不对数据进行加密。如果您忘记使用TLS
,而Voldemort
截取了传输信息,他会怀疑Harry
是他的灵魂之一。
或者,Voldemort
可能决定手动加密他的魂器清单,以确保Dumbledore
和Harry
找不到并销毁它们。
数据加密就是密码学的全部内容,因此来了。
5. Encrypting and Authenticating Data
即使使用TLS
加密网络数据,您也可能希望将加密的文件发送给用户。例如,如果他们进行了应用内购买,则可以向他们发送加密文件和(单独)解密文件的密钥。
当前的技术水平是具有关联数据的认证加密(Authenticated Encryption with Associated Data - AEAD)
密码。 AEAD
密码使用不同的密钥进行加密和MAC
,并且MAC
会对密码文本而不是纯文本进行哈希处理。一切都结合到一个密封的盒子里。
有两种常用的AEAD
:Advanced Encryption Standard Galois/Counter Mode (AES-GCM)
和 ChaCha20-Poly1305
。两者都是Apple
加密API的新功能。 AES-GCM
是NIST
建立的标准。 Daniel J. Bernstein
开发了ChaCha20-Poly1305
,以防攻击者设法破坏AES-GCM
。
在2019年,几乎所有TLS
流量都使用AES
,但是ChaCha20-Poly1305
越来越受欢迎,因为在不支持AES-GCM
的移动设备上,它比AES-GCM
快得多。但是请放心在您的应用程序中使用AES-GCM
,因为iOS
设备具有AES
硬件。
AES
和ChaCha20
是密码算法。 GCM
和Poly1305
是MAC
算法。伯恩斯坦(Bernstein)
从他较早的莎莎(Salsa)
密码开发了ChaCha20
。
要创建密封盒,AEAD
密码采用以下输入:
- 1) 要加密的明文
- 2) 秘密钥匙
- 3) 唯一的初始化值 -
IV
或随机数:这可以防止重播攻击,例如某人(确实需要生命)重新发送同一购物车订单多次。 - 4) 可选:已认证但未加密的其他非秘密数据。这是
AEAD
中的AD
。
然后进行密封操作:
- 1) 使用密钥和随机数生成辅助密钥。
- 2) 使用密钥和随机数将数据加密为长度相等的加密数据(密文
ciphertext
)。 - 3) 使用辅助密钥来生成任何其他数据,加密数据及其长度的带密钥摘要。
- 4) 使用密钥和随机数加密密钥摘要,然后将加密的密钥摘要附加到加密的数据。
在此图中,我省略了相关数据:
这看起来需要很多工作,不是吗? 好吧,这里是您要键入的所有内容,以创建一个密封的盒子:
let sealedBoxData = try! ChaChaPoly.seal(data, using: key256).combined
您提供数据和密钥以得到一个密封的盒子。 它的combined
属性是Data
类型,您可以通过网络发送它。
要改用AES-GCM
,您可以将ChaChaPoly
替换为AES.GCM
。
现在,Dumbledore
将密封的盒子数据发送给哈利。 您是哈利,请键入以下代码以将其转回密封盒中:
let sealedBox = try! ChaChaPoly.SealedBox(combined: sealedBoxData)
作为哈利的用户已经具有与邓布利多相同的密钥,因此您可以打开密封盒并解密其密文(ciphertext)
:
let decryptedData = try! ChaChaPoly.open(sealedBox, using: key256)
运行playground
注意SealedBoxData
的大小比图像数据大28
个字节。 添加这些行以查看密封框盒还有什么:
sealedBox.nonce // 12 bytes
sealedBox.tag // 16 bytes
两种算法都会为您选择一个随机数,然后将其打包到密封的盒子中以供使用。 太简单! 16
字节tag
是加密的密钥摘要 - 验证数据的签名。 每次创建密封盒时,随机数都会更改,这也会更改加密的密钥摘要。 因此,您的实际nonce
和tag
值与我的屏幕截图不匹配。
最后,为了证明无法查看加密数据,请添加以下行:
let encryptedData = sealedBox.ciphertext
UIImage(data: encryptedData)
UIImage(data: decryptedData)
运行playground
,查看以下行:
这样就很容易发送加密的数据,但是发送者和接收者都需要知道密钥。如果不可能的话怎么办?
Public-Key Cryptography
注意:这仍然是
CryptoKit
的一部分,但这是一个很大的话题,因此请获取其自己的部分。
HMAC
和密封箱(Sealed Box)
加密使用对称密钥,其中发送者和接收者都知道密钥。对称密钥必须“带外”传输。如果您不能安全地执行此操作,请使用公共密钥加密。实际上,互联网上的大多数常规加密都使用公钥加密,包括每当Xcode
对您的应用进行签名时。
公钥加密(Public-key cryptography)
创建两个数学链接的密钥。您将私钥保密,并发布相应的公钥数据。您使用私钥对数据或其摘要签名,然后将其发送。接收者根据您的公共密钥数据创建一个公共密钥,然后使用它来检查已签名的数据或摘要。
例如,您的应用可能需要通过后端服务器对操作进行身份验证。它在用户的设备上创建一个私钥,并将其存储在Keychain
或SecureEnclave
中,然后在服务器上注册相应的公钥。用户设置操作时,您的应用会使用用户的私钥对操作的详细信息进行签名,然后将签名的详细信息发送到服务器,服务器将使用用户的公钥对其进行验证。
要发送加密的数据,您和您的收件人分别创建一对密钥并发布公共密钥。然后,你们两个都将您的私钥与另一个人的公钥结合在一起,以创建共享密钥。你们两个都使用该共享机密来导出相同的对称密钥,然后可以按照上一节中的说明将其用于AEAD
。
1. Creating Private and Public Keys
公钥密码术的关键特征是它使用活板门算法(trapdoor algorithms
):从公钥中计算私钥非常困难。
在1978
年发布后,RSA(Rivest-Shamir-Adleman)
成为最受欢迎的公钥算法。它依赖于确定一个非常非常大的数字的两个素数因子的难度。其他数学家则面临挑战,开发了只能通过增加键的大小来阻止因素分解的算法。生成RSA
密钥的速度很慢,并且时间随着密钥的大小而增加。最终,保理算法的改进速度快于移动设备进行大量计算的能力。注意力转移到另一种技术上,事实证明,这种技术很难被攻击。
ECC (Elliptic Curve Cryptography)
, 于1985年首次提出,自2004年以来已广泛使用,并在2015年底成为服务器上的首选技术。ECC
密钥比具有类似安全性的RSA
密钥小得多:例如, 256
位ECC
公钥与3072
位RSA
公钥相当。
CryptoKit
仅使用ECC
算法。您可以在此处选择NIST
的P256 / P384 / P521
或Daniel J. Bernstein
的Curve25519
。 P256
是迄今为止使用最广泛的曲线。这是OpenSSL
的默认曲线,称为prime256v1
。 P384
的计算量是P256
的2-3
倍。美国国家安全局(NSA)
要求使用P384
保护绝密信息。 P256
和P384
位于NSA
的Suite B Cryptography
中,但P521
没有,而且我找不到有关谁使用它的任何信息。超级惊吓吧?
注意:在2018年,
NSA
用 Commercial National Security Algorithm Suite(CNSA)
取代了Suite B
,这是其向抗量子密码技术(quantum-resistant cryptography)
过渡的一步,因为量子计算quantum computing可以轻松打破ECC
算法。
伯恩斯坦(Bernstein)
在NSA Suite B
的同一年发布了Curve25519
,但在爱德华·斯诺登(Edward Snowden)
于2013年发布内部备忘录后暗示,NSA
已在基于椭圆曲线的伪随机生成器Dual_EC_DRBG
中插入了密码后门,从而使其流行度提高。 Dual_EC_DRBG
和P256
都依赖于 Nothing Up My Sleeve (NUMS)
幻数。密码学界怀疑,NSA使用特殊值来导出这些幻数,从而使它们能够解密使用P256
加密的任何内容。 Curve25519
不依赖于幻数。它的计算复杂度比P256
低40%
。并且2018年TLS 1.3
标准需要对此提供支持。
2. ECC: How Does It Work?
像这样this one的许多文章都使用小质数算法以易于理解的方式解释RSA
。该算法非常聪明。
椭圆曲线密码学(Elliptic curve cryptography)
甚至更好!但是解释起来有点复杂。
椭圆曲线由满足以下形式的等式的(x,y)
点组成:
y^2 = x^3 + ax + b
例如,y^2 = x^3 - x + 1
看起来如下所示:
您可以在Desmos Graphing Calculator.中绘制更多的椭圆曲线。
ECC
使用椭圆曲线的两个属性:
- 1) 任何非垂直线与图形的相交点不得超过三个点。
- 2) 该图围绕
x
轴对称。
这些属性使您可以定义图形上任意两个点之间的点运算:
- 1) 在点A和点B之间画一条线。
- 2) 如果该线在第三个点交到图形,则在
x
轴上反射以得到点C = A·B
。
您还可以定义点乘法运算k * A
,其中k
是一个正整数。
- 1) 从
A·A
开始获得点B
:在点A
处的曲线上绘制切线,并在x轴击中图形的地方横切x
线。这是第一个点操作。 - 2) 现在执行
A·B
以获得点C
,然后A·C = D
,依此类推。总共进行k
个点运算,以点P
结尾。如果您告诉某人点A
和P
在哪里,那么他们很难计算k
。
实际算法仅使用x
和y
的整数值,以质数n
为模。起点称为生成器,因此通常称为G
。它的属性为n * G =G
。
要设置ECC
密码系统,请选择一个曲线方程式(x
的系数和常数b
),该曲线上的生成器点G
及其对应的素数n
。私钥是随机选择的数字k
,公钥是k * G
- 对G
进行k
点操作后曲线上的端点。
当然,您不需要执行任何操作。已经有标准曲线,如Curve25519
和NIST ECC
所使用的曲线,并通过CryptoKit
的公钥方法实现。
3. Digital Signatures
数字签名就像HMAC
,但带有公共密钥密码学。发送者不是使用相同的密钥来签名和验证,而是使用私钥进行签名,接收者使用发送者的公钥进行验证。
数字签名的缩写是使用NIST
的P256 / P384 / P512
曲线的椭圆曲线数字签名算法(Elliptic Curve Digital Signature Algorithm - ECDSA)
,以及使用Bernstein
的Ed25519
曲线的Edwards
曲线数字签名算法(EdDSA)
。
假设您像Dumbledore
一样想要将魂器图像发送给哈利。 您将对其进行签名,以便Harry
可以从您作为Dumbledore
处进行验证。 首先,创建用于数字签名的私钥和公钥:
let albusSigningPrivateKey = Curve25519.Signing.PrivateKey()
let albusSigningPublicKeyData =
albusSigningPrivateKey.publicKey.rawRepresentation
公钥的rawRepresentation
类型为Data
,因此您可以通过网络发送它。
接下来,您作为Dumbledore
使用私钥对数据进行签名:
let signatureForData = try! albusSigningPrivateKey.signature(for: data)
或者,您可以签署数据摘要,这样更快:
let digest512 = SHA512.hash(data: data)
let signatureForDigest = try! albusSigningPrivateKey.signature(
for: Data(digest512))
您是邓布利多,这次使用SHA-512
。
当使用Curve25519
私钥时,signature(for :)
使用SHA-512
作为哈希函数,生成数据的椭圆曲线数字签名算法(EdDSA)
签名或Ed25519
椭圆曲线上的摘要。 该算法会生成随机随机数,以在每次调用时产生不同的签名,即使对于相同的数据和密钥也是如此,以防止定时攻击。
如果使用P256
而不是Curve25519
,它将使用SHA-256
作为哈希函数,生成P-256
椭圆曲线上数据的椭圆曲线数字签名算法(ECDSA)
签名。
现在,您作为邓布利多将data
,digest512
和signatureForData
或signatureForDigest
发送给Harry。
切换到你是Harry
,您添加此代码以使用从albusSigningPublicKeyData
创建的密钥来验证签名。
let publicKey = try! Curve25519.Signing.PublicKey(
rawRepresentation: albusSigningPublicKeyData)
if publicKey.isValidSignature(signatureForData, for: data) {
print("Dumbledore sent this data.")
}
if publicKey.isValidSignature(signatureForDigest,
for: Data(digest512)) {
print("Data received == data sent.")
UIImage(data: data)
}
注意:
P256
及其NIST
兄弟姐妹有一种方法来签名摘要,而无需将其转换为Data
。
运行playground
以查看经过身份验证的数据:
这就是进行数字签名的容易程度。 但是同样,数据没有加密。 因此,请继续阅读!
4. Creating a Symmetric Key for Encryption
在本教程的早期,Dumbledore
和Harry
有一个他们都知道的秘密密钥,这使他们可以使用AEAD
密封盒来加密和解密数据。 但是伏地魔以某种方式发现了这个秘密钥匙,并且不可能安全地设置一个新的对称钥匙-哈里在奔跑,邓布利多只是一种精神-所以现在他们需要寻找另一种方式。
密钥协商算法(Key agreement algorithm)
使他们能够从其公共密钥和私有密钥创建共享机密,然后添加约定的盐值(salt value)
以生成对称密钥(symmetric key)
。
计算共享秘密非常简单。 如果G
是椭圆曲线的生成点,而a
是邓布利多的秘密密钥,那么a * G
是他的公钥。 同样,Harry
的秘密密钥是h
,而他的公共密钥是h *G
。事实证明这是事实:
(a * G) * h = (h * G) * a
是的,Harry
将Dumbledore
的公钥乘以他的私钥,这与Dumbledore
通过将Harry
的公钥乘以他自己的私钥所获得的相同。 魔法!
在这里,acronym
是Diffie Hellman(ECDH)
秘钥协议,使用NIST
的P256 / P384 / P512
曲线或Bernstein
的X25519
曲线。
首先,邓布利多和哈里创建密钥协议的私钥和公钥,并将其公钥发布为Data
。
let albusPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let albusPublicKeyData = albusPrivateKey.publicKey.rawRepresentation
let harryPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let harryPublicKeyData = harryPrivateKey.publicKey.rawRepresentation
Dumbledore
和Harry
必须就创建对称密钥的盐值达成共识:
let protocolSalt = "Voldemort's Horcruxes".data(using: .utf8)!
通过网络发送此邮件是安全的。 除非攻击者也知道其中一个私钥,否则它不会有所帮助。
Dumbledore
从原始表示中创建Harry
的公钥,然后将其与他的私钥结合起来,首先计算sharedSecret
,然后计算对称密钥symmetricKey
:
let harryPublicKey = try! Curve25519.KeyAgreement.PublicKey(
rawRepresentation: harryPublicKeyData)
let ADsharedSecret = try! albusPrivateKey.sharedSecretFromKeyAgreement(
with: harryPublicKey)
let ADsymmetricKey = ADsharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self, salt: protocolSalt,
sharedInfo: Data(), outputByteCount: 32)
哈里(Harry)
从原始表示中创建了邓布利多(Dumbledore)
的公钥,然后将其与他的私钥结合起来,首先计算sharedSecret
,然后计算symmetricKey
:
let albusPublicKey = try! Curve25519.KeyAgreement.PublicKey(
rawRepresentation: albusPublicKeyData)
let HPsharedSecret = try! harryPrivateKey.sharedSecretFromKeyAgreement(
with: albusPublicKey)
let HPsymmetricKey = HPsharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self, salt: protocolSalt,
sharedInfo: Data(), outputByteCount: 32)
现在来看它们是否匹配:
if ADsymmetricKey == HPsymmetricKey {
print("Dumbledore and Harry have the same symmetric key.")
}
运行playground
就像是魔术一样,邓布利多和哈利产生了相同的对称密钥! 现在,他们可以使用此对称密钥进行AEAD
身份验证的加密。
5. P256 Key in Secure Enclave
注意:
SecureEnclave
代码仅在具有Secure Enclave
芯片的设备上起作用:iPhone 5S,iPad Air
和更高型号。
为了提供额外的保护,您可以直接在设备的Secure Enclave
中创建P256
私钥。 对代码的改动很小:
// Check that the device has a Secure Enclave
if SecureEnclave.isAvailable {
// Generate private key in Secure Enclave
let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
} else {
let privateKey = P256.Signing.PrivateKey()
}
使用Secure Enclave
中的私钥,创建公钥数据和签名数据或摘要的工作原理完全相同:
// Create public key data
let publicKeyData = privateKey.publicKey.compactRepresentation!
// Produce a signature
let dataSignature = try privateKey.signature(for: data)
let digestSignature = try privateKey.signature(for: digest)
注意:您只能在
Secure Enclave
中创建P256
密钥。 这可能是因为该芯片包含P256
使用的AES
引擎,而不是因为美国政府要求苹果提供后门。
Secure Enclave
具有以下很酷的功能:您可以通过访问控制来限制密钥的使用。 例如:
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.privateKeyUsage, .userPresence],
nil)!
let privateKey = try SecureEnclave.P256.Signing.PrivateKey(
accessControl: accessControl)
您正在设置访问控制,因此只有在设备解锁后才能访问在Secure Enclave
中创建的密钥,并且仅在此设备上可用。另外,您的应用在执行私钥操作时要求用户在场。需要用户在场意味着用户必须通过TouchID
或FaceID
或设备密码进行身份验证。
Complying With Encryption Export Regulations
最后一件事。在许多国家,加密是一个敏感的政治问题。该地图This map显示了哪些国家/地区具有许可或注册要求,进出口控制,提供者协助当局的义务和其他限制。
美国是实行出口管制的国家之一。这是合法的内容,因此我将直接引用Apple的文章 Complying with Encryption Export Regulations:
当您将应用程序提交到
TestFlight
或App Store
时,会将您的应用程序上传到美国的服务器。如果您在美国或加拿大境外分发应用程序,则无论您的法人实体位于何处,您的应用程序都必须遵守美国出口法律。如果您的应用使用,访问,包含,实施或合并加密,则这被视为加密软件的出口,这意味着您的应用要遵守美国出口合规性要求以及您分发产品所在国家/地区的进口合规性要求。应用程式。
从Apple
的App Store Connect Help
Export compliance overview:
加密的使用包括但不限于:
- 通过安全通道(即
HTTPS,SSL
等)进行调用。 - 使用标准的加密算法。
- 使用其他来源的加密功能,例如
iOS
或macOS
。 - 使用专有或非标准加密算法。美国政府将“非标准加密”
(non-standard cryptography)
定义为涉及并入或使用专有或未发布的加密功能的任何“加密”实施方式,包括尚未得到公认的国际标准机构(例如,未采用或批准)的加密算法或协议。IEEE,IETF,ISO,ITU,ETSI,3GPP,TIA
和GSMA
),除非另行发布。
App Store Connect提供了一系列问题App Store Connect provides a set of questions,可帮助您确定是否需要您提供出口合规性文件以及需要哪些表格。
与往常一样,您可以在源代码中找到更多信息:
- WWDC 2019会议709:Cryptography and Your Apps。
- Apple CryptoKit documentation。
除了本教程中的(大部分为Wikipedia
)链接之外,这是对椭圆曲线密码学的解释,它确实帮助我了解了所发生的情况:
A (Relatively Easy To Understand) Primer on Elliptic Curve Cryptography入门确实很容易理解,并且具有一些非常有用的GIF
动画。 但是,请务必扫描注释,以查找某些缺少的信息。 Cloudflare
还有许多其他有关密码学主题的精彩文章。
后记
本篇主要讲述了
CryptoKit
使用入门简单示例,感兴趣的给个赞或者关注~~~