CryptoSwift
用 Swift 实现的与加密相关的函数和助手,适用于 Swift。(#PureSwift)
注意:main
分支遵循最新发布的 Swift 版本。如果您需要较早版本的 Swift,请在 Podfile
中指定其版本或使用该版本的分支代码。旧分支不受支持。查看版本了解详情。
需求 | 特性 | 贡献 | 安装 | Swift 版本 | 使用方法 | 作者 | 许可证 | 更新日志
支持与赞助商
项目的财务可持续性得益于我们的 GitHub 赞助商 持续的贡献
高级赞助商
Emerge Tools 是一套革命性产品,旨在为移动应用及其开发团队提供超级动力。
需求
心情愉悦
特性
- 易于使用
- 为 String 和 Data 提供便利扩展
- 支持增量更新(流等)
- 支持 iOS、Android、macOS、AppleTV、watchOS、Linux
哈希(摘要)
MD5 | SHA1 | SHA2-224 | SHA2-256 | SHA2-384 | SHA2-512 | SHA3
循环冗余校验 (CRC)
密码
AES-128, AES-192, AES-256 | ChaCha20 | XChaCha20 | Rabbit | Blowfish
RSA(公钥加密算法)
消息认证器
Poly1305 | HMAC (MD5, SHA1, SHA256) | CMAC | CBC-MAC
密码操作模式
- 电子密码本 (ECB)
- 密码块链接 (CBC)
- 传播密码块链接 (PCBC)
- 密码反馈 (CFB)
- 输出反馈 (OFB)
- 计数器模式 (CTR)
- 伽罗瓦/计数器模式 (GCM)
- 带密码块链接消息认证码的计数器 (CCM)
- OCB 认证加密算法 (OCB)
基于密码的密钥派生函数
数据填充
带关联数据的认证加密(AEAD)
为什么
如何参与?
你想帮忙,太好了!继续 fork 我们的仓库,做出你的修改并发送给我们一个拉取请求。
贡献
查看 CONTRIBUTING.md 了解如何帮助 CryptoSwift 的更多信息。
安装
强化运行时(macOS)和 Xcode
如果应用使用本地运行签名签名证书并启用了强化运行时,二进制 CryptoSwift.xcframework(由 Swift Package Manager 包集成使用)将无法在你的应用中正确加载。Xcode 可能会这样设置。要解决这个问题,你有两个选择:
- 使用适当的签名证书,例如 Development <- 这是正确的做法
- 使用
禁用库验证
即com.apple.security.cs.disable-library-validation
权限
Xcode 项目
要安装 CryptoSwift,将其作为子模块添加到你的项目(在顶级项目目录):
git submodule add https://github.com/krzyzanowskim/CryptoSwift.git
建议启用整模块优化以获得更好的性能。未优化的构建会导致性能明显变差。
Swift Package Manager
你可以使用 Swift Package Manager 并在 Package.swift
中通过添加以下内容指定依赖:
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.8.3")
注意:Swift Package Manager 在 Xcode 调试构建中使用调试配置,这可能会导致性能显著下降(最多 10000 倍)。发布构建中的性能特征有所不同。要解决这个问题,请考虑嵌入下面描述的 CryptoSwift.xcframework
。
CocoaPods
你可以使用 CocoaPods。
pod 'CryptoSwift', '~> 1.8.3'
请记住,CocoaPods 将在没有整模块优化的情况下构建 CryptoSwift,这可能会影响性能。你可以在安装后手动更改它,或使用 cocoapods-wholemodule 插件。
Carthage
你可以使用 Carthage。 在 Cartfile 中指定:
github "krzyzanowskim/CryptoSwift"
运行 carthage
构建框架,并将构建的 CryptoSwift.framework 拖入你的 Xcode 项目。按照构建说明进行操作。常见问题。
XCFramework
XCFrameworks 需要 Xcode 11 或更高版本,它们可以像我们习惯集成 .framework
格式一样进行集成。
请使用脚本 scripts/build-framework.sh 生成二进制 CryptoSwift.xcframework
存档,你可以在 Xcode 中将其用作依赖项。
CryptoSwift.xcframework 是一个发布(优化)二进制文件,提供最佳可用的 Swift 代码性能。
嵌入式框架
嵌入式框架要求最低部署目标为 iOS 11.0 或 macOS Sierra (10.13)。将 CryptoSwift.xcodeproj
文件拖入你的 Xcode 项目,并将适当的框架作为依赖项添加到你的目标。现在选择你的应用并选择应用目标的 General 选项卡。找到 嵌入式二进制文件 并按 "+",然后选择 `CryptoSwift
- Swift 1.2: 分支 swift12 版本 <= 0.0.13
- Swift 2.1: 分支 swift21 版本 <= 0.2.3
- Swift 2.2, 2.3: 分支 swift2 版本 <= 0.5.2
- Swift 3.1, 分支 swift3 版本 <= 0.6.9
- Swift 3.2, 分支 swift32 版本 = 0.7.0
- Swift 4.0, 分支 swift4 版本 <= 0.12.0
- Swift 4.2, 分支 swift42 版本 <= 0.15.0
- Swift 5.0, 分支 swift5 版本 <= 1.2.0
- Swift 5.1, 分支 swift5 版本 <= 1.3.3
- Swift 5.3 及更新版本, 分支 main
使用方法
- 基础知识(数据类型、转换等)
- 摘要(MD5、SHA等)
- 消息验证器(HMAC、CMAC等)
- 基于密码的密钥派生函数(PBKDF2等)
- 基于HMAC的密钥派生函数(HKDF)
- 数据填充
- ChaCha20
- Rabbit
- Blowfish
- AES - 高级加密标准
- AES-GCM
- 带关联数据的认证加密(AEAD)
基础知识
import CryptoSwift
CryptoSwift使用字节数组(即Array<UInt8>
)作为所有操作的基本类型。任何数据都可以转换为字节流。你会发现一些接受String
或Data
的便捷函数,它们会在内部转换为字节数组。
数据类型转换
为了方便使用,CryptoSwift提供了两个函数,可以轻松地将字节数组转换为Data
或将Data
转换为字节数组:
从字节创建Data:
let data = Data([0x01, 0x02, 0x03])
Data
转换为Array<UInt8>
let bytes = data.bytes // [1,2,3]
十六进制编码:
let bytes = Array<UInt8>(hex: "0x010203") // [1,2,3]
let hex = bytes.toHexString() // "010203"
从String
构建字节
let bytes: Array<UInt8> = "cipherkey".bytes // Array("cipherkey".utf8)
另外...查看使用Base64编码数据的辅助函数:
"aPf/i9th9iX+vf49eR7PYk2q7S5xmm3jkRLejgzHNJs=".decryptBase64ToString(cipher)
"aPf/i9th9iX+vf49eR7PYk2q7S5xmm3jkRLejgzHNJs=".decryptBase64(cipher)
bytes.toBase64()
计算摘要
对数据或字节数组(即Array<UInt8>
)进行哈希处理
/* Hash结构体用法 */
let bytes: Array<UInt8> = [0x01, 0x02, 0x03]
let digest = input.md5()
let digest = Digest.md5(bytes)
let data = Data([0x01, 0x02, 0x03])
let hash = data.md5()
let hash = data.sha1()
let hash = data.sha224()
let hash = data.sha256()
let hash = data.sha384()
let hash = data.sha512()
do {
var digest = MD5()
let partial1 = try digest.update(withBytes: [0x31, 0x32])
let partial2 = try digest.update(withBytes: [0x33])
let result = try digest.finish()
} catch { }
对字符串进行哈希处理并打印结果
let hash = "123".md5() // "123".bytes.md5()
计算CRC
bytes.crc16()
data.crc16()
bytes.crc32()
data.crc32()
消息验证器
// 为消息计算消息认证码(MAC)
let key: Array<UInt8> = [1,2,3,4,5,6,7,8,9,10,...]
try Poly1305(key: key).authenticate(bytes)
try HMAC(key: key, variant: .sha256).authenticate(bytes)
try CMAC(key: key).authenticate(bytes)
基于密码的密钥派生函数
let password: Array<UInt8> = Array("s33krit".utf8)
let salt: Array<UInt8> = Array("nacllcan".utf8)
let key = try PKCS5.PBKDF2(password: password, salt: salt, iterations: 4096, keyLength: 32, variant: .sha256).calculate()
let password: Array<UInt8> = Array("s33krit".utf8)
let salt: Array<UInt8> = Array("nacllcan".utf8)
// Scrypt实现不支持并行化工作,因此`p`参数即使在多核系统中也会增加工作时间
let key = try Scrypt(password: password, salt: salt, dkLen: 64, N: 16384, r: 8, p: 1).calculate()
基于HMAC的密钥派生函数
let password: Array<UInt8> = Array("s33krit".utf8)
let salt: Array<UInt8> = Array("nacllcan".utf8)
let key = try HKDF(password: password, salt: salt, variant: .sha256).calculate()
数据填充
某些内容加密算法假设输入长度是k
个八位字节的倍数,其中k
大于1。对于这样的算法,需要对输入进行填充。
Padding.pkcs7.add(to: bytes, blockSize: AES.blockSize)
使用密码
ChaCha20
let encrypted = try ChaCha20(key: key, iv: iv).encrypt(message)
let decrypted = try ChaCha20(key: key, iv: iv).decrypt(encrypted)
Rabbit
let encrypted = try Rabbit(key: key, iv: iv).encrypt(message)
let decrypted = try Rabbit(key: key, iv: iv).decrypt(encrypted)
Blowfish
let encrypted = try Blowfish(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).encrypt(message)
let decrypted = try Blowfish(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).decrypt(encrypted)
AES
关于填充的注意事项:手动填充数据是可选的,CryptoSwift默认使用PKCS7填充。如果你需要手动禁用/启用填充,可以通过设置__AES__类的参数来实现
AES加密的变体(AES-128、AES-192、AES-256)取决于给定的密钥长度:
- AES-128 = 16字节
- AES-192 = 24字节
- AES-256 = 32字节
AES-256示例
let encryptedBytes = try AES(key: [1,2,3,...,32], blockMode: CBC(iv: [1,2,3,...,16]), padding: .pkcs7)
完整示例:
let password: [UInt8] = Array("s33krit".utf8)
let salt: [UInt8] = Array("nacllcan".utf8)
/* 从`密码`生成密钥。如果已经有密钥,则可选 */
let key = try PKCS5.PBKDF2(
password: password,
salt: salt,
iterations: 4096,
keyLength: 32, /* AES-256 */
variant: .sha256
).calculate()
/* 生成随机IV值。IV是公开值。需要生成或从其他地方获取 */
let iv = AES.randomIV(AES.blockSize)
/* AES加密器实例 */
let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7)
/* 加密数据 */
let inputData = Data()
let encryptedBytes = try aes.encrypt(inputData.bytes)
let encryptedData = Data(encryptedBytes)
/* 解密数据 */
let decryptedBytes = try aes.decrypt(encryptedData.bytes)
let decryptedData = Data(decryptedBytes)
一次性操作
do {
let aes = try AES(key: "keykeykeykeykeyk", iv: "drowssapdrowssap") // aes128
let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
} catch { }
增量更新
增量操作使用Cryptor实例,一次加密/解密一部分,这样可以为大文件节省内存。
do {
var encryptor = try AES(key: "keykeykeykeykeyk", iv: "drowssapdrowssap").makeEncryptor()
var ciphertext = Array<UInt8>()
// 聚合部分结果
ciphertext += try encryptor.update(withBytes: Array("Nullam quis risus ".utf8))
ciphertext += try encryptor.update(withBytes: Array("eget urna mollis ".utf8))
ciphertext += try encryptor.update(withBytes: Array("ornare vel eu leo.".utf8))
// 最后完成
ciphertext += try encryptor.finish()
print(ciphertext.toHexString())
} catch {
print(error)
}
AES高级用法
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9]
let key: Array<UInt8> = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
let iv: Array<UInt8> = // `AES.blockSize`长度的随机字节
do {
let encrypted = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).encrypt(input)
let decrypted = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).decrypt(encrypted)
} catch {
print(error)
}
不带数据填充的AES
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9]
let encrypted: Array<UInt8> = try! AES(key: Array("secret0key000000".utf8), blockMode: CBC(iv: Array("0123456789012345".utf8)), padding: .noPadding).encrypt(input)
使用便捷扩展
let plain = Data([0x01, 0x02, 0x03])
let encrypted = try! plain.encrypt(ChaCha20(key: key, iv: iv))
let decrypted = try! encrypted.decrypt(ChaCha20(key: key, iv: iv))
AES-GCM
伽罗瓦/计数器模式(GCM)加密的结果是密文和认证标签,后者用于解密。
加密
do {
// 在组合模式下,认证标签直接附加到加密消息后。这通常是你想要的。
let gcm = GCM(iv: iv, mode: .combined)
let aes = try AES(key: key, blockMode: gcm, padding: .noPadding)
let encrypted = try aes.encrypt(plaintext)
let tag = gcm.authenticationTag
} catch {
// 失败
}
解密
do {
// 在组合模式下,认证标签附加在加密消息后。这通常是你想要的。
let gcm = GCM(iv: iv, mode: .combined)
let aes = try AES(key: key, blockMode: gcm, padding: .noPadding)
return try aes.decrypt(encrypted)
} catch {
// 失败
}
注意:GCM实例不能重复使用。所以你不能使用同一个GCM
实例进行加密和解密。
AES-CCM
带密码块链消息认证码的计数器模式(CCM)加密的结果是密文和认证标签,后者用于解密。
do {
// 认证标签附加在加密消息后。
let tagLength = 8
let ccm = CCM(iv: iv, tagLength: tagLength, messageLength: ciphertext.count - tagLength, additionalAuthenticatedData: data)
let aes = try AES(key: key, blockMode: ccm, padding: .noPadding)
return try aes.decrypt(encrypted)
} catch {
// 失败
}
请查看文档或CCM规范以了解CCM的有
let encrypt = try AEADChaCha20Poly1305.encrypt(plaintext, key: key, iv: nonce, authenticationHeader: header)
let decrypt = try AEADChaCha20Poly1305.decrypt(ciphertext, key: key, iv: nonce, authenticationHeader: header, authenticationTag: tagArr: tag)
RSA
从参数初始化RSA
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9]
let n: Array<UInt8> = // RSA模数
let e: Array<UInt8> = // RSA公开指数
let d: Array<UInt8> = // RSA私有指数
let rsa = RSA(n: n, e: e, d: d)
do {
let encrypted = try rsa.encrypt(input)
let decrypted = try rsa.decrypt(encrypted)
} catch {
print(error)
}
RSA密钥生成
let rsa = try RSA(keySize: 2048) // 这将生成具有给定大小的模数、公开指数和私有指数
RSA加密和解密示例
// Alice生成一个私钥
let alicesPrivateKey = try RSA(keySize: 1024)
// Alice与Bob分享她的**公钥**
let alicesPublicKeyData = try alicesPrivateKey.publicKeyExternalRepresentation()
// Bob接收Alice公钥的原始外部表示并导入它
let bobsImportOfAlicesPublicKey = try RSA(rawRepresentation: alicesPublicKeyData)
// Bob现在可以使用Alice的公钥为她加密一条消息
let message = "Hi Alice! This is Bob!"
let privateMessage = try bobsImportOfAlicesPublicKey.encrypt(message.bytes)
// 这会产生一些加密输出,如下所示
// URcRwG6LfH63zOQf2w+HIllPri9Rb6hFlXbi/bh03zPl2MIIiSTjbAPqbVFmoF3RmDzFjIarIS7ZpT57a1F+OFOJjx50WYlng7dioKFS/rsuGHYnMn4csjCRF6TAqvRQcRnBueeINRRA8SLaLHX6sZuQkjIE5AoHJwgavmiv8PY=
// Bob现在可以将这个加密消息发送给Alice,而不用担心其他人能够读取原始内容
// Alice接收加密消息并使用她的私钥解密数据,恢复原始消息
let originalDecryptedMessage = try alicesPrivateKey.decrypt(privateMessage)
print(String(data: Data(originalDecryptedMessage), encoding: .utf8))
// "Hi Alice! This is Bob!"
RSA签名和验证示例
// Alice生成一个私钥
let alicesPrivateKey = try RSA(keySize: 1024)
// Alice想要签署一条她同意的消息
let messageAliceSupports = "Hi my name is Alice!"
let alicesSignature = try alicesPrivateKey.sign(messageAliceSupports.bytes)
// Alice与Bob分享她的公钥和签名
let alicesPublicKeyData = try alicesPrivateKey.publicKeyExternalRepresentation()
// Bob接收Alice公钥的原始外部表示并导入它!
let bobsImportOfAlicesPublicKey = try RSA(rawRepresentation: alicesPublicKeyData)
// Bob现在可以验证Alice使用与她共享的公钥相关联的私钥签署了该消息。
let verifiedSignature = try bobsImportOfAlicesPublicKey.verify(signature: alicesSignature, for: "Hi my name is Alice!".bytes)
if verifiedSignature == true {
// Bob知道Alice提供的签名对该消息是有效的,并且是使用与Alice共享的公钥相关联的私钥签署的。
} else {
// 签名无效,所以要么
// - Alice签署的消息与我们预期的不同。
// - 或者Alice使用了与Bob拥有的共享公钥不相关联的私钥。
}
CryptoSwift RSA密钥 -> Apple的Security Framework SecKey示例
/// 从CryptoSwift RSA密钥开始
let rsaKey = try RSA(keySize: 1024)
/// 定义你的密钥属性
let attributes: [String:Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // 或 kSecAttrKeyClassPublic
kSecAttrKeySizeInBits as String: 1024, // 适当的位数
kSecAttrIsPermanent as String: false
]
var error:Unmanaged<CFError>? = nil
guard let rsaSecKey = try SecKeyCreateWithData(rsaKey.externalRepresentation() as CFData, attributes as CFDictionary, &error) else {
/// 从原始密钥数据构造SecKey时出错
return
}
/// 现在你有了一个可以在Apple的Security框架中使用的RSA SecKey
Apple的Security Framework SecKey -> CryptoSwift RSA密钥示例
/// 从SecKey RSA密钥开始
let rsaSecKey:SecKey
/// 复制外部表示
var externalRepError:Unmanaged<CFError>?
guard let cfdata = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) else {
/// 无法复制RSA SecKey的外部表示
return
}
/// 从原始外部表示实例化RSA密钥
let rsaKey = try RSA(rawRepresentation: cfdata as Data)
/// 现在你有了一个CryptoSwift RSA密钥
作者
CryptoSwift由Marcin Krzyżanowski拥有和维护
你可以在Twitter上关注我 @krzyzanowskim 以获取项目更新和发布信息。
密码学通知
本发行版包含加密软件。你目前居住的国家可能对加密软件的进口、拥有、使用和/或再出口到另一个国家有限制。在使用任何加密软件之前,请检查你所在国家的法律、法规和政策,了解是否允许进口、拥有或使用加密软件,以及是否允许将其再出口。详情请参见 https://www.wassenaar.org/。
许可证
版权所有 (C) 2014-2022 Marcin Krzyżanowski marcin@krzyzanowskim.com 本软件按"原样"提供,不提供任何明示或暗示的保证。
在任何情况下,作者均不对因使用本软件而产生的任何损害承担责任。
允许任何人出于任何目的使用本软件,包括商业应用,并且可以自由更改和重新分发,但须遵守以下限制:
- 不得歪曲本软件的来源;你不得声称你编写了原始软件。如果你在产品中使用本软件,产品文档中需要致谢。
- 更改后的源版本必须明确标明为such,不得歪曲为原始软件。
- 本通知不得从任何源代码或二进制分发中删除或更改。
- 任何形式的再分发都必须保留以下致谢:"本产品包含由"Marcin Krzyzanowski"(https://krzyzanowskim.com/)开发的软件。"
更新日志
请参阅CHANGELOG文件。