用 Swift 实现的与加密相关的函数和助手,适用于 Swift。(#PureSwift)
注意:main
分支遵循最新发布的 Swift 版本。如果您需要较早版本的 Swift,请在 Podfile
中指定其版本或使用该版本的分支代码。旧分支不受支持。查看版本了解详情。
需求 | 特性 | 贡献 | 安装 | Swift 版本 | 使用方法 | 作者 | 许可证 | 更新日志
项目的财务可持续性得益于我们的 GitHub 赞助商 持续的贡献
Emerge Tools 是一套革命性产品,旨在为移动应用及其开发团队提供超级动力。
心情愉悦
MD5 | SHA1 | SHA2-224 | SHA2-256 | SHA2-384 | SHA2-512 | SHA3
AES-128, AES-192, AES-256 | ChaCha20 | XChaCha20 | Rabbit | Blowfish
Poly1305 | HMAC (MD5, SHA1, SHA256) | CMAC | CBC-MAC
你想帮忙,太好了!继续 fork 我们的仓库,做出你的修改并发送给我们一个拉取请求。
查看 CONTRIBUTING.md 了解如何帮助 CryptoSwift 的更多信息。
如果应用使用本地运行签名签名证书并启用了强化运行时,二进制 CryptoSwift.xcframework(由 Swift Package Manager 包集成使用)将无法在你的应用中正确加载。Xcode 可能会这样设置。要解决这个问题,你有两个选择:
禁用库验证
即 com.apple.security.cs.disable-library-validation
权限要安装 CryptoSwift,将其作为子模块添加到你的项目(在顶级项目目录):
git submodule add https://github.com/krzyzanowskim/CryptoSwift.git
建议启用整模块优化以获得更好的性能。未优化的构建会导致性能明显变差。
你可以使用 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。
pod 'CryptoSwift', '~> 1.8.3'
请记住,CocoaPods 将在没有整模块优化的情况下构建 CryptoSwift,这可能会影响性能。你可以在安装后手动更改它,或使用 cocoapods-wholemodule 插件。
你可以使用 Carthage。 在 Cartfile 中指定:
github "krzyzanowskim/CryptoSwift"
运行 carthage
构建框架,并将构建的 CryptoSwift.framework 拖入你的 Xcode 项目。按照构建说明进行操 作。常见问题。
XCFrameworks 需要 Xcode 11 或更高版本,它们可以像我们习惯集成 .framework
格式一样进行集成。
请使用脚本 scripts/build-framework.sh 生成二进制 CryptoSwift.xcframework
存档,你可以在 Xcode 中将其用作依赖项。
CryptoSwift.xcframework 是一个发布(优化)二进制文件,提供最佳可用的 Swift 代码性能。
<img width="320" alt="Screen Shot 2020-10-27 at 00 06 32" src="https://yellow-cdn.veclightyear.com/2b54e442/1e2df743-9203-4fdb-97fd-e7ffdddc9bc1.png">嵌入式框架要求最低部署目标为 iOS 11.0 或 macOS Sierra (10.13)。将 CryptoSwift.xcodeproj
文件拖入你的 Xcode 项目,并将适当的框架作为依赖项添加到你的目标。现在选择你的应用并选择应用目标的 General 选项卡。找到 嵌入式二进制文件 并按 "+",然后选择 `CryptoSwift
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()
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()
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)
let encrypted = try ChaCha20(key: key, iv: iv).encrypt(message) let decrypted = try ChaCha20(key: key, iv: iv).decrypt(encrypted)
let encrypted = try Rabbit(key: key, iv: iv).encrypt(message) let decrypted = try Rabbit(key: key, iv: iv).decrypt(encrypted)
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)
关于填充的注意事项:手动填充数据是可选的,CryptoSwift默认使用PKCS7填充。如果你需要手动禁用/启用填充,可以通过设置__AES__类的参数来实现
AES加密的变体(AES-128、AES-192、AES-256)取决于给定的密钥长度:
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) }
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))
伽罗瓦/计数器模式(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
实例进行加密和解密。
带密码块链消息认证码的计数器模式(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
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 本软件按"原样"提供,不提供任何明示或暗示的保证。
在任何情况下,作者均不对因使用本软件而产生的任何损害承担责任。
允许任何人出于任何目的使用本软件,包括商业应用,并且可以自由更改和重新分发,但须遵守以下限制:
请参阅CHANGELOG文件。
字节跳动发布的AI编程神器IDE
Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速 、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。
全能AI智能助手,随时解答生活与工作的多样问题
问小白,由元石科技研发 的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。
实时语音翻译/同声传译工具
Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全 球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。
一键生成PPT和Word,让学习生活更轻松
讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。