HTTP 로 통신하게 되면 데이터를 암호화하지 않습니다.
데이터에는 사용자 정보, 비밀번호, 쿠키, 개인 정보, API 요청 내용 등
이를 중간에 훔칠 수 ( 스니핑 ) 할수 있습니다.
HTTP 프로토콜에 암호화 프로토콜인 SSL / TLS 를 통해 이를 보안합니다.
SSL / TLS 의 자세한 이야기는 다음 시간에 해보도록 하겠습니다.
let url = URL(string: "https://kimjaehyung.com/api")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("에러발생!!!:", error ?? "Unknown error")
return
}
print("데이터 받음: \(data)")
}
task.resume()
URLSession에선 기본적으로 HTTPS를 지원합니다
SSL 인증서 혹은 TLS 방식을 통해 이를 검증하기 때문에, 안전한 통신이 이루어지도록 합니다.
그런데도, 왜 더 낮은 수준의 암호화가 필요할까요?
만약 저희가 유저의 정보를 통신하였다고 생각해 보곘습니다.
그런데 이를 안전하게 통신하고 받아왔다고 생각해 볼께요
그리고 이를 DB에 저장하였다고 생각해 보겠습니다.
자 문제는 이때 발생합니다. 클라이언트 혹은 서버에 암호화 되어 있지 않은 정보를
가지고 있다는 것이죠.
그리하야 Apple 님을 암호화를 위해 CryptoKit이라는 프레임워크를 제공합니다...
공개키와 비공개 키를 알아봐야 합니다.
1) 공개적으로 배포되어서 누구나 사용할 수 있습니다.
2) 데이터를 암호화하거나, 서명을 검증할때 사용됩니다.
1) 소유자만이 알아야 하며, 공개되어선 않됩니다.
2) 암호화된 데이터를 복호화하거나, 서명할때 사용됩니다.
예를 들어 볼까요?
A 와 B 가 정보를 주고 받고자 합니다. C가 질투하여 중간에 메시지를 훔치겠습니다.
이때 C는 복사한 메시지를 열어 보지만, 읽을 수가 없습니다.
왜냐면.... 암호화 되어 있기 때문이죠
iOS 13 부터 도입한 암호화 프레임워크입니다.
대칭키 암호화, 비대칭키 암호화, 해시 함수, 그리고 디지털 서명 등 다양한 암호화 기능을 제공합니다.
import Foundation
import CryptoKit
// MARK: 1. 유저 Privatekey 생성하기(KeyAgreement)
func makePrivateKey() -> Curve25519.KeyAgreement.PrivateKey {
return Curve25519.KeyAgreement.PrivateKey()
}
// MARK: 1. 유저 Public key 생성하기(KeyAgreement)
func makePublicKey(base: Curve25519.KeyAgreement.PrivateKey) -> Curve25519.KeyAgreement.PublicKey {
return base.publicKey
}
print( publicKey.rawRepresentation )
예시를 만들어 보았습니다.
Crypto 프레임워크는 SHA-256, SHA-512, MD5와 같은 여러 해싱 알고리즘을 지원하는데,
Curve25519는 비대칭 키 암호화 알고리즘 중 하나인 타원 곡선 암호화 알고리즘입니다
PrivateKey(): 개인키를 생성하는 코드입니다.
PublicKey(): 공개키를 생성하는 코드입니다.
publicKey.rawRepresentation: 공개키를 가져오는 코드로, 상대방과 공유하여 대칭키를 생성할 때 사용합니다.
import CryptoKit
// 개인키와 공개키를 UserDefaults에 저장하는 함수
func saveKeys(privateKey: Curve25519.KeyAgreement.PrivateKey, publicKey: Data) {
// 개인키는 rawRepresentation으로 변환 후 저장
let privateKeyData = privateKey.rawRepresentation
UserDefaults.standard.set(privateKeyData, forKey: "privateKey")
UserDefaults.standard.set(publicKey, forKey: "publicKey")
}
// 저장된 키 불러오기 (없으면 새로운 키 생성)
func loadKeys() -> (privateKey: Curve25519.KeyAgreement.PrivateKey, publicKey: Data) {
let privateKeyData = UserDefaults.standard.data(forKey: "privateKey")
let publicKeyData = UserDefaults.standard.data(forKey: "publicKey")
if let privateKeyData = privateKeyData, let publicKeyData = publicKeyData {
// 저장된 개인키 불러오기
let privateKey = try! Curve25519.KeyAgreement.PrivateKey(rawRepresentation: privateKeyData)
return (privateKey, publicKeyData)
} else {
// 저장된 키가 없으면 새로 생성하고 저장
let newPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let newPublicKey = newPrivateKey.publicKey.rawRepresentation
saveKeys(privateKey: newPrivateKey, publicKey: newPublicKey)
return (newPrivateKey, newPublicKey)
}
}
salt : 동일한 입력 데이터를 사용하더라도 서로 다른 결과의 대칭키를 얻을 수 있도록 데이터 입니다.
let messageSalt = "Message 암호화".data(using: .utf8)!
let symmetricKey = MSsharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: messageSalt, // 여기서 사용됨
sharedInfo: Data(),
outputByteCount: 32
)
서버에서 메시지를 받아오기 위한 방법을 생각해보죠
// 진수님의 공개키는 서버나 DB에서 가져온다고 가정합니다.
let JinsuSealedBox = try! ChaChaPoly.SealedBox(combined: 받은 데이터)
let minsuPublicKeyData = ...
// 대칭키 생성
let otherPublicKey = try! Curve25519.KeyAgreement.PublicKey(rawRepresentation: minsuPublicKeyData)
let MeSharedSecret = try! jennyPrivateKey.sharedSecretFromKeyAgreement(with: otherPublicKey)
let meSymmetricKey = MeSharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: messageSalt,
sharedInfo: Data(),
outputByteCount: 32
)
// 메시지 복호화
let decryptedMessageData = try! ChaChaPoly.open(JinsuSealedBox, using: meSymmetricKey)
let decryptedMessage = String(decoding: decryptedMessageData, as: UTF8.self)
음... 역시 한번에 마무리 짓기에는 어려운 개념입니다....
2편에서 좀더 해당 부분들을 정리해 보도록 하겠습니다
모두 고생 하셨습니다!