mcpField
C
cryptokit

cryptokit

github.com/dpearson2699セキュリティ1.2k3.2k 回インストール13 週間前に更新
リポジトリ

cryptokit について

Swift 開発者向けに、Apple CryptoKit で暗号処理を実装する方法を解説するガイドです。API の使い方や OS の対応状況、安全な Enclave の鍵、よくある問題を扱います。

主な機能

  1. 01ハッシュと HMAC
  2. 02認証付き対称暗号化
  3. 03公開鍵署名と鍵共有
  4. 04HPKE と耐量子 API
  5. 05Secure Enclave 対応鍵
  6. 06暗号コードのチェックリスト

活用シーン

  1. 01データのハッシュ化や認証
  2. 02AES-GCM または ChaChaPoly による暗号化
  3. 03メッセージ署名や共有鍵の導出
  4. 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 には対応していません)。

曲線の選択
曲線署名方式鍵のサイズ一般的な用途
P256ECDSA256 ビット汎用。Secure Enclave に対応
P384ECDSA384 ビットより高いセキュリティ要件
P521ECDSA521 ビットNIST の最高セキュリティレベル
Curve25519Ed25519256 ビット高速。シンプルな 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 として直接使用できません。必ず以下のいずれかを使って鍵を導出してください。

方法標準用途
hkdfDerivedSymmetricKeyHKDF(RFC 5869)推奨されるデフォルト
x963DerivedSymmetricKeyANSI X9.63X9.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)を考慮している

参考資料

コメント