π Mastering CryptoKit
From encryption to signatures! Fully protect your data with Apple's modern cryptography framework.
β Difficulty: ββ
β±οΈ Est. Time: 1-2h
π System & Network
β¨ CryptoKit is?
CryptoKit is Apple's Swift-native cryptography framework. It lets you implement hashing, symmetric encryption, public-key encryption, and digital signatures concisely and safely. Available on iOS 13+, all cryptographic operations are processed in the Secure Enclave.
π¦ Key Features
Feature Overview
β
ν΄μ± (SHA-256, SHA-384, SHA-512)
β
λμΉν€ μνΈν (AES-GCM, ChaCha20-Poly1305)
β
곡κ°ν€ μνΈν (Curve25519, P-256, P-384, P-521)
β
λμ§νΈ μλͺ
(ECDSA, EdDSA)
β
ν€ κ΅ν (ECDH)
β
HKDF (ν€ μ λ)
β
HMAC (λ©μμ§ μΈμ¦ μ½λ)π Hashing
Hashing.swift
import CryptoKit import Foundation // SHA-256 ν΄μ± func hashData(_ input: String) -> String { let data = Data(input.utf8) let hash = SHA256.hash(data: data) // Hex λ¬Έμμ΄λ‘ λ³ν return hash.compactMap { String(format: "%02x", $0) }.joined() } // Usage Example let password = "mySecurePassword123" let hashed = hashData(password) print(hashed) // "b3d2f8c5..." (64μ hex λ¬Έμμ΄) // SHA-512 ν΄μ± func hashSHA512(_ input: String) -> String { let data = Data(input.utf8) let hash = SHA512.hash(data: data) return hash.description // or hex λ³ν } // File ν΄μ± func hashFile(at url: URL) throws -> String { let data = try Data(contentsOf: url) let hash = SHA256.hash(data: data) return hash.compactMap { String(format: "%02x", $0) }.joined() }
π Symmetric Encryption (AES-GCM)
SymmetricEncryption.swift
import CryptoKit // AES-GCM μνΈν func encryptData(_ plaintext: String, key: SymmetricKey) throws -> Data { let data = Data(plaintext.utf8) // AES-GCM μνΈν (μΈμ¦ νκ·Έ ν¬ν¨) let sealedBox = try AES.GCM.seal(data, using: key) // combined: nonce + ciphertext + tag return sealedBox.combined! } func decryptData(_ encrypted: Data, key: SymmetricKey) throws -> String { // SealedBox 볡μ let sealedBox = try AES.GCM.SealedBox(combined: encrypted) // 볡νΈν let decryptedData = try AES.GCM.open(sealedBox, using: key) return String(data: decryptedData, encoding: .utf8)! } // Usage Example let key = SymmetricKey(size: .bits256) // λλ€ ν€ μμ± let message = "Secret message" let encrypted = try encryptData(message, key: key) let decrypted = try decryptData(encrypted, key: key) print(decrypted) // "Secret message"
π Key Storage & Management
KeyManagement.swift
import CryptoKit import Foundation // ν€λ₯Ό Keychainμ μ μ₯ func saveKey(_ key: SymmetricKey, tag: String) throws { let keyData = key.withUnsafeBytes { Data($0) } let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: tag, kSecValueData as String: keyData ] let status = SecItemAdd(query as CFDictionary, nil) guard status == errSecSuccess else { throw NSError(domain: "KeychainError", code: Int(status)) } } func loadKey(tag: String) throws -> SymmetricKey { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: tag, kSecReturnData as String: true ] var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) guard status == errSecSuccess, let keyData = item as? Data else { throw NSError(domain: "KeychainError", code: Int(status)) } return SymmetricKey(data: keyData) } // Usage Example let key = SymmetricKey(size: .bits256) try saveKey(key, tag: "MyEncryptionKey") // λμ€μ ν€ λΆλ¬μ€κΈ° let loadedKey = try loadKey(tag: "MyEncryptionKey")
π Public Key Encryption (Curve25519)
PublicKeyEncryption.swift
import CryptoKit // ν€ μ μμ± let alicePrivateKey = Curve25519.KeyAgreement.PrivateKey() let alicePublicKey = alicePrivateKey.publicKey let bobPrivateKey = Curve25519.KeyAgreement.PrivateKey() let bobPublicKey = bobPrivateKey.publicKey // ECDH ν€ κ΅ν let aliceSharedSecret = try alicePrivateKey.sharedSecretFromKeyAgreement(with: bobPublicKey) let bobSharedSecret = try bobPrivateKey.sharedSecretFromKeyAgreement(with: alicePublicKey) // 곡μ λΉλ°μμ λμΉν€ μμ± let aliceSymmetricKey = aliceSharedSecret.hkdfDerivedSymmetricKey( using: SHA256.self, salt: Data(), sharedInfo: Data("Encryption Key".utf8), outputByteCount: 32 ) let bobSymmetricKey = bobSharedSecret.hkdfDerivedSymmetricKey( using: SHA256.self, salt: Data(), sharedInfo: Data("Encryption Key".utf8), outputByteCount: 32 ) // μ΄μ Aliceμ Bobμ like λμΉν€λ₯Ό κ°μ§ let message = "Secret message" let encrypted = try encryptData(message, key: aliceSymmetricKey) let decrypted = try decryptData(encrypted, key: bobSymmetricKey) print(decrypted) // "Secret message"
βοΈ Digital Signatures (EdDSA)
DigitalSignature.swift
import CryptoKit // μλͺ ν€ μμ± let signingKey = Curve25519.Signing.PrivateKey() let publicKey = signingKey.publicKey // Data μλͺ func signData(_ data: Data, with key: Curve25519.Signing.PrivateKey) throws -> Data { let signature = try key.signature(for: data) return Data(signature) } // μλͺ κ²μ¦ func verifySignature( _ signature: Data, for data: Data, publicKey: Curve25519.Signing.PublicKey ) -> Bool { do { return try publicKey.isValidSignature(signature, for: data) } catch { return false } } // Usage Example let document = Data("μ€μν λ¬Έμ".utf8) // μλͺ μμ± let signature = try signData(document, with: signingKey) // μλͺ κ²μ¦ let isValid = verifySignature(signature, for: document, publicKey: publicKey) print("μλͺ μ ν¨: \(isValid)") // true // λ¬Έμ λ³μ‘° μ let tamperedDoc = Data("λ³μ‘°λ λ¬Έμ".utf8) let isValid2 = verifySignature(signature, for: tamperedDoc, publicKey: publicKey) print("μλͺ μ ν¨: \(isValid2)") // false
π± SwiftUI Integration
EncryptionView.swift
import SwiftUI import CryptoKit struct EncryptionDemoView: View { @State private var plaintext = "" @State private var encrypted = "" @State private var decrypted = "" let key = SymmetricKey(size: .bits256) var body: some View { VStack(spacing: 20) { Text("AES-GCM μνΈν") .font(.title) .bold() TextField("μνΈνν ν μ€νΈ", text: $plaintext) .textFieldStyle(.roundedBorder) Button("μνΈν") { encrypt() } .buttonStyle(.borderedProminent) if !encrypted.isEmpty { VStack(alignment: .leading, spacing: 8) { Text("μνΈνλ λ°μ΄ν°:") .font(.headline) Text(encrypted) .font(.system(.caption, design: .monospaced)) .foregroundStyle(.secondary) .textSelection(.enabled) } .padding() .background(.gray.opacity(0.1)) .cornerRadius(8) Button("볡νΈν") { decrypt() } .buttonStyle(.bordered) if !decrypted.isEmpty { Text("볡νΈν κ²°κ³Ό: \(decrypted)") .font(.headline) .foregroundStyle(.green) } } } .padding() } func encrypt() { guard !plaintext.isEmpty else { return } do { let data = Data(plaintext.utf8) let sealedBox = try AES.GCM.seal(data, using: key) encrypted = sealedBox.combined!.base64EncodedString() } catch { encrypted = "μνΈν μ€ν¨: \(error)" } } func decrypt() { guard let data = Data(base64Encoded: encrypted) else { return } do { let sealedBox = try AES.GCM.SealedBox(combined: data) let decryptedData = try AES.GCM.open(sealedBox, using: key) decrypted = String(data: decryptedData, encoding: .utf8) ?? "μ€λ₯" } catch { decrypted = "볡νΈν μ€ν¨" } } }
π Practical Example: Secure Notes App
SecureNote.swift
import SwiftUI import CryptoKit // μνΈνλ λ ΈνΈ λͺ¨λΈ struct SecureNote: Identifiable, Codable { let id: UUID let encryptedContent: Data let createdAt: Date } class SecureNoteManager { private let key: SymmetricKey init() { // App μμ μ ν€ λ‘λ or μμ± if let savedKey = try? loadKey(tag: "SecureNoteKey") { key = savedKey } else { key = SymmetricKey(size: .bits256) try? saveKey(key, tag: "SecureNoteKey") } } func createNote(_ content: String) throws -> SecureNote { let data = Data(content.utf8) let sealedBox = try AES.GCM.seal(data, using: key) return SecureNote( id: UUID(), encryptedContent: sealedBox.combined!, createdAt: Date() ) } func decryptNote(_ note: SecureNote) throws -> String { let sealedBox = try AES.GCM.SealedBox(combined: note.encryptedContent) let decryptedData = try AES.GCM.open(sealedBox, using: key) return String(data: decryptedData, encoding: .utf8) ?? "" } } // SwiftUI View struct SecureNoteView: View { @State private var notes: [SecureNote] = [] @State private var newNoteContent = "" @State private var selectedNote: SecureNote? let manager = SecureNoteManager() var body: some View { NavigationStack { List { Section { TextField("μ λ ΈνΈ λ΄μ©", text: $newNoteContent) Button("Save") { saveNote() } .disabled(newNoteContent.isEmpty) } Section("μνΈνλ λ ΈνΈ") { ForEach(notes) { note in Button("λ ΈνΈ \(note.id.uuidString.prefix(8))...") { selectedNote = note } } } } .navigationTitle("μμ ν λ ΈνΈ") .sheet(item: $selectedNote) { note in NoteDetailView(note: note, manager: manager) } } } func saveNote() { do { let note = try manager.createNote(newNoteContent) notes.append(note) newNoteContent = "" } catch { print("μνΈν μ€ν¨: \(error)") } } } struct NoteDetailView: View { let note: SecureNote let manager: SecureNoteManager var body: some View { VStack { if let content = try? manager.decryptNote(note) { Text(content) .padding() } else { Text("볡νΈν μ€ν¨") .foregroundStyle(.red) } } } }
π P-256 (NIST Standard Curve)
P256Example.swift
import CryptoKit // P-256 ECDSA μλͺ let privateKey = P256.Signing.PrivateKey() let publicKey = privateKey.publicKey let data = Data("μ€μν λ°μ΄ν°".utf8) // μλͺ μμ± let signature = try privateKey.signature(for: data) // μλͺ κ²μ¦ let isValid = publicKey.isValidSignature(signature, for: data) print("P-256 μλͺ μ ν¨: \(isValid)") // P-256 ν€ κ΅ν let aliceKey = P256.KeyAgreement.PrivateKey() let bobKey = P256.KeyAgreement.PrivateKey() let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(with: bobKey.publicKey) let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey( using: SHA256.self, salt: Data(), sharedInfo: Data(), outputByteCount: 32 )
π ChaCha20-Poly1305 Encryption
ChaCha20.swift
import CryptoKit // ChaCha20-Poly1305 (AES λμ, λͺ¨λ°μΌμμ λ λΉ λ¦) func encryptWithChaCha(_ plaintext: String, key: SymmetricKey) throws -> Data { let data = Data(plaintext.utf8) let sealedBox = try ChaChaPoly.seal(data, using: key) return sealedBox.combined } func decryptWithChaCha(_ encrypted: Data, key: SymmetricKey) throws -> String { let sealedBox = try ChaChaPoly.SealedBox(combined: encrypted) let decryptedData = try ChaChaPoly.open(sealedBox, using: key) return String(data: decryptedData, encoding: .utf8)! } // Usage Example let key = SymmetricKey(size: .bits256) let encrypted = try encryptWithChaCha("Secret message", key: key) let decrypted = try decryptWithChaCha(encrypted, key: key)
π‘ HIG Guidelines
Security Best Practices
β
DO
1. μνΈν ν€λ Keychainμ μ μ₯
2. λμΉν€λ 256λΉνΈ μ¬μ© (.bits256)
3. μλͺ
κ²μ¦ νμ (λ¬΄κ²°μ± νμΈ)
4. λ―Όκ°ν λ°μ΄ν°λ λ©λͺ¨λ¦¬μμ μ¦μ μ κ±°
5. SHA-256 μ΄μ μ¬μ© (SHA-1 κΈμ§)
β DON'T
1. νλμ½λ©λ μνΈν ν€
2. UserDefaultsμ ν€ μ μ₯
3. 컀μ€ν
μνΈν μκ³ λ¦¬μ¦ κ΅¬ν
4. νλ¬ΈμΌλ‘ λ€νΈμν¬ μ μ‘
5. λ‘κ·Έμ λ―Όκ°ν λ°μ΄ν° μΆλ ₯π§ Practical Tips
Real-World Patterns
import CryptoKit // 1. HMACμΌλ‘ λ©μμ§ μΈμ¦ func authenticateMessage(_ message: String, key: SymmetricKey) -> Data { let data = Data(message.utf8) let authCode = HMAC<SHA256>.authenticationCode(for: data, using: key) return Data(authCode) } // 2. Passwordμμ ν€ μμ± (PBKDF2 λμ Argon2 κΆμ₯) func deriveKey(from password: String, salt: Data) -> SymmetricKey { let passwordData = Data(password.utf8) let hash = SHA256.hash(data: passwordData + salt) return SymmetricKey(data: hash) } // 3. νμΌ μνΈν func encryptFile(at url: URL, key: SymmetricKey) throws { let data = try Data(contentsOf: url) let sealedBox = try AES.GCM.seal(data, using: key) try sealedBox.combined!.write(to: url.appendingPathExtension("encrypted")) } // 4. 곡κ°ν€λ₯Ό Dataλ‘ λ³ν (μ μ‘μ©) let privateKey = Curve25519.Signing.PrivateKey() let publicKeyData = privateKey.publicKey.rawRepresentation // Dataμμ 곡κ°ν€ 볡μ let restoredPublicKey = try Curve25519.Signing.PublicKey(rawRepresentation: publicKeyData)
π‘ CryptoKit ν΅μ¬
β
Swift λ€μ΄ν°λΈ μνΈν νλ μμν¬
β
Secure Enclave νμ©
β
AES-GCM, ChaCha20-Poly1305 Supported
β
Curve25519, P-256 곡κ°ν€ μνΈν
β
λμ§νΈ μλͺ
λ° ν€ κ΅ν