Platform

Swift support Swift Package Manager compatible CocoaPods Compatible Carthage compatible

CryptoSwift

用于 Swift 的加密相关函数和助手,使用 Swift 实现。(#PureSwift)

注意main 分支遵循当前发布的最新 Swift 版本。如果您需要更早的版本以用于旧版本的 Swift,请在您的 Podfile 中指定其版本,或使用该版本分支上的代码。较旧的分支不受支持。查看 版本 以了解详情。


要求 | 特性 | 贡献 | 安装 | Swift 版本 | 使用方法 | 作者 | 许可证 | 更新日志

支持与赞助商

该项目的财务可持续性归功于我们 GitHub 赞助商 的持续贡献

高级赞助商

Emerge Tools 是一套革命性的产品,旨在为移动应用以及构建它们的团队提供强大的支持。

www.emergetools.com/

要求

心情愉快

特性

哈希 (摘要)

MD5 | SHA1 | SHA2-224 | SHA2-256 | SHA2-384 | SHA2-512 | SHA3

循环冗余校验 (CRC)

CRC32 | CRC32C | CRC16

密码

AES-128, AES-192, AES-256 | ChaCha20 | XChaCha20 | Rabbit | Blowfish

RSA (公钥加密算法)

加密,签名

消息认证码

Poly1305 | HMAC (MD5, SHA1, SHA256) | CMAC | CBC-MAC

密码工作模式

基于密码的密钥派生函数

数据填充

带关联数据的认证加密 (AEAD)

为什么

为什么? 因为我能

如何参与?

您想帮忙,太棒了!请随意 fork 我们的仓库,进行更改并向我们发送 pull request。

贡献

查看 CONTRIBUTING.md 以获取有关如何帮助 CryptoSwift 的更多信息。

安装

硬化运行时 (macOS) 和 Xcode

如果应用使用启用硬化运行时的 本地运行签名 证书,则二进制 CryptoSwift.xcframework(Swift Package Manager 包集成使用)将无法在您的应用中正确加载。可以像这样设置 Xcode。要解决此问题,您有两个选择

Xcode 项目

要安装 CryptoSwift,请将其作为子模块添加到您的项目(在顶级项目目录中)

git submodule add https://github.com/krzyzanowskim/CryptoSwift.git

建议启用 Whole-Module Optimization 以获得更好的性能。非优化构建会导致性能显著下降。

Swift Package Manager

您可以使用 Swift Package Manager 并在 Package.swift 中通过添加以下内容来指定依赖项

.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.8.3")

参见:Package.swift - 手册

注意:Swift Package Manager 为调试 Xcode 构建使用调试配置,这可能会导致性能显著下降(高达 x10000 倍)。Release 构建中的性能特征是不同的。要克服此问题,请考虑嵌入下面描述的 CryptoSwift.xcframework

CocoaPods

您可以使用 CocoaPods

pod 'CryptoSwift', '~> 1.8.3'

请记住,CocoaPods 将在不使用 Whole-Module Optimization 的情况下构建 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 是一个 Release(优化)二进制文件,可提供最佳的 Swift 代码性能。

Screen Shot 2020-10-27 at 00 06 32

嵌入式框架

嵌入式框架需要最低部署目标为 iOS 11.0 或 macOS Sierra (10.13)。将 CryptoSwift.xcodeproj 文件拖到您的 Xcode 项目中,并将相应的框架作为依赖项添加到您的目标。现在选择您的 App 并为应用目标选择 General 选项卡。找到 Embedded Binaries 并按“+”,然后选择 CryptoSwift.framework (iOS, macOS, watchOS 或 tvOS)

有时“embedded framework”选项不可用。在这种情况下,您必须为目标添加新的构建阶段。

iOS、macOS、watchOS、tvOS

在项目中,您会找到适用于所有平台的 single scheme

Swift 版本支持

使用方法

基础知识
import CryptoSwift

CryptoSwift 使用字节数组,也称为 Array<UInt8>,作为所有操作的基础类型。所有数据都可以转换为字节流。您会找到接受 StringData 的便捷函数,它将在内部转换为字节数组。

数据类型转换

为了您的方便,CryptoSwift 提供了两个函数,可以轻松地将字节数组转换为 Data 或将 Data 转换为字节数组

Data 来自字节

let data = Data([0x01, 0x02, 0x03])

DataArray<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()
计算 CRC
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()
基于 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-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)
}
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> = // 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))
AES-GCM

伽罗瓦/计数器模式 (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 实例从编码到执行解码。

AES-CCM

带密码块链接消息认证码的计数器模式加密的结果是密文和认证标签,稍后用于解密。

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 的有效参数。

AEAD
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 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 文件。