cryptokit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CryptoKit

CryptoKit

Apple CryptoKit provides a Swift-native API for cryptographic operations: hashing, message authentication, symmetric encryption, public-key signing, key agreement, and Secure Enclave key storage. Available on iOS 13+. Prefer CryptoKit over CommonCrypto or raw Security framework APIs in all new code targeting Swift 6.3+.
Apple CryptoKit 为加密操作提供了Swift原生API:哈希运算、消息认证、对称加密、公钥签名、密钥协商和Secure Enclave密钥存储,支持iOS 13及以上版本。在所有面向Swift 6.3+的新代码中,优先使用CryptoKit,而非CommonCrypto或原生Security框架API。

Contents

目录

Hashing

哈希运算

CryptoKit provides SHA256, SHA384, and SHA512 hash functions. All conform to the
HashFunction
protocol.
CryptoKit提供SHA256、SHA384和SHA512哈希函数,全部符合
HashFunction
协议。

One-shot hashing

单次哈希

swift
import CryptoKit

let data = Data("Hello, world!".utf8)
let digest = SHA256.hash(data: data)
let hex = digest.compactMap { String(format: "%02x", $0) }.joined()
SHA384 and SHA512 work identically -- substitute the type name.
swift
import CryptoKit

let data = Data("Hello, world!".utf8)
let digest = SHA256.hash(data: data)
let hex = digest.compactMap { String(format: "%02x", $0) }.joined()
SHA384和SHA512的使用方式完全一致,替换对应类型名即可。

Incremental hashing

增量哈希

For large data or streaming input, hash incrementally:
swift
var hasher = SHA256()
hasher.update(data: chunk1)
hasher.update(data: chunk2)
let digest = hasher.finalize()
针对大数据或流式输入,可使用增量哈希方式:
swift
var hasher = SHA256()
hasher.update(data: chunk1)
hasher.update(data: chunk2)
let digest = hasher.finalize()

Digest comparison

摘要对比

CryptoKit digests use constant-time comparison by default. Direct
==
checks between digests are safe against timing attacks.
swift
let expected = SHA256.hash(data: reference)
let actual = SHA256.hash(data: received)
if expected == actual {
    // Data integrity verified
}
CryptoKit摘要默认使用恒定时间对比,直接使用
==
对比两个摘要可以抵御时序攻击。
swift
let expected = SHA256.hash(data: reference)
let actual = SHA256.hash(data: received)
if expected == actual {
    // 数据完整性验证通过
}

HMAC

HMAC

HMAC provides message authentication using a symmetric key and a hash function.
HMAC通过对称密钥和哈希函数提供消息认证能力。

Computing an authentication code

计算认证码

swift
let key = SymmetricKey(size: .bits256)
let data = Data("message".utf8)

let mac = HMAC<SHA256>.authenticationCode(for: data, using: key)
swift
let key = SymmetricKey(size: .bits256)
let data = Data("message".utf8)

let mac = HMAC<SHA256>.authenticationCode(for: data, using: key)

Verifying an authentication code

验证认证码

swift
let isValid = HMAC<SHA256>.isValidAuthenticationCode(
    mac, authenticating: data, using: key
)
This uses constant-time comparison internally.
swift
let isValid = HMAC<SHA256>.isValidAuthenticationCode(
    mac, authenticating: data, using: key
)
该方法内部使用恒定时间对比。

Incremental HMAC

增量HMAC

swift
var hmac = HMAC<SHA256>(key: key)
hmac.update(data: chunk1)
hmac.update(data: chunk2)
let mac = hmac.finalize()
swift
var hmac = HMAC<SHA256>(key: key)
hmac.update(data: chunk1)
hmac.update(data: chunk2)
let mac = hmac.finalize()

Symmetric Encryption

对称加密

CryptoKit provides two authenticated encryption ciphers: AES-GCM and ChaChaPoly. Both produce a sealed box containing the nonce, ciphertext, and authentication tag.
CryptoKit提供两种认证加密算法:AES-GCM和ChaChaPoly,两者都会生成包含nonce、密文和认证标签的密封盒子。

AES-GCM

AES-GCM

The default choice for symmetric encryption. Hardware-accelerated on Apple silicon.
swift
let key = SymmetricKey(size: .bits256)
let plaintext = Data("Secret message".utf8)

// Encrypt
let sealedBox = try AES.GCM.seal(plaintext, using: key)
let ciphertext = sealedBox.combined!  // nonce + ciphertext + tag

