사용자의 개인 정보와 비즈니스의 중요 정보를 보호하는 것이 필요하다. → 특히 애플이 중요하게 생각하는 부분이기도 하다.
암호화는 그 도구다.
암호화는 어렵다.
리스크가 크고, 많은 노력과 기술, 전문성을 필요로 한다.
그래서 네이티브로 솔루션을 제공하면, 앱의 로직에 집중할 수 있다.
디바이스의 데이터 보호
do {
try data.write(to: fileURL, options: .completeFileProtection)
}
catch {
}
인증서와 키 보호
디바이스 혹은 유저 간의 데이터 공유
// url로 지정된 파일의 데이터를 사용자 클라우드 공간에 저장
let asset = CKAsset(fileURL: ...)
let record: CKRecord = ...
record["AssetField"] = asset
let database = CKContainer.default().privateCloudDatabase
database.save(record) { ... }
네트워크 연결 보호
// Network.framework
let conn = NWConnection(host: "imap.mail.me.com", port: .imaps, using: .tls)
conn.start(queue: .main)
// URLSession
let url = URL(string: "<https://www.apple.com>")
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
// 에러 핸들링
}
// 데이터 처리
}
task.resume()
외부 접속에 대한 보호
SecTrustEvaluateAsyncWithError(trust, queue) { (trust, success, error) in
if success {
let publicKey = SecTrustCopyPublicKey(trust)
// key 사용
} else {
// handle errors
}
}
요약: 최대한 시스템 기능을 사용하라
그럼에도 그 이상이 필요할 수 있다.
그래서 만들었다. Apple CryptoKit
특징
과거에는 어떻게 했는가? → C기반 API를 썼어야 되서 복잡했다.
let status = cipherText.withUnsafeMutableBytes { (cipherPtr: UnsafeMutableRawBufferPointer) in
tag.withUnsafeMuableBytes { tagPtr: (UnsafeMutableRawBufferPointer) in
nonceData.withUnsafeBytes { noncePtr: UnsafeRawBufferPointer) in
key.withUnsafeBytes { keyPtr: UnsafeRawBufferPointer in
plainText.withUnsafeBytes { plainText: UnsafeRawBufferPointer in
return encrypt(keyPtr, plainTextPtr, noncePtr, keyPtr, cipherPtr, tagPtr)
}
}
}
}
}
// 위와 동일
let sealed = try AES.GCM.seal(dataToEncrypt, using: symmectricKey)
// 암호화 키 만들고 해제하기
//C
let keyByteCount = 256/8
var key = Array(repeating: 0, count: keyByteCount)
let err = SecRandomCopyBytes(kSecRandomDefault, keyByteCount, &key)
if err != errSecSuccess {
// 에러 처리
}
// 키 사용
...
// 키를 메모리에서 제거(zeroize)
memset_s(&key, keyByteCount, 0, keyByteCount)
// 위와 동일
// 이 객체가 해제될 때, 자동으로 zeroize가 된다.
let key = SymmetricKey(size: .bits256)
CryptoKit이 Swift여서 좋은 점
알고리즘
표준적이고 상호리뷰가 완료된 알고리즘 사용
해시 함수: 결정론적인 고정 사이즈 digest생성
// 한꺼번에 해싱
let audioData = FileManager.default.contents(atPath: filePath)!
let digest = SHA256.hash(data: audioData)
// 스트림 방식
var hasher = SHA256()
let fileStream = InputStream(fileAtPath: filePath)!
fileStream.open()
let bufferSize = 64000
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
while fileStream.hasBytesAvailable {
let read = fileStream.read(buffer, maxLength: bufferSize)
let bufferPointer = UnsafeRawBufferPointer(start: buffer, count: read)
hasher.update(bufferPointer: bufferPointer)
}
let digest = hasher.finalize()
Authentication과 encryption을 모두 제공
let key = SymmetricKey(data: keyData)
// 암호화된 데이터를 담아둘 박스 생성
guard let sealedBox = ChaChaPoly.SealedBox(combined: downloadedData) else {
throw MapDownloaderError.invalidDownload
}
// 인증 + 암호화
let mapData = try ChaChaPoly.open(sealedBox, using: key)
서명
let privateKey = P256.Signing.PrivateKey()
let publicKey = privateKey.publicKey.compactRepresentation!
// privateKey는 키체인에, publicKey는 서버에 저장
// 컨텐츠에 사인
let signature = try privateKey.signature(for: transactionData)
Secure Enclave: 하드웨어 기반 키 매니저(T2)
// 디바이스가 Secure Enclave를 지원하는 지 확인
if !SecureEnclave.isAvailable {
}
// 이후 로직은 위와 동일
// privateKey를 만들 때 Secure Enclave가 관여한다는 점이 다르다.
let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
let publicKey = privateKey.publicKey.compactRepresentation!
// privateKey는 키체인에, publicKey는 서버에 저장
// 컨텐츠에 사인
let signature = try privateKey.signature(for: transactionData)
// Access Control 적용
let accessControl = SecAccessControlCreateWithFlags(nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.privateKeyUsage, .userPresence],
nil)!
// privateKey 만들 때 accessControl 이용
let privateKey = try SecureEnclave.P256.Signing.PrivateKey(accessControl: accessControl)
let authContext = LAContext()
authContext.touchIDAuthenticationAllowableReuseDuration = 10
authContext.localizedReason = "Authorizing $10 trnafer to Bob."
let privateKey = try SecureEnclave.P256.Signing.PrivateKey(accessControl: accessControl,
authenticationContext: authContext)
성능