DADAHAE's Log
비싼 장난감 가지고 노는 중 (❁´▽`❁)*✲゚*
Apple CryptoKit으로 채팅 메세지를 암호화할 수 있을까?
최초 작성: 2023.06.14

 

ㅇㅇ 가능함.

 

공개 키 암호화로 공유 비밀 만들고, 그걸로 대칭 키 만들면 데이터 암호화도 되고 데이터 무결성도 보여줄 수 있음. 서버에 공개 키 올려두고 사용하면 된다. 

 

끗.

 

 

 

.

.

.

.

.

 

 

 

어케 하는지 모르겠다고?

 

ㅇㅋㅇㅋ 레츄고.

 


 

 

 

 

📌 CryptoKit 시리즈

1. 암호화 작업을 위한 Apple CryptoKit 알아보기
2. Apple CryptoKit으로 채팅 메세지를 암호화할 수 있을까? (현재 글)

 

 

 

 

 

1. CryptoKit으로 메세지 안전하게 송수신하기

이론편

프로젝트에 적용하기 전에 어떤 암호화 방식을 사용할지와 그 이유, 전체 코드 흐름을 살펴보도록 하자.

 

목적

채팅 메세지를 안전하게 송수신한다. 즉, 채팅 내역은 수신자와 송신자만 볼 수 있어야 한다. 서버에 저장된 채팅은 모두 암호화되며 개발자가 임의로 혹은 실수로 채팅 내용을 알아낼 수 없게 한다.

 

암호화 방식

공개 키 암호화 + 대칭 키(CryptoKit Curve25519.KeyAgreement + Symmetric Key)

 

해당 방식을 선택한 이유

메세지를 암호화해서 보내고, 받을 때는 복호화한다. 암호화/복호화를 위한 비밀 키는 메세지를 주고 받는 유저끼리 알고 있어야 한다. 대칭 키를 사용하면 이를 해결할 수 있다. 그러나 문제는 '어떻게 대칭 키를 안전하게 전송할 것이냐' 이다. 그래서 공개 키 암호화로 대칭 키를 만들어 대칭 키를 직접 보내지 않고도 사용할 수 있는 방식을 채택했다.

 

시나리오

  • 사용자 계정 생성 시
    1. 사용자의 계정을 생성한다.
    2. 사용자의 Public, Private key를 생성한다.
    3. Public key는 사용자의 계정 정보(서버)에 저장하고, Private key는 사용자의 기기(keychain 혹은 secure enclave)에 저장한다.
  • 사용자 A와 B가 채팅을 할 때
    1. A, B의 채팅방이 개설된다. (혹은 이미 개설되어 있거나)
    2. A가 B에게 메세지를 보낸다.
    3. A의 메세지는 암호화되어 서버에 전송된다.
    4. B에게 암호화된 메세지가 전달된다.
    5. 전달된 메세지는 복호화되어 B에게 읽힌다.

 

전체 코드

import SwiftUI
import CryptoKit

// MARK: 0. Message를 보낼 떄 사용하는 salt 값은 앱의 상수 파일에 선언해두기
let messageSalt = "Message 암호화".data(using: .utf8)!


// MARK: 1. 유저 생성시 Public, Private key 생성하기(KeyAgreement)
let minsuPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let minsuPublicKeyData = minsuPrivateKey.publicKey.rawRepresentation

let jennyPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let jennyPublicKeyData = jennyPrivateKey.publicKey.rawRepresentation

let poongPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let poongPublicKeyData = poongPrivateKey.publicKey.rawRepresentation


// MARK: 2. message 송신 (minsu -> jenny)
// 2-1. message 내용
let messageContent = "제니야 안녕? 나 민수야."
// 2-2. 대칭 키 생성
let jennyPublicKey = try! Curve25519.KeyAgreement.PublicKey(rawRepresentation: jennyPublicKeyData)
let MSsharedSecret = try! minsuPrivateKey.sharedSecretFromKeyAgreement(with: jennyPublicKey)
let MSsymmetricKey = MSsharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: messageSalt,
    sharedInfo: Data(),
    outputByteCount: 32
)
// 2-3. message 암호화
let messageContentData = messageContent.data(using: .utf8)!
let encryptedMessageData = try! ChaChaPoly.seal(messageContentData, using: MSsymmetricKey).combined
let minsuSealedBox = try! ChaChaPoly.SealedBox(combined: encryptedMessageData)
// 2-4. message 송신
/// [sealedBox]를 서버에 올린다.


// MARK: 3. message 수신 (jenny <- minsu)
// 3-1. message 수신
/// minsuSealedBox를 받았다.
// 3-1. 대칭 키 생성
let minsuPublicKey = try! Curve25519.KeyAgreement.PublicKey(rawRepresentation: minsuPublicKeyData)
let JEsharedSecret = try! jennyPrivateKey.sharedSecretFromKeyAgreement(with: minsuPublicKey)
let JEsymmetricKey = JEsharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: messageSalt,
    sharedInfo: Data(),
    outputByteCount: 32
)
// 3-2. message 복호화
let decryptedMessageData = try! ChaChaPoly.open(minsuSealedBox, using: JEsymmetricKey)
String(decoding: decryptedMessageData, as: UTF8.self)

 

 

 

2. SwiftUI로 Demo 프로젝트 만들기

실전편

 

- 개발 상황 가정: firebase를 사용하여 채팅 내역 저장, firebase의 firestore + listener로 실시간 채팅 기능 구현

 

(준비중)

  Comments,     Trackbacks