// Decrypt
let box = try AES.GCM.SealedBox(combined: ciphertext)
let decrypted = try AES.GCM.open(box, using: key)
对称加密的默认选择,在苹果芯片上有硬件加速支持。
swift
let key = SymmetricKey(size: .bits256)
let plaintext = Data("Secret message".utf8)

// 加密
let sealedBox = try AES.GCM.seal(plaintext, using: key)
let ciphertext = sealedBox.combined!  // nonce + 密文 + 标签

// 解密
let box = try AES.GCM.SealedBox(combined: ciphertext)
let decrypted = try AES.GCM.open(box, using: key)

ChaChaPoly

ChaChaPoly

Use ChaChaPoly when AES hardware acceleration is unavailable or when interoperating with protocols that require ChaCha20-Poly1305 (e.g., TLS, WireGuard).
swift
let sealedBox = try ChaChaPoly.seal(plaintext, using: key)
let combined = sealedBox.combined  // Always non-optional for ChaChaPoly

let box = try ChaChaPoly.SealedBox(combined: combined)
let decrypted = try ChaChaPoly.open(box, using: key)
当AES硬件加速不可用,或是需要与要求ChaCha20-Poly1305的协议(如TLS、WireGuard)交互时使用ChaChaPoly。
swift
let sealedBox = try ChaChaPoly.seal(plaintext, using: key)
let combined = sealedBox.combined  // ChaChaPoly的该属性永远不为空

let box = try ChaChaPoly.SealedBox(combined: combined)
let decrypted = try ChaChaPoly.open(box, using: key)

Authenticated data

附加认证数据

Both ciphers support additional authenticated data (AAD). The AAD is authenticated but not encrypted -- useful for metadata that must remain in the clear but be tamper-proof.
swift
let header = Data("v1".utf8)
let sealedBox = try AES.GCM.seal(
    plaintext, using: key, authenticating: header
)
let decrypted = try AES.GCM.open(
    sealedBox, using: key, authenticating: header
)
两种算法都支持附加认证数据(AAD),AAD会被认证但不会被加密,适用于需要明文传输但不能被篡改的元数据场景。
swift
let header = Data("v1".utf8)
let sealedBox = try AES.GCM.seal(
    plaintext, using: key, authenticating: header
)
let decrypted = try AES.GCM.open(
    sealedBox, using: key, authenticating: header
)

SymmetricKey sizes

SymmetricKey 大小

SizeUse
.bits128
AES-128-GCM; adequate for most uses
.bits192
AES-192-GCM; uncommon
.bits256
AES-256-GCM or ChaChaPoly; recommended default
大小用途
.bits128
AES-128-GCM;适用于大多数场景
.bits192
AES-192-GCM;使用较少
.bits256
AES-256-GCM或ChaChaPoly;推荐默认使用

Generating a key

生成密钥

swift
let key = SymmetricKey(size: .bits256)
To create a key from existing data:
swift
let key = SymmetricKey(data: existingKeyData)
swift
let key = SymmetricKey(size: .bits256)
从已有数据创建密钥:
swift
let key = SymmetricKey(data: existingKeyData)

Public-Key Signing

公钥签名

CryptoKit supports ECDSA signing with NIST curves and Ed25519 via Curve25519.
CryptoKit支持使用NIST曲线的ECDSA签名,以及通过Curve25519实现的Ed25519签名。

NIST curves: P256, P384, P521

NIST曲线:P256、P384、P521

swift
let signingKey = P256.Signing.PrivateKey()
let publicKey = signingKey.publicKey

// Sign
let signature = try signingKey.signature(for: data)

// Verify
let isValid = publicKey.isValidSignature(signature, for: data)
P384 and P521 use the same API -- substitute the curve name.
NIST key representations:
swift
// Export
let der = signingKey.derRepresentation
let pem = signingKey.pemRepresentation
let x963 = signingKey.x963Representation
let raw = signingKey.rawRepresentation

// Import
let restored = try P256.Signing.PrivateKey(derRepresentation: der)
swift
let signingKey = P256.Signing.PrivateKey()
let publicKey = signingKey.publicKey

// 签名
let signature = try signingKey.signature(for: data)

// 验证
let isValid = publicKey.isValidSignature(signature, for: data)
P384和P521使用相同的API,替换曲线名称即可。
NIST密钥表示形式:
swift
// 导出
let der = signingKey.derRepresentation
let pem = signingKey.pemRepresentation
let x963 = signingKey.x963Representation
let raw = signingKey.rawRepresentation

