axiom-cryptokit について
iOS 開発者向けの CryptoKit 実践ガイドです。暗号化、署名、鍵交換、Secure Enclave の鍵管理に加え、ポスト量子アルゴリズムとサーバー側 Swift Crypto との相互運用も扱います。
主な機能
- 01AES-GCM による暗号化と復号
- 02デジタル署名の作成と検証
- 03ECDH と HPKE による鍵確立
- 04Secure Enclave のハードウェア鍵管理
- 05ML-KEM と ML-DSA の解説
- 06クロスプラットフォーム署名形式の処理
活用シーン
- 01保存中または転送中のアプリデータの保護
- 02リクエストやペイロードの署名と完全性検証
- 03エンドツーエンド暗号化用の鍵交換
- 04CommonCrypto コードから CryptoKit への移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-cryptokitインストール後、Claude Code で/axiom-cryptokitを入力して読み込まれたか確認します。
詳細
CryptoKit
iOS アプリ向けの認証付き暗号化、デジタル署名、鍵共有、Secure Enclave の鍵管理、量子耐性暗号です。
このスキルを使用する場面
次のような場合に使用します。
- ☑ TLS/Data Protection で提供される保護を超えて、保存データまたは転送中のデータを暗号化する
- ☑ 整合性検証のためにペイロードに署名する(レシート、トークン、API リクエスト)
- ☑ 暗号鍵を生成または管理する(Secure Enclave のハードウェア鍵を含む)
- ☑ CommonCrypto C API から CryptoKit に移行する
- ☑ エンドツーエンド暗号化向けに鍵共有(ECDH)を実装する
- ☑ 最新の公開鍵暗号化に HPKE を使用する
- ☑ 耐量子対応に向けて量子耐性アルゴリズム(ML-KEM、ML-DSA)を採用する
- ☑ サーバーサイドの Swift Crypto または Apple 以外のプラットフォームと相互運用する
プロンプト例
「AES-GCM でユーザーデータを暗号化するにはどうすればよいですか?」 「ペイロードに署名して、サーバーで検証するにはどうすればよいですか?」 「署名鍵を保護するために Secure Enclave を使うにはどうすればよいですか?」 「CommonCrypto を使っていますが、CryptoKit に移行すべきですか?」 「エンドツーエンド暗号化向けの ECDH 鍵共有はどうすればよいですか?」 「アプリを量子耐性にするにはどうすればよいですか?」 「サーバーで iOS アプリの署名を検証できません」 「P256 と Curve25519 の違いは何ですか?」
要注意の兆候
必要以上に複雑、または安全性が低くなっている兆候です。
- ❌ CryptoKit があるのに CommonCrypto C API を使っている — バッファー管理は悪夢のようで、認証付き暗号化はなく、IV の処理も手動です。CryptoKit なら、メモリーセーフな認証付き暗号化を一度の呼び出しで実現できます。呼び出し箇所ごとの移行は数分で済みます。
- ❌ 認証なしで AES-CBC を使っている — 暗号文を改ざんできます。パディングオラクル攻撃では、攻撃者が鍵なしでデータを1バイトずつ復号できます。AES-GCM(CryptoKit のデフォルト)は認証付き暗号化を提供し、改ざんを自動的に検出します。
- ❌ 独自の暗号プロトコルを作っている — タイミング攻撃、パディングオラクル、ノンスの再利用につながります。プロの暗号技術者でない限り、CryptoKit の高水準 API を使用してください。Apple のセキュリティチームは、まさにこうしたミスを防ぐために設計しています。
- ❌ 署名鍵に Secure Enclave を使っていない — 「複雑すぎる」という考えは誤りです。SE API はソフトウェアの P256 とほぼ同じです。SE の鍵は脱獄後も保護されます。鍵が金銭、健康データ、または身元情報を守る場合、追加の手間をかけずに SE を選ぶのが適切です。
- ❌ 追加コストがないのに AES-128 を使っている — 量子コンピューター上では、グローバーのアルゴリズムによって共通鍵の強度が半減します。AES-128 は64ビット相当の安全性になり、AES-256 は128ビット相当を保つため、依然として安全です。性能差は無視できる程度です。
- ❌ 暗号処理を強制アンラップしている —
AES.GCM.openと署名検証は失敗時にエラーをスローします。強制アンラップでは認証の失敗が隠され、セキュリティエラーがクラッシュになります。必ずtry/catchを使用してください。 - ❌ 生の秘密鍵を UserDefaults に保存している — デバイスのバックアップから読み取れ、脱獄したデバイスではアクセスでき、アプリの plist 内で平文として見えてしまいます。ソフトウェア鍵には Keychain、ハードウェアにひも付く鍵には Secure Enclave を使用してください。
本当に CryptoKit が必要ですか?
digraph need_cryptokit {
"What are you\nprotecting?" [shape=diamond];
"Data in transit\nvia HTTPS?" [shape=diamond];
"Data at rest\non device?" [shape=diamond];
"Credentials or\ntokens?" [shape=diamond];
"CloudKit or\niCloud?" [shape=diamond];
"Custom crypto\nneeded?" [shape=diamond];
"Use URLSession + TLS\n(system handles crypto)" [shape=box];
"Use Data Protection\n(.completeFileProtection)" [shape=box];
"Use Keychain\n(axiom-keychain skill)" [shape=box];
"Use CloudKit encryption\n(encryptedValues)" [shape=box];
"YES — Use CryptoKit" [shape=box, style=bold];
"What are you\nprotecting?" -> "Data in transit\nvia HTTPS?" [label="network"];
"What are you\nprotecting?" -> "Data at rest\non device?" [label="storage"];
"What are you\nprotecting?" -> "Credentials or\ntokens?" [label="secrets"];
"What are you\nprotecting?" -> "CloudKit or\niCloud?" [label="sync"];
"What are you\nprotecting?" -> "Custom crypto\nneeded?" [label="signatures, key exchange,\nE2E encryption"];
"Data in transit\nvia HTTPS?" -> "Use URLSession + TLS\n(system handles crypto)" [label="standard HTTPS"];
"Data in transit\nvia HTTPS?" -> "Custom crypto\nneeded?" [label="need E2E beyond TLS"];
"Data at rest\non device?" -> "Use Data Protection\n(.completeFileProtection)" [label="file-level"];
"Data at rest\non device?" -> "Custom crypto\nneeded?" [label="field-level encryption"];
"Credentials or\ntokens?" -> "Use Keychain\n(axiom-keychain skill)" [label="yes"];
"CloudKit or\niCloud?" -> "Use CloudKit encryption\n(encryptedValues)" [label="yes"];
"Custom crypto\nneeded?" -> "YES — Use CryptoKit" [label="yes"];
}
WWDC 2019-709 より:「可能な場合は、より高水準のシステムフレームワークを利用することを強く推奨します。」CryptoKit は、システムフレームワークではユースケースをカバーできない場合に使用します。カスタム署名、フィールド単位の暗号化、鍵共有、Apple 以外のシステムとの相互運用などが該当します。
Secure Enclave の鍵管理
Secure Enclave は、Apple デバイスに組み込まれたハードウェアセキュリティモジュールです。SE で生成された鍵はハードウェアの外に出ることはなく、Apple でさえ取り出せません。
Secure Enclave を使用する場面
| シナリオ | SE を使用するか | 理由 |
|---|---|---|
| API リクエストへの署名 | はい | 鍵をデバイスから取り出せません |
| 生体認証で保護された復号 | はい | SE が鍵を Face ID/Touch ID にひも付けます |
| エンドツーエンド暗号化の鍵 | はい(鍵共有) | 秘密鍵がハードウェアにひも付きます |
| 別のデバイス向けデータの暗号化 | いいえ | 鍵をエクスポートできる必要があります |
| サーバーと共有する共通鍵 | いいえ | SE が対応するのは公開鍵暗号方式のみです(P256、ML-KEM、ML-DSA) |
| デバイス間同期 | いいえ | SE の鍵はデバイスにひも付いています |
生体認証で保護された SE キー生成
import CryptoKit
import LocalAuthentication
guard SecureEnclave.isAvailable else {
let softwareKey = P256.Signing.PrivateKey()
return
}
let context = LAContext()
context.localizedReason = "Authenticate to sign transaction"
guard let accessControl = SecAccessControlCreateWithFlags(
nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.privateKeyUsage, .biometryCurrentSet], nil
) else {
throw CryptoKitError.underlyingCoreCryptoError(error: 0)
}
let seKey = try SecureEnclave.P256.Signing.PrivateKey(
compactRepresentable: false,
accessControl: accessControl,
authenticationContext: .init(context)
)
let signature = try seKey.signature(for: data)
let publicKeyDER = seKey.publicKey.derRepresentation
SE キーのライフサイクル
seKey.dataRepresentation はラップされた blobであり、生のキー素材ではありません。これには、この特定の Secure Enclave ハードウェアのみがラップを解除できる暗号化された参照が含まれています。別のデバイスにエクスポートしても、使用できない blob になります。
- 維持されるもの:アプリのアップデート、デバイスの再起動
- 維持されないもの:アプリの再インストール、デバイスの復元、デバイスの消去、新しいデバイスへの移行
- 永続化:
dataRepresentationを Keychain に保存します。次のように復元します。
let restoredKey = try SecureEnclave.P256.Signing.PrivateKey(
dataRepresentation: savedKeyData, authenticationContext: .init(context)
)
SE の制約
- 従来型:P256 のみ — Curve25519、P384、P521 は使用できません。曲線は 1 つ、用途は 2 つです:
SecureEnclave.P256.SigningとSecureEnclave.P256.KeyAgreement - 耐量子(iOS 26 以降):
SecureEnclave.MLKEM768、SecureEnclave.MLKEM1024、SecureEnclave.MLDSA65、SecureEnclave.MLDSA87 - 署名と鍵共有のみ — 直接暗号化はできません
- シミュレーターには SE がありません —
SecureEnclave.isAvailableは false を返します。テスト用に必ずソフトウェアのフォールバックを用意してください。
一般的なワークフロー
データの完全性をハッシュで検証
import CryptoKit
let data = "Hello, world".data(using: .utf8)!
let digest = SHA256.hash(data: data)
guard digest == SHA256.hash(data: data) else { /* tampering detected */ }
利用可能:SHA256、SHA384、SHA512。Insecure.MD5/Insecure.SHA1 はレガシープロトコルとの互換性のためだけに使用し、セキュリティ目的では決して使用しないでください。
HMAC メッセージ認証
let key = SymmetricKey(size: .bits256)
let mac = HMAC<SHA256>.authenticationCode(for: data, using: key)
let isValid = HMAC<SHA256>.isValidAuthenticationCode(mac, authenticating: data, using: key)
定時間比較が組み込まれているため、タイミング攻撃に対して安全です。両者が対称鍵を共有している場合は HMAC を使用してください。
AES-GCM による暗号化/復号
let key = SymmetricKey(size: .bits256)
let plaintext = "Secret message".data(using: .utf8)!
let sealedBox = try AES.GCM.seal(plaintext, using: key)
let combined = sealedBox.combined!
let restoredBox = try AES.GCM.SealedBox(combined: combined)
let decrypted = try AES.GCM.open(restoredBox, using: key)
認証付き暗号化のため、改ざんがあると CryptoKitError.authenticationFailure が発生します。別途 HMAC を実行する必要はありません。Nonce は自動生成され、combined の先頭に付加されます。
ECDSA の署名/検証
let privateKey = P256.Signing.PrivateKey()
let signature = try privateKey.signature(for: data)
let isValid = privateKey.publicKey.isValidSignature(signature, for: data)
曲線:P256(secp256r1、最も一般的)、P384、P521、Curve25519(Ed25519、最速)。サーバーとの相互運用には P256 を使用してください。両側を自分で管理する場合は Curve25519 を使用してください。
ECDH 鍵共有
let alicePrivate = P256.KeyAgreement.PrivateKey()
let bobPrivate = P256.KeyAgreement.PrivateKey()
let sharedSecret = try alicePrivate.sharedSecretFromKeyAgreement(
with: bobPrivate.publicKey
)
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: "my-app-v1".data(using: .utf8)!,
sharedInfo: Data(),
outputByteCount: 32
)
生の共有秘密をそのまま使用しないでください。必ず HKDF で対称鍵を導出してください。生の秘密には偏ったビットが含まれ、暗号化が弱まります。
HPKE エンドツーエンド暗号化
HPKE(ハイブリッド公開鍵暗号)は、鍵共有と対称暗号化を 1 つの手順で組み合わせます。新しいプロトコルでは、手動の ECDH + AES-GCM よりも推奨されます。従来型の暗号スイート(P256、Curve25519)は iOS 17 以降で利用できます。量子安全な XWing 暗号スイートと ML-KEM/ML-DSA には iOS 26 以降が必要です。
let recipientPrivate = P256.KeyAgreement.PrivateKey()
var sender = try HPKE.Sender(
recipientKey: recipientPrivate.publicKey,
ciphersuite: .P256_SHA256_AES_GCM_256,
info: "my-app-message-v1".data(using: .utf8)!
)
let ciphertext = try sender.seal(plaintext)
var recipient = try HPKE.Recipient(
privateKey: recipientPrivate,
ciphersuite: .P256_SHA256_AES_GCM_256,
info: "my-app-message-v1".data(using: .utf8)!,
encapsulatedKey: sender.encapsulatedKey
)
let decrypted = try recipient.open(ciphertext)
量子安全な方式への移行
WWDC 2025-314 で示されたとおり、「今すぐ収集し、後で復号する」脅威は理論上の話ではありません。国家主体の攻撃者は、将来の量子コンピューターで復号するため、現在すでに暗号化された通信を記録しています。機密性を 10 年以上保つ必要があるデータは、今すぐ耐量子保護が必要です。
移行の優先順位
- 量子安全な TLS(自動) — iOS 26 の URLSession と Network.framework は、デフォルトで量子安全な TLS を使用します。コードの変更は不要です。iMessage は iOS 17.4 以降、PQ3 を使用しています。
- カスタムプロトコルの暗号化 — アプリが手動の ECDH + AES またはカスタムの鍵交換を使用している場合、それらのチャネルは自動的にアップグレードされません。耐量子 HPKE に置き換えてください。
- 対称鍵のアップグレード — AES-128 を AES-256 にアップグレードしてください。グローバーのアルゴリズムは対称鍵の強度を半減させるため、256 ビット鍵で 128 ビット相当の耐量子セキュリティを実現できます。
耐量子 HPKE(iOS 26 以降)
同じ HPKE API で、暗号スイートが異なります。ML-KEM(量子安全)と X25519(従来型)を組み合わせてハイブリッドセキュリティを実現します。これは Apple の推奨方式です。
let recipientPrivate = XWingMLKEM768X25519.PrivateKey()
var sender = try HPKE.Sender(
recipientKey: recipientPrivate.publicKey,
ciphersuite: .XWingMLKEM768X25519_SHA256_AES_GCM_256,
info: "my-app-pq-v1".data(using: .utf8)!
)
let ciphertext = try sender.seal(plaintext)
var recipient = try HPKE.Recipient(
privateKey: recipientPrivate,
ciphersuite: .XWingMLKEM768X25519_SHA256_AES_GCM_256,
info: "my-app-pq-v1".data(using: .utf8)!,
encapsulatedKey: sender.encapsulatedKey
)
let decrypted = try recipient.open(ciphertext)
ハイブリッド構成(従来型 + 耐量子)により、いずれか一方のアルゴリズムが破られてもセキュリティが保たれます。XWing 構成では ML-KEM768 と X25519 を組み合わせているため、従来型の攻撃だけでも量子攻撃だけでも鍵交換は侵害されません。
ML-KEM と ML-DSA の直接利用(iOS 26 以降)
let kemPrivate = try MLKEM768.PrivateKey()
let (sharedSecret, encapsulation) = try kemPrivate.publicKey.encapsulate()
let derivedSecret = try kemPrivate.decapsulate(encapsulation)
let dsaPrivate = try MLDSA65.PrivateKey()
let signature = try dsaPrivate.signature(for: data)
let isValid = dsaPrivate.publicKey.isValidSignature(signature, for: data)
カスタムプロトコルを構築する際の鍵カプセル化には ML-KEM を使用してください。量子解析に耐えられない P256/Ed25519 の署名を置き換える場合は ML-DSA を使用してください。ほとんどのアプリケーションでは、生の ML-KEM よりハイブリッド暗号スイートを使用した HPKE を優先してください。
クロスプラットフォームに関する考慮事項
Swift Crypto の互換性
Linux/サーバーでは、import Crypto(apple/swift-crypto)が Secure Enclave と Keychain を除く同じ API を提供します。コードを共有する場合:
#if canImport(CryptoKit)
import CryptoKit
#else
import Crypto
#endif
署名エンコーディングの相互運用性
CryptoKit の ECDSA 署名は生形式(r || s の連結、P256 では 64 バイト)を使用します。Apple 以外のほとんどのプラットフォーム(OpenSSL、Java、.NET)では、DER 形式(ASN.1 エンコード、P256 では可変長の 70~72 バイト)が必要です。
let signature = try privateKey.signature(for: data)
let raw = signature.rawRepresentation
let der = signature.derRepresentation
サーバーから署名を受信する場合:
let fromDER = try P256.Signing.ECDSASignature(derRepresentation: serverDER)
let fromRaw = try P256.Signing.ECDSASignature(rawRepresentation: serverRaw)
サーバー検証用の鍵のエクスポート
let publicKey = privateKey.publicKey
publicKey.derRepresentation
publicKey.pemRepresentation
publicKey.x963Representation
ほとんどのプラットフォームでは DER、設定ファイルや REST API では PEM、コンパクトな JavaScript との相互運用では X9.63 を使用します。
secp256r1 と secp256k1
CryptoKit は、TLS、政府システム、エンタープライズソフトウェアで使われる NIST 標準曲線のsecp256r1(P-256、prime256v1)を使用します。
Bitcoin と Ethereum はsecp256k1(Koblitz 曲線)を使用します。これはまったく別の曲線です。相互運用はできません。P-256 署名は secp256k1 検証器では検証できません。ブロックチェーンとの相互運用に secp256k1 が必要な場合は、CryptoKit ではなく専用ライブラリ(libsecp256k1 ラッパー)を使用してください。
SE 鍵とクロスプラットフォーム
Secure Enclave の鍵はデバイスに紐付けられています。dataRepresentation はラップされた blob であり、元のハードウェアでしかアンラップできません。つまり、SE 鍵を使えば設計上、暗号シュレッディングが可能です。デバイスの破棄やアプリの再インストールにより、SE で暗号化されたデータはすべて永久に復元できなくなります。それに応じて鍵のライフサイクルを計画してください。データをデバイスの紛失後も保持する必要がある場合は、復旧手段(サーバーにエスクローされたバックアップ鍵、複数デバイスへの鍵配布)を用意してください。
不合理な正当化
| 正当化 | 失敗する理由 | 時間コスト |
|---|---|---|
| 「CommonCrypto で問題なく動いているので、移行する必要はない」 | バッファーオーバーフロー、認証付き暗号化の欠如、IV の手動管理が問題になります。バッファーサイズを一つ間違えるだけで、気づかないままデータが破損したり、セキュリティ上の脆弱性が生じたりします。 | CryptoKit なら設計上防げる微妙な暗号化の不具合のデバッグに 2~4 時間 |
| 「AES-CBC への認証の追加は後でやる」 | 認証がなければ、暗号文は改ざん可能です。攻撃者は検知されずにデータを変更できます。「後で」とは、脆弱性を含む状態でリリースした後という意味です。 | 本番環境で改ざんされたデータが見つかった際のインシデント対応に 4~8 時間 |
| 「自分の用途では nonce をランダムにする必要はない」 | AES-GCM で nonce を再利用すると、暗号文の XOR から平文が漏えいします。GCM で固定 nonce を安全に使える用途はありません。 | 壊滅的です。nonce を再利用して暗号化したすべてのメッセージの平文が完全に復元されます |
| 「Secure Enclave はこの用途には過剰だ」 | ソフトウェア鍵は、脱獄されたデバイスやバックアップから抽出できます。SE 鍵は抽出できません。鍵が資金、健康データ、または本人確認情報を保護する場合、SE は過剰ではありません。 | 開発時間の追加コストはありません(API はソフトウェアの P256 とほぼ同じです) |
| 「量子コンピューターの実用化は何十年も先だ」 | 今すぐ収集し後で復号する攻撃により、今日記録されたデータは量子コンピューターが登場した時点で復号されます。Apple はすでに iOS 26 で量子耐性 TLS を提供しています。 | iOS 26 のデフォルトを使えば追加コストはありません。独自プロトコルの移行には 1~2 時間かかります。 |
| 「独自の暗号方式を使えばいい」 | プロの暗号技術者は、プロトコルの設計に何年も費やします。タイミングサイドチャネル、パディングオラクル、nonce の誤用は、専門家によるレビューなしでは見抜けません。 | セキュリティ監査と修正に数週間から数か月 |
| 「DER と生形式の違いは重要ではない。どちらも同じ署名だ」 | エンコーディングが間違っていると、サーバーでの検証に失敗します。計算は正しくても、エンコーディングが間違っています。 | .derRepresentation を一度呼び出せば防げる相互運用の問題のデバッグに 2~4 時間 |
プレッシャーのかかる状況
シナリオ 1:「とにかく何でもいいから暗号化して、今日リリースしよう」
背景:機能の期限が迫っています。開発者は、ユーザーデータを永続化する前に暗号化する必要があります。
プレッシャー:「暗号化方式について考えすぎないでください。AES-CBCでもCommonCryptoでも、今日中にできるものなら何でもいいです。」
実情:認証なしのAES-CBCは、パディングオラクル攻撃に対して脆弱です。CommonCryptoではバッファーを手動で管理する必要があり、サイズを一つ間違えるだけで、気づかないままデータが破損します。CCCryptからAES.GCM.sealへの移行は5〜10行のコードで済み、CommonCrypto版よりコードが少ないうえ、脆弱性の一種を丸ごと排除できます。
適切な対応:CCCrypt(kCCEncrypt, kCCAlgorithmAES, ...)をAES.GCM.seal(plaintext, using: key)に置き換えます。既存の鍵素材はSymmetricKey(data:)にマッピングします。既存のCBC暗号化データを移行する場合は、旧形式を復号し、初回アクセス時に再暗号化する読み取り処理を追加します。
反論の例:「CryptoKit経由のAES-GCMは、実際にはCommonCryptoよりコードが少なく、認証付き暗号化も標準で提供します。CBCのコードには、GCMなら排除できる脆弱性の一種があります。切り替えにかかるのは10分で、何時間もかかりません。」
シナリオ 2:「クロスプラットフォーム対応が必要なので、Secure Enclaveは使わない」
背景:iOSとAndroidの両方で動作するE2E暗号化機能を開発しています。開発者は、Androidのソフトウェアキーストアと仕様を揃えるため、署名鍵をKeychain(ソフトウェア)に保存することを提案しています。
プレッシャー:「Androidには、同じ保証を提供するSecure Enclave相当のものがありません。シンプルにして、プラットフォーム間で統一しましょう。」
実情:Androidには、同様の保証を持つハードウェアベースのKeystore(StrongBox)があります。Android側でソフトウェア鍵を使うとしても、iOS側もそうする必要はありません。iOSではSEによる保護を追加費用なしで利用できます。APIはソフトウェアのP256とほぼ同じです。iOSではSE、AndroidではハードウェアKeystoreを使い、ハードウェアが利用できない場合はソフトウェアにフォールバックします。
適切な対応:SecureEnclave.P256.Signing.PrivateKeyを使い、P256.Signing.PrivateKeyにフォールバックします。公開鍵と署名の形式は同じなので、サーバーはそれらがどちらで生成されたかを知る必要も、気にする必要もありません。
反論の例:「SEのAPIはソフトウェアのP256と同じなので、複雑さは増えません。どちらの場合も、サーバーが検証する公開鍵の形式は同じです。対応デバイスではハードウェア保護を利用でき、非対応デバイスではスムーズにフォールバックできます。どちらのプラットフォームでも、利用可能な最善のハードウェアを使えます。」
シナリオ 3:「耐量子暗号は時期尚早な最適化です」
背景:新しいE2E暗号化メッセージングプロトコルを設計しています。開発者は、従来型のECDHとAES-GCMの組み合わせを提案しています。
プレッシャー:「量子コンピューターの実用化は少なくとも10年先です。これは過剰設計です。」
実情:ハーベスト・ナウ、ディクリプト・レイター(今収集し、後で復号する)攻撃では、攻撃者は今日、暗号化された通信を記録し、量子コンピューターが登場した後に復号します。セッショントークンのような一時的なデータには、従来型の暗号で問題ありません。医療記録、金融データ、私的な通信など、長期にわたって機密性を保つ必要のあるメッセージには、今すぐ耐量子保護を適用するのが妥当です。Appleはすでに、iMessage向けのPQ3と、iOS 26の量子耐性TLSを提供しています。
適切な対応:.P256_SHA256_AES_GCM_256の代わりに、HPKE.Ciphersuite.XWingMLKEM768X25519_SHA256_AES_GCM_256を使います。コードの変更は、暗号スイート定数を一つ置き換えるだけです。APIも複雑さもそのままで、従来型暗号と耐量子暗号を組み合わせた保護を得られます。
反論の例:「コードの変更は、文字どおり暗号スイート定数を一つ置き換えるだけです。Appleはすでに、iMessage(PQ3)とiOS 26のTLSで対応しています。コードを1行変えるだけで、10年後も機密性が必要なデータに対するハーベスト・ナウ、ディクリプト・レイターのリスクを取り除けます。」
チェックリスト
独自の暗号化をリリースする前に、以下を確認します。
アルゴリズムの選択:
- 認証付き暗号化(AES-CBC/ECBではなくAES-GCM)を使用している
- セキュリティ上の用途にはSHA256以上のハッシュ関数を使用している(MD5/SHA1ではない)
- 用途に適した曲線を選択している(相互運用性にはP256、パフォーマンスにはCurve25519)
- 長期にわたり機密性が必要なデータについて、耐量子暗号スイートを検討している
鍵管理:
- 秘密鍵をSecure Enclaveに保存している(ハードウェアが利用可能で、鍵のエクスポートが不要な場合)
- SEが利用できない場合は、適切なアクセス制御を設定してKeychainにフォールバックする
- UserDefaults、ファイル、ソースコードへのハードコードで秘密鍵を保存していない
- 長期間使用する鍵のローテーション戦略を定義している
ノンス/IVの処理:
- CryptoKitの自動ノンス生成を使用している(独自実装ではない)
- 暗号化をまたいでノンスを再利用していない
- ノンスを保存したり、ハードコードしたりしていない
相互運用性(Apple以外のプラットフォームと通信する場合):
- 署名のエンコーディングがサーバーの想定と一致している(DERまたはraw)
- 公開鍵の形式について合意している(DER、PEM、またはX9.63)
- 曲線がサーバーと一致している(secp256k1ではなくsecp256r1)
- ローカルでの往復テストだけでなく、実際のサーバー検証でテストしている
Secure Enclave(使用する場合):
- ソフトウェアへのフォールバックを用意して
SecureEnclave.isAvailableを確認している - 永続化のために
dataRepresentationをKeychainに保存している - アクセス制御フラグがUXに合っている(使用ごとの生体認証か、セッション単位か)
- 鍵のライフサイクルを文書化している(再インストール/復元後は保持されない)
リソース
WWDC:2019-709、2025-314
ドキュメント:/cryptokit、/cryptokit/secureenclave、/security/certificate_key_and_trust_services
スキル:axiom-cryptokit-ref、axiom-keychain