TextbookRSA
RS 加密的一个教科书级实现,用于教学目的。
不要使用此框架进行实际的加密!
TextbookRSA 是一个用 swift
编写的框架,旨在为初学者提供一个(非常)简化和易于理解的 RSA 加密实现。除此之外,它还包含了一个“通过周期查找器解密”的实现,这展示了如果有一个快速的周期查找器(例如有 Shor 算法实现的量子计算机),RSA 加密可能会被破解。
RSA 加密最重要的方面之一是公钥的 长度。现代标准要求 2048 位或更多,而使用 1024 位加密仍然在使用中,但略有缺点,使用 512 位已被认为相当危险。在这个框架中,密钥长度从不超过 32 位,所以我要再次强调:不要使用这个框架进行实际世界的加密!密钥之所以这么短,是因为我们不希望实现“大整数”类型(我们只是使用现有的类型 UInt
和 UInt32
)。大整数算术可能非常有趣,但它不是 RSA 加密的概念性部分,而我们在这里的目标是关注基础。
ECB
RSA 加密被称为 非对称 或 公钥 加密。它独立加密 块(简单地说就是整数),这些块必须小于公钥。特别地,块的大小有限意味着我们只能通过 RSA 发送非常短的消息。
遗憾的是,如果这个框架只能加密一些单个、微小的数字,那么它可能会相当无聊。为了让用户能够加密任意消息,我们添加了一个更高级的协议,称为电子密码簿(ECB)。这是一个分组密码方案的形式,其中原始消息被简单地切割成更小的块(块),然后单独加密这些块。
同样,这只是为了使框架的使用更加有趣和触手可及,但对于实际应用来说却被强烈反对。在分组密码方案中使用RSA通常是一个糟糕的主意,它既低效又过时,甚至在其他加密协议中,ECB也被认为是一个糟糕且过时的选择。然而,它却是理解起来最简单且实现起来最简单的一种。
Alice想要向Bob发送一条秘密消息
让我们通过一个典型的RSA交换过程来演示,并使用我们的框架来阐述每个步骤。
步骤0:选择我们想要使用的实现
目前《RSAProtocol》的唯一实现是《UIntRSA》,因此我们可以从一些代码糖开始
typealias RSA = UIntRSA
步骤1:Bob生成并保存他的密钥
let keys = RSA.Keys()
keys
对象包含私钥,即公钥的两个素数因子。它是CustomStringConvertible
,因此您可以打印出来,并且它是Codable
,所以Bob可以轻松地生成一个JSON文件来将密钥存储在他的计算机上的某个位置
let keysJSON = try JSONEncoder().encode(keys)
// ... store `keysJSON` (type `Data`) somewhere (safe) on the computer.
步骤2:Bob生成加密参数并将其发送给Alice
let encryptionParms = keys.generateEncryptionParameters()
这些参数包含公钥(作为成员modulo
)和一个加密指数。同样,它们也是CustomStringConvertible
和Codable
,所以鲍勃可以生成一个JSON文件并将其发送给爱丽丝(因为这些参数是公开的,所以可以使用任何安全的通信渠道)
let encryptionParmsJSON = try JSONEncoder().encode(encryptionParms)
// ... send `encryptionParmsJSON` to Alice via e-mail/SMS/smoke signals...
步骤 3:爱丽丝加密她的消息并发送给鲍勃
爱丽丝收到鲍勃的(公开)消息,该消息作为JSON文件包含加密参数,并将其再次转换为RSA.TransformationParameters
对象
let encryptionParmsJSON = Data(/* ... get the JSON data sent by Bob ... */)
let encryptionParms = try JSONDecoder().decode(
RSA.TransformationParameters.self, from: encryptionParmsJSON)
现在,爱丽丝可以使用加密参数来加密她的秘密消息。如果她的消息是一个任意文件,即一系列字节,她可以加密一个类型为Data
的对象
let message = Data(/* ... get data from a file ... */)
let encryptedMessage = Encrypter.encrypt(message, parameters: encryptionParms)
另一方面,如果爱丽丝只是想加密一个文本字符串,她可以使用接受一个String
参数的便利重载
let message = "Hi, Bob. The code for my safe is 1234."
let encryptedMessage = Encrypter.encrypt(message, parameters: encryptionParms)
对象encryptedMessage
的类型为Encrypter.EncryptedData
。它不仅包含加密结果,还包含加密参数的一部分(即指数)。这一点很重要,因为鲍勃可能会使用相同的密钥进行许多对话,与许多不同的人,但是为了每条信息,他应该生成新的加密参数,并且他需要知道哪些参数对应于哪些信息,以便最终解密它们。
同样,此类型是CustomStringConvertible
和Codable
,因此爱丽丝可以轻松地将她的加密消息(这是公开的)发送给鲍勃
let encryptedMessageJSON = try JSONEncoder().encode(encryptedMessage)
// ... send `encryptedMessageJSON` to Bob via e-mail/SMS/smoke signals...
步骤 4:鲍勃解密爱丽丝的消息
首先,鲍勃需要将收到的JSON数据再次转换为Encrypter.EncryptedData
对象
let encryptedMessageJSON = Data(/* ... get the JSON data sent by Alice ... */)
let encryptedMessage = try JSONDecoder().decode(
Encrypter.EncryptedData.self, from: encryptedMessageJSON)
他还需要他的密钥,这些密钥存储在某处的一个文件中
let keysJSON = Data(/* ... get the JSON data for the keys from a file */)
let keys = try JSONDecoder().decode(RSA.Keys.self, from: keysJSON)
使用这些密钥,鲍勃可以初始化一个Decrypter
let decrypter = Decrypter(keys: keys)
最后,鲍勃可以解密爱丽丝的消息。基本方法将消息解密为Data
对象
let message = decrypter.decrypt(encryptedMessage) // `message` is of type `Data`
如果他们(公开地)同意爱丽丝会发送一个文本消息,鲍勃可以使用便利方法decryptText
,该方法返回一个String
let message = decrypter.decryptText(encryptedMessage) // `message` is of type `String`
艾芙试图解密爱丽丝的消息
与大多数RSA教科书介绍一样,我们假设一个名为Eve的监听者一直在监听对话,并希望仅使用公开信息(即不知道公钥的素数因子)来解密Alice的消息。一种理论上可以做到的方法是,如果Eve有一个快速的模指数化的周期预言机。
为了更精确,给定正数m
(加法群的阶)和b
(指数运算的底数),考虑函数f(x) = b^x (mod m)
。如果b
和m
互质(没有公共的非平凡因子),那么这个函数有一个周期。也就是说,存在一个正数r
,使得对所有x
,有f(x) = f(x + r)
。这个周期是非常难以找到的,如果有人找到了快速计算它的方法,RSA就处于严重危险之中(以下将会显示)。
在我们的框架中,我们实现了类PeriodOracle
(实际上是一个具有静态方法的enum
),它使用“暴力”方法(即仅计算f(1)
,f(2)
,f(3)
...直到值开始重复)计算模指数化的这样的周期。这非常慢,在实际操作中只能用于处理小数字。有更多的先进算法用于计算周期,但即使最快的算法在面对有数千位数的公钥时也是无用的。
类PeriodDecrypter
是对DecrypterProtocol
的第二个实现(第一个是类Decrypter
),它使用PeriodOracle
来解密消息,而不必知道私钥。如何实现以及为什么它工作的理论超出了本文档的范围。我们只想显示如何使用此框架。以下是Eve使用PeriodDecrypter
的方法:
// Listen to public information being sent between Alice and Bob:
let encryptionParmsJSON = Data(/* ... get the JSON data for the encryption parameters ... */)
let encryptedMessageJSON = Data(/* ... get the JSON data for the encrypted message ... */)
// Convert JSON data into objects from the framework:
let encryptionParms = try JSONDecoder().decode(
RSA.TransformationParameters.self, from: encryptionParmsJSON)
let encryptedMessage = try JSONDecoder().decode(
Encrypter.EncryptedData.self, from: encryptedMessageJSON)
// Initialize a decrypter:
let decrypter = PeriodDecrypter(publicKey: encryptionParms.modulo)
在此之后,解密工作与Decrypter
类型完全一样,即基本方法解密为Data
let message = decrypter.decrypt(encryptedMessage) // `message` is of type `Data`
便利方法decryptText
更进一步,将数据转换为String
let message = decrypter.decryptText(encryptedMessage) // `message` is of type `String`
重要:记得我们说寻找周期是非常难的吗?如果你尝试使用具有大公钥的PeriodDecrypter
,计算可能需要一天、一年或数千年。即使使用我们高达32位的微小公钥,也需要花费很长时间。为了使你能够测试基于周期的解密,我们在类UIntRSAKeys
中添加了一个静态方法small(maxPrime: UInt32)
。使用此方法,你可以指定公钥素数因子的上限。100的上限似乎可以得到相当快的测试。你可以决定多高,所以当你想使用基于周期的解密运行测试时,请确保替换Bob初始化键的行,如下所示:
let keys = RSA.Keys.small(maxPrime: 100)
安装
除了手动克隆存储库并按需使用/修改源文件外,我们还支持通过CocoaPods和Swift包管理器进行安装。
CocoaPods
要将 TextbookRSA 安装为 pod,请将以下行添加到您的 Podfile
中并运行
pod 'TextbookRSA', '~> 1.0.1'
。
$ pod install
如果您不熟悉 CocoaPods,您可以在 这里 了解有关 CocoaPods 的所有信息。
Swift Package Manager
要通过 SPM 安装 TextbookRSA,请打开 Package.swift
,将依赖项
.package(url: "https://github.com/imagineon/TextbookRSA.git", from: "1.0.1")
添加到您的 package
常量中,并将依赖项
"TextbookRSA"
添加到应该使用此框架的每个目标中。然后运行
$ swift build
。