// 导入
let restored = try P256.Signing.PrivateKey(derRepresentation: der)

Curve25519 / Ed25519

Curve25519 / Ed25519

swift
let signingKey = Curve25519.Signing.PrivateKey()
let publicKey = signingKey.publicKey

// Sign
let signature = try signingKey.signature(for: data)

// Verify
let isValid = publicKey.isValidSignature(signature, for: data)
Curve25519 keys use
rawRepresentation
only (no DER/PEM/X9.63).
swift
let signingKey = Curve25519.Signing.PrivateKey()
let publicKey = signingKey.publicKey

// 签名
let signature = try signingKey.signature(for: data)

// 验证
let isValid = publicKey.isValidSignature(signature, for: data)
Curve25519密钥仅支持
rawRepresentation
(不支持DER/PEM/X9.63格式)。

Choosing a curve

曲线选择

CurveSignature SchemeKey SizeTypical Use
P256ECDSA256-bitGeneral purpose; Secure Enclave support
P384ECDSA384-bitHigher security requirements
P521ECDSA521-bitMaximum NIST security level
Curve25519Ed25519256-bitFast; simple API; no Secure Enclave
Use P256 by default. Use Curve25519 when interoperating with Ed25519-based protocols.
曲线签名方案密钥长度典型用途
P256ECDSA256位通用场景;支持Secure Enclave
P384ECDSA384位更高安全要求场景
P521ECDSA521位NIST最高安全等级
Curve25519Ed25519256位速度快;API简单;不支持Secure Enclave
默认使用P256,当需要与基于Ed25519的协议交互时使用Curve25519。

Key Agreement

密钥协商

Key agreement lets two parties derive a shared symmetric key from their public/private key pairs using ECDH.
密钥协商允许两方通过ECDH算法,从各自的公私钥对派生共享对称密钥。

ECDH with P256

基于P256的ECDH

swift
// Alice
let aliceKey = P256.KeyAgreement.PrivateKey()

// Bob
let bobKey = P256.KeyAgreement.PrivateKey()

// Alice computes shared secret
let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(
    with: bobKey.publicKey
)

// Derive a symmetric key using HKDF
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: Data("salt".utf8),
    sharedInfo: Data("my-app-v1".utf8),
    outputByteCount: 32
)
Bob computes the same
sharedSecret
using his private key and Alice's public key. Both derive the same
symmetricKey
.
swift
// 爱丽丝
let aliceKey = P256.KeyAgreement.PrivateKey()

// 鲍勃
let bobKey = P256.KeyAgreement.PrivateKey()

// 爱丽丝计算共享密钥
let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(
    with: bobKey.publicKey
)

// 使用HKDF派生对称密钥
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: Data("salt".utf8),
    sharedInfo: Data("my-app-v1".utf8),
    outputByteCount: 32
)
鲍勃使用自己的私钥和爱丽丝的公钥计算得到相同的
sharedSecret
,双方会派生得到相同的
symmetricKey

ECDH with Curve25519

基于Curve25519的ECDH

swift
let aliceKey = Curve25519.KeyAgreement.PrivateKey()
let bobKey = Curve25519.KeyAgreement.PrivateKey()

let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(
    with: bobKey.publicKey
)

let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: Data(),
    sharedInfo: Data("context".utf8),
    outputByteCount: 32
)
swift
let aliceKey = Curve25519.KeyAgreement.PrivateKey()
let bobKey = Curve25519.KeyAgreement.PrivateKey()

let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(
    with: bobKey.publicKey
)

let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: Data(),
    sharedInfo: Data("context".utf8),
    outputByteCount: 32
)

Key derivation functions

密钥派生函数

SharedSecret
is not directly usable as a
SymmetricKey
. Always derive a key using one of:
MethodStandardUse
hkdfDerivedSymmetricKey
HKDF (RFC 5869)Recommended default
x963DerivedSymmetricKey
ANSI X9.63Interop with X9.63 systems
Always provide a non-empty
sharedInfo
string to bind the derived key to a specific protocol context.
SharedSecret
不能直接作为
SymmetricKey
使用,必须通过以下方法之一派生密钥:
方法标准用途
hkdfDerivedSymmetricKey
HKDF (RFC 5869)推荐默认使用
x963DerivedSymmetricKey
ANSI X9.63与X9.63系统交互场景
请始终提供非空的
sharedInfo
字符串,将派生密钥绑定到特定协议上下文。

