CryptoSwift 1.8.3

CryptoSwift 1.8.3

测试已测试
语言语言 SwiftSwift
许可证 NOASSERTION
发布最后发布2024年8月
SPM支持 SPM

Marcin Krzyżanowski 维护。



Platform

Swift support Swift Package Manager compatible CocoaPods Compatible Carthage compatible

CryptoSwift

CryptoSwift 使用 Swift 实现的 Swift 项目中的加密相关函数和辅助工具。(#PureSwift

注意:主分支遵循当前发布的最新 Swift 版本。如果您需要旧版本的 Swift,可以在您的 Podfile 中指定其版本,或使用该版本分支上的代码。旧分支不受支持。请查看 版本 以获取详细信息。


要求 | 特性 | 贡献 | 安装 | Swift 版本 | 如何使用 | 作者 | 许可证 | 变更日志

赞助

为了您的便利,我花费了一些时间来维护,所以或许您可以赞助 1 美元,这样我可以继续工作。每天有超过 8000 次克隆。如果每个使用我在这里的工作的公司每个月赞助我 1 美元,我会说我们已经持平了。赶快行动吧,找到 赞助 按钮,履行您的责任。

CryptoSwift 并未得到任何大公司的支持,它是在我的业余时间开发的,同时也是我作为自由职业者的一部分。

Twitter

需求

好心情

功能

  • 易于使用
  • 方便的 String 和 Data 扩展
  • 支持增量更新(流等)
  • 支持 iOS、Android、macOS、AppleTV、watchOS、Linux 平台

哈希(摘要)

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

循环冗余校验(CRC)

CRC32 | CRC32C | CRC16

加密方法

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

RSA (公钥加密算法)

加密,签名

消息认证器

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

加密操作模式

  • 电子密码本 (ECB)
  • 分组密码块链接 (CBC)
  • 传播密码块链链 (PCBC)
  • 密码反馈 (CFB)
  • 输出反馈 (OFB)
  • 计数模式(CTR
  • Galois/计数模式(GCM
  • 带有密码块链接消息认证码的计数(CCM
  • OCB认证加密算法(OCB

基于密码的密钥派生函数

  • PBKDF1(基于密码的密钥派生函数 1)
  • PBKDF2(基于密码的密钥派生函数 2)
  • HKDF(基于 HMAC 的提取和扩展密钥派生函数)
  • Scrypt(基于密码的密钥派生函数 scrypt)

数据填充

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

为什么

为什么? 因为我可以

如何参与?

你想帮忙,太好了!去fork我们的仓库,做出改变并发送给我们一个pull request。

贡献

查看CONTRIBUTING.md了解如何帮助CryptoSwift。

安装

加固运行时(macOS)和Xcode

Binary CryptoSwift.xcframework(用于Swift Package Manager包集成)如果在应用中使用带有启用加固运行时的Sign to Run Locally签名证书则无法在应用中正确加载。要解决这个问题,你有两种选择:

  • 使用合适的签名证书,例如:Development <- 这是一个正确的操作
  • 使用Disable Library Validation也称为com.apple.security.cs.disable-library-validation权限

Xcode 项目

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

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

建议启用 整个模块优化 以获得更好的性能。未经优化的构建会导致性能显著下降。

Swift 包管理器

您可以使用 Swift 包管理器 并在 Package.swift 中指定依赖项,添加以下内容

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

请参阅: Package.swift - 手册

注意:Swift 包管理器在调试 Xcode 构建中使用调试配置,这可能会导致性能严重下降(高达 x10000)。发布构建中性能特性不同。为了克服这个问题,请考虑嵌入以下所描述的 CryptoSwift.xcframework

CocoaPods

您可以使用 CocoaPods

pod 'CryptoSwift', '~> 1.7.1'

请注意,CocoaPods 将在未启用 整个模块优化 的情况下构建 CryptoSwift,这可能会影响性能。您可以在安装后手动更改它,或使用 cocoapods-wholemodule 插件。

Carthage

您可以使用 Carthage 并在 Cartfile 中指定。

github "krzyzanowskim/CryptoSwift"

运行 carthage 构建框架,并将构建的 CryptoSwift.framework 拖入您的 Xcode 项目中。遵循 构建说明。常见问题 请见

XCFramework

构建 XCFramework 需要使用 Xcode 11 或更高版本,并且它们的集成方式类似于我们常用的 .framework 格式的集成方式。请使用脚本 scripts/build-framework.sh 生成二进制的 CryptoSwift.xcframework 归档,您可以用作 Xcode 中的依赖项。

CryptoSwift.xcframework 是一个发布(优化)二进制文件,提供最佳可用的 Swift 代码性能。

Screen Shot 2020-10-27 at 00 06 32

嵌入式框架

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

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

iOS、macOS、watchOS、tvOS

在项目中,您将找到一个适用于所有平台的 单个方案

  • CryptoSwift

Swift 版本支持

  • Swift 1.1: 分支 swift11 版本 <= 0.0.6
  • 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: 分支 swift51 版本 <= 1.3.3
  • Swift 5.3 及以上版本,分支 main

如何使用

基础知识
import CryptoSwift

CryptoSwift使用字节数组aka Array<UInt8>作为所有操作的基本类型。每个数据都可以转换为字节流。您将找到接受StringData的便利函数,并将其内部转换为字节数组。

数据类型转换

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

字节数据

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

Data转换为Array

let bytes = data.bytes                     // [1,2,3]

十六进制编码

let bytes = Array<UInt8>(hex: "0x010203")  // [1,2,3]
let hex   = bytes.toHexString()            // "010203"

从字符串构建字节

let bytes: Array<UInt8> = "cipherkey".bytes  // Array("cipherkey".utf8)

另外,查看与Base64编码数据一起工作的辅助函数

"aPf/i9th9iX+vf49eR7PYk2q7S5xmm3jkRLejgzHNJs=".decryptBase64ToString(cipher)
"aPf/i9th9iX+vf49eR7PYk2q7S5xmm3jkRLejgzHNJs=".decryptBase64(cipher)
bytes.toBase64()
计算摘要

散列数据或字节数组(即Array

/* 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: .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)
兔子
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)

/* 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实例不应重复使用。因此,您不能从编码到解码同时使用相同的代码实例。

AES-CCM

计数密文块链接-消息认证码(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的安全框架 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的安全框架 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 关注我的项目更新和发布

加密通知

本分发包括加密软件。您目前居住的国家可能对加密软件的进口、拥有、使用和/或将加密软件再出口到另一个国家的行为有限制。在使用任何加密软件之前,请检查您国家的法律、法规和政策,以了解加密软件的进口、拥有或使用、再出口是否被允许。有关更多信息,请见http://www.wassenaar.org/

许可

版权 (C) 2014-2022 Marcin Krzyżanowski [email protected] 本软件按“原样”提供,不提供任何明示或暗示的担保。

在任何情况下,作者不应对使用本软件引起的任何损害承担责任。

任何人许可使用此软件,以任何目的,包括商业应用,并将其自由修改和重新分发,但受以下限制。

  • 本软件的来源不得虚假陈述;您不得宣称您是原始软件的作者。如果您在产品中使用本软件,产品文档中必须有一次承认。
  • 修改后的源版本必须清楚地标记为修改版本,并且不得谎称是原始软件。
  • 本通知不得从任何源或二进制分发中删除或修改。
  • 任何形式的重新分发都必须保留以下承认:“本产品包含由“Marcin Krzyzanowski”(http://krzyzanowskim.com/)开发的软件。”

变更日志

参见变更日志文件