用于 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 我们的仓库,进行更改并向我们发送 pull request。
查看 CONTRIBUTING.md 以获取有关如何帮助 CryptoSwift 的更多信息。
如果应用使用启用硬化运行时的 本地运行签名 证书,则二进制 CryptoSwift.xcframework(Swift Package Manager 包集成使用)将无法在您的应用中正确加载。可以像这样设置 Xcode。要解决此问题,您有两个选择
Disable Library Validation
又名 com.apple.security.cs.disable-library-validation
授权要安装 CryptoSwift,请将其作为子模块添加到您的项目(在顶级项目目录中)
git submodule add https://github.com/krzyzanowskim/CryptoSwift.git
建议启用 Whole-Module Optimization 以获得更好的性能。非优化构建会导致性能显著下降。
您可以使用 Swift Package Manager 并在 Package.swift
中通过添加以下内容来指定依赖项
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.8.3")
注意:Swift Package Manager 为调试 Xcode 构建使用调试配置,这可能会导致性能显著下降(高达 x10000 倍)。Release 构建中的性能特征是不同的。要克服此问题,请考虑嵌入下面描述的 CryptoSwift.xcframework
。
您可以使用 CocoaPods。
pod 'CryptoSwift', '~> 1.8.3'
请记住,CocoaPods 将在不使用 Whole-Module Optimization 的情况下构建 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 是一个 Release(优化)二进制文件,可提供最佳的 Swift 代码性能。
嵌入式框架需要最低部署目标为 iOS 11.0 或 macOS Sierra (10.13)。将 CryptoSwift.xcodeproj
文件拖到您的 Xcode 项目中,并将相应的框架作为依赖项添加到您的目标。现在选择您的 App 并为应用目标选择 General 选项卡。找到 Embedded Binaries 并按“+”,然后选择 CryptoSwift.framework
(iOS, macOS, watchOS 或 tvOS)
有时“embedded framework”选项不可用。在这种情况下,您必须为目标添加新的构建阶段。
在项目中,您会找到适用于所有平台的 single scheme
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 struct usage */
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()
// Calculate Message Authentication Code (MAC) for message
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: .sha2(.sha256)).calculate()
let password: Array<UInt8> = Array("s33krit".utf8)
let salt: Array<UInt8> = Array("nacllcan".utf8)
// Scrypt implementation does not implement work parallelization, so `p` parameter will
// increase the work time even in multicore systems
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)
/* Generate a key from a `password`. Optional if you already have a key */
let key = try PKCS5.PBKDF2(
password: password,
salt: salt,
iterations: 4096,
keyLength: 32, /* AES-256 */
variant: .sha256
).calculate()
/* Generate random IV value. IV is public value. Either need to generate, or get it from elsewhere */
let iv = AES.randomIV(AES.blockSize)
/* AES cryptor instance */
let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7)
/* Encrypt Data */
let inputData = Data()
let encryptedBytes = try aes.encrypt(inputData.bytes)
let encryptedData = Data(encryptedBytes)
/* Decrypt Data */
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>()
// aggregate partial results
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))
// finish at the end
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> = // Random bytes of `AES.blockSize` length
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 {
// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want.
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 {
// failed
}
解密
do {
// In combined mode, the authentication tag is appended to the encrypted message. This is usually what you want.
let gcm = GCM(iv: iv, mode: .combined)
let aes = try AES(key: key, blockMode: gcm, padding: .noPadding)
return try aes.decrypt(encrypted)
} catch {
// failed
}
注意:GCM 实例不打算重复使用。因此,您不能使用同一个 GCM
实例从编码到执行解码。
带密码块链接消息认证码的计数器模式加密的结果是密文和认证标签,稍后用于解密。
do {
// The authentication tag is appended to the encrypted message.
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 {
// failed
}
查看文档或 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 modulus
let e: Array<UInt8> = // RSA public exponent
let d: Array<UInt8> = // RSA private exponent
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) // This generates a modulus, public exponent and private exponent with the given size
RSA 加密和解密示例
// Alice Generates a Private Key
let alicesPrivateKey = try RSA(keySize: 1024)
// Alice shares her **public** key with Bob
let alicesPublicKeyData = try alicesPrivateKey.publicKeyExternalRepresentation()
// Bob receives the raw external representation of Alices public key and imports it
let bobsImportOfAlicesPublicKey = try RSA(rawRepresentation: alicesPublicKeyData)
// Bob can now encrypt a message for Alice using her public key
let message = "Hi Alice! This is Bob!"
let privateMessage = try bobsImportOfAlicesPublicKey.encrypt(message.bytes)
// This results in some encrypted output like this
// URcRwG6LfH63zOQf2w+HIllPri9Rb6hFlXbi/bh03zPl2MIIiSTjbAPqbVFmoF3RmDzFjIarIS7ZpT57a1F+OFOJjx50WYlng7dioKFS/rsuGHYnMn4csjCRF6TAqvRQcRnBueeINRRA8SLaLHX6sZuQkjIE5AoHJwgavmiv8PY=
// Bob can now send this encrypted message to Alice without worrying about people being able to read the original contents
// Alice receives the encrypted message and uses her private key to decrypt the data and recover the original message
let originalDecryptedMessage = try alicesPrivateKey.decrypt(privateMessage)
print(String(data: Data(originalDecryptedMessage), encoding: .utf8))
// "Hi Alice! This is Bob!"
RSA 签名和验证示例
// Alice Generates a Private Key
let alicesPrivateKey = try RSA(keySize: 1024)
// Alice wants to sign a message that she agrees with
let messageAliceSupports = "Hi my name is Alice!"
let alicesSignature = try alicesPrivateKey.sign(messageAliceSupports.bytes)
// Alice shares her Public key and the signature with Bob
let alicesPublicKeyData = try alicesPrivateKey.publicKeyExternalRepresentation()
// Bob receives the raw external representation of Alices Public key and imports it!
let bobsImportOfAlicesPublicKey = try RSA(rawRepresentation: alicesPublicKeyData)
// Bob can now verify that Alice signed the message using the Private key associated with her shared Public key.
let verifiedSignature = try bobsImportOfAlicesPublicKey.verify(signature: alicesSignature, for: "Hi my name is Alice!".bytes)
if verifiedSignature == true {
// Bob knows that the signature Alice provided is valid for the message and was signed using the Private key associated with Alices shared Public key.
} else {
// The signature was invalid, so either
// - the message Alice signed was different then what we expected.
// - or Alice used a Private key that isn't associated with the shared Public key that Bob has.
}
CryptoSwift RSA 密钥 -> Apple Security Framework SecKey 示例
/// Starting with a CryptoSwift RSA Key
let rsaKey = try RSA(keySize: 1024)
/// Define your Keys attributes
let attributes: [String:Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // or kSecAttrKeyClassPublic
kSecAttrKeySizeInBits as String: 1024, // The appropriate bits
kSecAttrIsPermanent as String: false
]
var error:Unmanaged<CFError>? = nil
guard let rsaSecKey = try SecKeyCreateWithData(rsaKey.externalRepresentation() as CFData, attributes as CFDictionary, &error) else {
/// Error constructing SecKey from raw key data
return
}
/// You now have an RSA SecKey for use with Apple's Security framework
Apple Security Framework SecKey -> CryptoSwift RSA 密钥示例
/// Starting with a SecKey RSA Key
let rsaSecKey:SecKey
/// Copy External Representation
var externalRepError:Unmanaged<CFError>?
guard let cfdata = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) else {
/// Failed to copy external representation for RSA SecKey
return
}
/// Instantiate the RSA Key from the raw external representation
let rsaKey = try RSA(rawRepresentation: cfdata as Data)
/// You now have a CryptoSwift RSA Key
CryptoSwift 由 Marcin Krzyżanowski 拥有和维护
您可以在 Twitter 上关注我 @krzyzanowskim 以获取项目更新和发布信息。
此发行版包含密码软件。您当前居住的国家/地区可能对加密软件的进口、拥有、使用和/或再出口到另一个国家/地区有限制。在使用任何加密软件之前,请检查您所在国家/地区的法律、法规和政策,了解有关加密软件的进口、拥有或使用以及再出口是否被允许。有关更多信息,请参阅 https://www.wassenaar.org/。
版权所有 (C) 2014-2025 Marcin Krzyżanowski marcin@krzyzanowskim.com 此软件按“原样”提供,不提供任何明示或暗示的保证。
在任何情况下,作者均不对因使用本软件而引起的任何损害承担责任。
特此授予任何人使用本软件的许可,用于任何目的,包括商业应用程序,以及对其进行更改和自由再分发,但须符合以下限制
请参阅 CHANGELOG 文件。