Secure Enclave

Secure Enclave

The Secure Enclave provides hardware-backed key storage. Private keys never leave the hardware. Only P256 signing and key agreement are supported for ECDH operations. Post-quantum key types (MLKEM, MLDSA) are also available in the Secure Enclave on supported hardware.
Secure Enclave提供硬件级别的密钥存储,私钥永远不会离开硬件,仅支持P256签名和ECDH密钥协商操作。在支持的硬件上,Secure Enclave还提供后量子密钥类型(MLKEM、MLDSA)。

Availability check

可用性检查

swift
guard SecureEnclave.isAvailable else {
    // Fall back to software keys
    return
}
swift
guard SecureEnclave.isAvailable else {
    // 回退到软件密钥
    return
}

Creating a Secure Enclave signing key

创建Secure Enclave签名密钥

swift
let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
let publicKey = privateKey.publicKey  // Standard P256.Signing.PublicKey

let signature = try privateKey.signature(for: data)
let isValid = publicKey.isValidSignature(signature, for: data)
swift
let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
let publicKey = privateKey.publicKey  // 标准P256.Signing.PublicKey

let signature = try privateKey.signature(for: data)
let isValid = publicKey.isValidSignature(signature, for: data)

Access control

访问控制

Require biometric authentication to use the key:
swift
let accessControl = SecAccessControlCreateWithFlags(
    nil,
    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
    [.privateKeyUsage, .biometryCurrentSet],
    nil
)!

let privateKey = try SecureEnclave.P256.Signing.PrivateKey(
    accessControl: accessControl
)
要求生物认证才能使用密钥:
swift
let accessControl = SecAccessControlCreateWithFlags(
    nil,
    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
    [.privateKeyUsage, .biometryCurrentSet],
    nil
)!

let privateKey = try SecureEnclave.P256.Signing.PrivateKey(
    accessControl: accessControl
)

Persisting Secure Enclave keys

持久化Secure Enclave密钥

The
dataRepresentation
is an encrypted blob that only the same device's Secure Enclave can restore. Store it in the Keychain.
swift
// Export
let blob = privateKey.dataRepresentation

// Restore
let restored = try SecureEnclave.P256.Signing.PrivateKey(
    dataRepresentation: blob
)
dataRepresentation
是加密后的二进制数据,只有同一设备的Secure Enclave可以还原,请将其存储在Keychain中。
swift
// 导出
let blob = privateKey.dataRepresentation

// 还原
let restored = try SecureEnclave.P256.Signing.PrivateKey(
    dataRepresentation: blob
)

Secure Enclave key agreement

Secure Enclave密钥协商

swift
let seKey = try SecureEnclave.P256.KeyAgreement.PrivateKey()
let peerPublicKey: P256.KeyAgreement.PublicKey = // from peer

let sharedSecret = try seKey.sharedSecretFromKeyAgreement(
    with: peerPublicKey
)
swift
let seKey = try SecureEnclave.P256.KeyAgreement.PrivateKey()
let peerPublicKey: P256.KeyAgreement.PublicKey = // 来自对端

let sharedSecret = try seKey.sharedSecretFromKeyAgreement(
    with: peerPublicKey
)

Common Mistakes

常见错误

1. Using the shared secret directly as a key

1. 直接将共享密钥作为对称密钥使用

swift
// DON'T
let badKey = SymmetricKey(data: sharedSecret)

// DO -- derive with HKDF
let goodKey = sharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: salt,
    sharedInfo: info,
    outputByteCount: 32
)
swift
// 错误示例
let badKey = SymmetricKey(data: sharedSecret)

// 正确示例 -- 通过HKDF派生
let goodKey = sharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: salt,
    sharedInfo: info,
    outputByteCount: 32
)

2. Reusing nonces

2. 重复使用nonce

swift
// DON'T -- hardcoded nonce
let nonce = try AES.GCM.Nonce(data: Data(repeating: 0, count: 12))
let box = try AES.GCM.seal(data, using: key, nonce: nonce)

// DO -- let CryptoKit generate a random nonce (default behavior)
let box = try AES.GCM.seal(data, using: key)
swift
// 错误示例 -- 硬编码nonce
let nonce = try AES.GCM.Nonce(data: Data(repeating: 0, count: 12))
let box = try AES.GCM.seal(data, using: key, nonce: nonce)

