cryptokit について
Swift 開発者向けに、Apple CryptoKit で暗号処理を実装する方法を解説するガイドです。API の使い方や OS の対応状況、安全な Enclave の鍵、よくある問題を扱います。
主な機能
- 01ハッシュと HMAC
- 02認証付き対称暗号化
- 03公開鍵署名と鍵共有
- 04HPKE と耐量子 API
- 05Secure Enclave 対応鍵
- 06暗号コードのチェックリスト
活用シーン
- 01データのハッシュ化や認証
- 02AES-GCM または ChaChaPoly による暗号化
- 03メッセージ署名や共有鍵の導出
- 04CryptoKit コードの問題点の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill cryptokitインストール後、Claude Code で/cryptokitを入力して読み込まれたか確認します。
同梱ファイルevals/evals.jsonreferences/cryptokit-patterns.md
詳細
CryptoKit
Apple CryptoKit は、ハッシュ計算、メッセージ認証、対称暗号化、公開鍵署名、鍵共有、HPKE、耐量子鍵カプセル化/署名、Secure Enclave による鍵に対応した、Swift ネイティブの暗号操作 API を提供します。主要なプリミティブのほとんどは iOS 13 以降で利用できます。HPKE(iOS 17 以降)と SHA-3/耐量子 API(iOS 26 以降)の利用可能状況を確認してください。Swift 6.3 以降を対象とする新しい暗号プリミティブのコードでは、CommonCrypto や Security フレームワークの低レベル API より CryptoKit を優先してください。
目次
ハッシュ計算
iOS 13 以降では SHA256/SHA384/SHA512 を使用できます。SHA3_256/SHA3_384/SHA3_512 には iOS 26 以降が必要です。いずれも HashFunction に準拠しています。
一括ハッシュ計算
import CryptoKit
let data = Data("Hello, world!".utf8)
let digest = SHA256.hash(data: data)
let hex = digest.compactMap { String(format: "%02x", $0) }.joined()
SHA-3 の利用可能状況
デプロイメントターゲットが iOS 26 以降でない限り、利用可能状況のチェックを通してのみ SHA-3 を使用してください。
if #available(iOS 26.0, *) {
let digest = SHA3_256.hash(data: data)
}
増分ハッシュ計算
大きなデータやストリーミング入力には、増分的にハッシュを計算します。
var hasher = SHA256()
hasher.update(data: chunk1)
hasher.update(data: chunk2)
let digest = hasher.finalize()
ダイジェストの比較
CryptoKit のダイジェスト値を直接比較してください。セキュリティ上重要な等価性チェックでは、ダイジェストを文字列や配列に変換しないでください。
let expected = SHA256.hash(data: reference)
let actual = SHA256.hash(data: received)
if expected == actual {
// Data integrity verified
}
HMAC
プロトコルで鍵付きメッセージ認証が必要な場合は HMAC を使用してください。シリアライズした値を自分で比較するのではなく、isValidAuthenticationCode で検証してください。
認証コードの計算
let key = SymmetricKey(size: .bits256)
let data = Data("message".utf8)
let mac = HMAC<SHA256>.authenticationCode(for: data, using: key)
認証コードの検証
let isValid = HMAC<SHA256>.isValidAuthenticationCode(
mac, authenticating: data, using: key
)
増分 HMAC
var hmac = HMAC<SHA256>(key: key)
hmac.update(data: chunk1)
hmac.update(data: chunk2)
let mac = hmac.finalize()
対称暗号化
CryptoKit は、AES-GCM と ChaChaPoly の 2 つの認証付き暗号を提供します。どちらも、ノンス、暗号文、認証タグを含むシールドボックスを生成します。
AES-GCM
対称暗号化の既定の選択肢です。Apple シリコンではハードウェアアクセラレーションを利用できます。
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)
ChaChaPoly
AES のハードウェアアクセラレーションが利用できない場合や、ChaCha20-Poly1305 が必須のプロトコル(TLS、WireGuard など)との相互運用時に ChaChaPoly を使用してください。
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)
認証付きデータ
どちらの暗号も、追加認証データ(AAD)に対応しています。AAD は認証されますが、暗号化はされません。改ざんを防ぎつつ、平文のままにしておく必要があるメタデータに役立ちます。
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
)
AES-256-GCM または ChaChaPoly では、既定の SymmetricKey サイズとして .bits256 を使用してください。既存のデータから鍵を作成するには、次のようにします。
let key = SymmetricKey(data: existingKeyData)
公開鍵署名
CryptoKit は、NIST 曲線を使用した ECDSA 署名と、Curve25519 を介した Ed25519 に対応しています。
NIST 曲線:P256、P384、P521
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 と P521 も同じ API を使用します。曲線名を置き換えてください。
NIST 鍵は DER、PEM、X9.63、raw の各表現に対応しています。シリアライズの例については、references/cryptokit-patterns.md を参照してください。
Curve25519/Ed25519
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 鍵で使用できるのは rawRepresentation のみです(DER/PEM/X9.63 には対応していません)。
曲線の選択
| 曲線 | 署名方式 | 鍵のサイズ | 一般的な用途 |
|---|---|---|---|
| P256 | ECDSA | 256 ビット | 汎用。Secure Enclave に対応 |
| P384 | ECDSA | 384 ビット | より高いセキュリティ要件 |
| P521 | ECDSA | 521 ビット | NIST の最高セキュリティレベル |
| Curve25519 | Ed25519 | 256 ビット | 高速。シンプルな API。Secure Enclave には非対応 |
既定では P256 を使用してください。Ed25519 ベースのプロトコルと相互運用する場合は Curve25519 を使用してください。
鍵共有
鍵共有では、ECDH を使用して、2 者がそれぞれの公開鍵/秘密鍵ペアから共有対称鍵を導出できます。
P256 を使用した ECDH
// 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 は、自分の秘密鍵と Alice の公開鍵を使って、同じ sharedSecret を計算します。両者は同じ symmetricKey を導出します。
Curve25519 を使用した ECDH
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
)
鍵導出関数
SharedSecret は SymmetricKey として直接使用できません。必ず以下のいずれかを使って鍵を導出してください。
| 方法 | 標準 | 用途 |
|---|---|---|
hkdfDerivedSymmetricKey | HKDF(RFC 5869) | 推奨されるデフォルト |
x963DerivedSymmetricKey | ANSI X9.63 | X9.63 を使用するシステムとの相互運用 |
導出した鍵を特定のプロトコルコンテキストにひも付けるため、空でない sharedInfo 文字列を必ず指定してください。
HPKE
HPKE は iOS 17 以降で公開鍵暗号化ワークフローに利用できます。受信者の公開鍵で暗号化する場合は、独自実装の ECDH + HKDF + AEAD プロトコルよりも HPKE を優先してください。
let info = Data("my-protocol-v1".utf8)
let recipientKey = Curve25519.KeyAgreement.PrivateKey()
var sender = try HPKE.Sender(
recipientKey: recipientKey.publicKey,
ciphersuite: .Curve25519_SHA256_ChachaPoly,
info: info
)
let encapsulatedKey = sender.encapsulatedKey
let ciphertext = try sender.seal(
plaintext,
authenticating: Data("metadata".utf8)
)
var recipient = try HPKE.Recipient(
privateKey: recipientKey,
ciphersuite: .Curve25519_SHA256_ChachaPoly,
info: info,
encapsulatedKey: encapsulatedKey
)
HPKE.Sender と HPKE.Recipient は状態を保持します。var として保持し、encapsulatedKey を暗号文と一緒に送信してください。また、メッセージは封印した順序と同じ順序で開封してください。暗号スイートの選択とポスト量子 HPKE については、references/cryptokit-patterns.md を参照してください。
ポスト量子 CryptoKit
iOS 26 以降では、量子耐性のある API が追加されます。
- 鍵カプセル化:
MLKEM768、MLKEM1024 - ハイブリッド HPKE:
.XWingMLKEM768X25519_SHA256_AES_GCM_256を使用するXWingMLKEM768X25519 - デジタル署名:
MLDSA65、MLDSA87 - Secure Enclave バリアント:
SecureEnclave.MLKEM768、SecureEnclave.MLKEM1024、SecureEnclave.MLDSA65、SecureEnclave.MLDSA87
従来型と量子耐性の両方への耐性が重要な移行では、ハイブリッド方式を使用してください。公開鍵、暗号文、署名は P256 や Curve25519 よりも大幅に大きくなる点を考慮してください。
Secure Enclave
Secure Enclave は、ハードウェアに裏付けられた鍵ストレージを提供します。秘密鍵がハードウェアの外に出ることはありません。従来型の楕円曲線 CryptoKit では、Secure Enclave は P256 の署名と鍵共有をサポートします。iOS 26 以降の対応ハードウェアでは、CryptoKit は Secure Enclave の ML-KEM 鍵カプセル化型と ML-DSA 署名型も公開します。
利用可能かどうかの確認
guard SecureEnclave.isAvailable else {
// Fall back to software keys
return
}
Secure Enclave 署名鍵の作成
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)
アクセス制御
鍵の使用に生体認証またはパスコード認証が必要な場合は、.privateKeyUsage とともに SecAccessControl を使用してください。Keychain ポリシーの詳細な決定は swift-security ドメインで扱ってください。
Secure Enclave 鍵の永続化
dataRepresentation は、同じデバイスの Secure Enclave でのみ復元できる暗号化されたデータです。Keychain に保存してください。
// Export
let blob = privateKey.dataRepresentation
// Restore
let restored = try SecureEnclave.P256.Signing.PrivateKey(
dataRepresentation: blob
)
Secure Enclave の鍵共有
let seKey = try SecureEnclave.P256.KeyAgreement.PrivateKey()
let peerPublicKey: P256.KeyAgreement.PublicKey = // from peer
let sharedSecret = try seKey.sharedSecretFromKeyAgreement(
with: peerPublicKey
)
よくある間違い
1. 共有秘密を鍵として直接使用する
// DON'T
let badKey = sharedSecret.withUnsafeBytes { bytes in
SymmetricKey(data: Data(bytes))
}
// DO -- derive with HKDF
let goodKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: salt,
sharedInfo: info,
outputByteCount: 32
)
2. nonce を再利用する
// 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)
3. 認証タグの検証を無視する
// DON'T -- manually strip tag and decrypt
// DO -- always use AES.GCM.open() or ChaChaPoly.open()
// which verifies the tag automatically
4. セキュリティ用途に安全でないハッシュを使用する
// 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 と Insecure.SHA1 は、レガシーとの互換性(チェックサム検証、プロトコルの相互運用)のためだけに存在します。新しいセキュリティ上重要な処理では、決して使用しないでください。
5. 対称鍵を UserDefaults に保存する
// DON'T
UserDefaults.standard.set(rawKeyData, forKey: "encryptionKey")
// DO -- store in Keychain
// See references/cryptokit-patterns.md for Keychain storage patterns
6. Secure Enclave の利用可否を確認しない
// 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()
レビューチェックリスト
- CommonCrypto や Security フレームワークの低レベル API ではなく、CryptoKit を使用している
- ハッシュには SHA256 以上を使用している。セキュリティ目的で MD5/SHA1 を使用していない
- HMAC 検証に
isValidAuthenticationCode(定時間処理)を使用している - 対称暗号化に AES-GCM または ChaChaPoly を使用し、256 ビット鍵を使用している
- nonce はランダム(デフォルト)であり、ハードコードも再利用もしていない
- メタデータの完全性が必要な箇所で認証済みデータ(AAD)を使用している
- SharedSecret を直接使用せず、HKDF で導出している
- sharedInfo パラメーターが空ではなく、コンテキストに固有である
- iOS 17 以降で受信者の公開鍵を使って暗号化する場合、独自実装の ECDH+HKDF+AEAD ではなく HPKE を使用している
- SHA-3 とポスト量子 API の利用を iOS 26 以降の利用可能性でガードしている
- 使用前に Secure Enclave の利用可否を確認している
- Secure Enclave 鍵の
dataRepresentationを Keychain に保存している - 秘密鍵を不必要にログ出力、表示、シリアル化していない
- 対称鍵を UserDefaults やファイルではなく Keychain に保存している
- 暗号化に関する輸出コンプライアンス(
ITSAppUsesNonExemptEncryption)を考慮している
参考資料
- 拡張パターン(キーのシリアライズ、Insecure モジュール、Keychain との統合、AES キーラッピング、HPKE):references/cryptokit-patterns.md
- Apple のドキュメント:CryptoKit
- Apple のドキュメント:HPKE
- Apple のドキュメント:量子セキュアなワークフロー
- Apple のサンプル:一般的な暗号化操作の実行
- Apple のサンプル:CryptoKit のキーを Keychain に保存する