// 正确示例 -- 让CryptoKit生成随机nonce(默认行为)
let box = try AES.GCM.seal(data, using: key)

3. Ignoring authentication tag verification

3. 忽略认证标签验证

swift
// DON'T -- manually strip tag and decrypt
// DO -- always use AES.GCM.open() or ChaChaPoly.open()
// which verifies the tag automatically
swift
// 错误示例 -- 手动剥离标签后解密
// 正确示例 -- 始终使用AES.GCM.open()或ChaChaPoly.open()
// 会自动验证标签

4. Using Insecure hashes for security

4. 使用不安全的哈希算法处理安全相关场景

swift
// DON'T -- MD5/SHA1 for integrity or security
import CryptoKit
let bad = Insecure.MD5.hash(data: data)

// DO -- use SHA256 or stronger
let good = SHA256.hash(data: data)
Insecure.MD5
and
Insecure.SHA1
exist only for legacy compatibility (checksum verification, protocol interop). Never use them for new security-sensitive operations.
swift
// 错误示例 -- 使用MD5/SHA1做完整性或安全校验
import CryptoKit
let bad = Insecure.MD5.hash(data: data)

// 正确示例 -- 使用SHA256或更高安全等级的算法
let good = SHA256.hash(data: data)
Insecure.MD5
Insecure.SHA1
仅用于旧版兼容场景(校验和验证、协议兼容),永远不要在新的安全敏感操作中使用它们。

5. Storing symmetric keys in UserDefaults

5. 将对称密钥存储在UserDefaults中

swift
// DON'T
UserDefaults.standard.set(key.rawBytes, forKey: "encryptionKey")

// DO -- store in Keychain
// See references/cryptokit-patterns.md for Keychain storage patterns
swift
// 错误示例
UserDefaults.standard.set(key.rawBytes, forKey: "encryptionKey")

// 正确示例 -- 存储在Keychain中
// 参考references/cryptokit-patterns.md了解Keychain存储模式

6. Not checking Secure Enclave availability

6. 未检查Secure Enclave可用性

swift
// DON'T -- crash on simulator or unsupported hardware
let key = try SecureEnclave.P256.Signing.PrivateKey()

// DO
guard SecureEnclave.isAvailable else { /* fallback */ }
let key = try SecureEnclave.P256.Signing.PrivateKey()
swift
// 错误示例 -- 在模拟器或不支持的硬件上会崩溃
let key = try SecureEnclave.P256.Signing.PrivateKey()

// 正确示例
guard SecureEnclave.isAvailable else { /* 回退逻辑 */ }
let key = try SecureEnclave.P256.Signing.PrivateKey()

Review Checklist

审查清单

  • Using CryptoKit, not CommonCrypto or raw Security framework
  • SHA256+ for hashing; no MD5/SHA1 for security purposes
  • HMAC verification uses
    isValidAuthenticationCode
    (constant-time)
  • AES-GCM or ChaChaPoly for symmetric encryption; 256-bit keys
  • Nonces are random (default) -- not hardcoded or reused
  • Authenticated data (AAD) used where metadata needs integrity
  • SharedSecret derived via HKDF, not used directly
  • sharedInfo parameter is non-empty and context-specific
  • Secure Enclave availability checked before use
  • Secure Enclave key
    dataRepresentation
    stored in Keychain
  • Private keys not logged, printed, or serialized unnecessarily
  • Symmetric keys stored in Keychain, not UserDefaults or files
  • Encryption export compliance considered (
    ITSAppUsesNonExemptEncryption
    )
  • 使用CryptoKit,而非CommonCrypto或原生Security框架
  • 哈希运算使用SHA256及以上等级算法;安全场景不使用MD5/SHA1
  • HMAC验证使用
    isValidAuthenticationCode
    (恒定时间验证)
  • 对称加密使用AES-GCM或ChaChaPoly;使用256位密钥
  • nonce是随机的(默认行为)-- 没有硬编码或重复使用
  • 元数据需要完整性校验时使用附加认证数据(AAD)
  • SharedSecret通过HKDF派生,不直接使用
  • sharedInfo参数非空且与上下文绑定
  • 使用Secure Enclave前检查可用性
  • Secure Enclave密钥的
    dataRepresentation
    存储在Keychain中
  • 私钥不会被不必要地日志、打印或序列化
  • 对称密钥存储在Keychain中,而非UserDefaults或文件
  • 考虑加密出口合规要求(
    ITSAppUsesNonExemptEncryption

References

参考资料