ukSeung iOS

[Swift] CryptoSwift, 라이브러리를 통한 암복호화 본문

iOS/Library

[Swift] CryptoSwift, 라이브러리를 통한 암복호화

욱승 2023. 5. 26. 10:22

해당 포스팅은 MVVM패턴 + RxSwift로 이루어진 예제입니다.

 

안녕하세요 욱승임당

이번 포스팅에서는 라이브러리를 통한 암복호화를 해볼건데요

앱에서 따로 암복호화 알고리즘을 구현하지 않고 라이브러리를 사용해서 간단히 암복화를 할 수 있다고 하더라구여

 

이 라이브러리도 뱅크샐러드에서 사용중이라고 합니다!

 

그럼 가보쟈고

카카오 떡상 가즈아!!!

 

GitHub

 

GitHub - krzyzanowskim/CryptoSwift: CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemen

CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift - GitHub - krzyzanowskim/CryptoSwift: CryptoSwift is a growing collection of standard and se...

github.com

 

Podfile

pod 'CryptoSwift' # Swfit 로 작성된 cryptographic algorithms들을 모아둔 라이브러리

 

import

import CryptoSwift

 

AES 알고리즘을 활용한 코드로 구현해볼껀데 그전에 알고리즘의 장단점을 알고 구현하면 더 좋겠쥬?😉

 

AES(Advanced Encryption Standard)는 현재 가장 널리 사용되는 대칭키 암호화 알고리즘 중 하나입니다. AES의 장점과 단점은 다음과 같습니다:

장점:

  1. 보안성: AES는 안전하고 강력한 암호화 알고리즘으로 알려져 있습니다. 적절한 키 길이를 사용하고 올바른 모드와 패딩을 적용하면 매우 안전한 암호화를 제공합니다.
  2. 효율성: AES는 암호화 및 복호화 과정에서 빠르고 효율적입니다. 최신 하드웨어와 소프트웨어 최적화 기술을 활용하면 더욱 빠른 처리가 가능합니다.
  3. 널리 지원: AES는 전 세계적으로 표준화되어 있으며, 다양한 플랫폼과 프로그래밍 언어에서 지원됩니다. 이는 상호 운용성을 높여줍니다.

단점:

  1. 대칭키 알고리즘: AES는 대칭키 알고리즘으로, 암호화와 복호화에 동일한 키를 사용합니다. 이는 키 분배와 관리에 대한 문제를 야기할 수 있습니다. 안전한 키 교환 및 보안 키 저장소의 필요성이 있습니다.
  2. 공격에 대한 취약성: AES 자체는 매우 강력하지만, 구현 방식, 키 관리, 취약한 애플리케이션 설계 등의 요소에 따라 공격에 취약할 수 있습니다. 잘못된 구현이나 암호 분석 기법에 의해 공격될 수 있으므로 주의가 필요합니다.
  3. 사회적 공격: AES는 암호화와 복호화에 사용되는 키를 외부에 안전하게 보호해야 합니다. 키가 유출되거나 제 3자에게 악용될 경우 암호화된 데이터의 보안이 손상될 수 있습니다.

AES는 현재 암호화 표준으로 널리 사용되고 있으며, 일반적으로 안전하고 효율적인 선택입니다. 그러나 암호화 시스템의 전반적인 보안은 알고리즘 선택 외에도 구현, 키 관리, 보안 프로토콜 등 다양한 요소에 따라 결정되므로 전체 시스템의 보안을 고려해야 합니다.

 

보아하니 키의 은닉을 강조 하는 것 같죠? '키'값만 안전하게 보관된다면 안전한 알고리즘이다~ 이말이당

 

키값 보관 가이드라인

 

암호화 시스템에서 키를 안전하게 보관하는 것은 매우 중요합니다. 키의 안전한 보관을 위해 고려해야 할 몇 가지 가이드라인은 다음과 같습니다:

  1. 키의 물리적 보안: 키를 물리적으로 안전한 장소에 저장해야 합니다. 안전한 잠금장치가 있는 잠금 보관함이나 금고, 안전한 서버 룸 등의 장소를 사용하는 것이 좋습니다. 또한 접근 권한을 제한하고 키에 대한 로깅 및 감시를 설정하여 불법 접근을 방지해야 합니다.
  2. 키의 암호화: 키를 저장할 때, 추가적인 보안을 위해 키 자체를 암호화할 수 있습니다. 키를 암호화하려면 다른 암호화 키 또는 암호화 비밀번호가 필요하므로, 암호화 키 또는 비밀번호를 안전하게 관리해야 합니다.
  3. 키 관리 및 접근 제어: 키에 대한 접근을 엄격히 제어해야 합니다. 적절한 접근 제어 메커니즘과 권한 관리 시스템을 구현하여 키에 접근할 수 있는 사람을 제한하고, 키를 필요로 하는 프로세스나 서비스에만 키를 제공해야 합니다.
  4. 키 분배: 키를 안전하게 분배해야 합니다. 안전하지 않은 통신 채널을 통해 키를 전송하지 않도록 주의해야 합니다. 안전한 키 교환 프로토콜을 사용하거나, 공개 키 암호화를 활용하여 키를 안전하게 전송할 수 있습니다.
  5. 키 회전: 일정한 주기로 키를 회전시키는 것이 좋습니다. 키 회전은 키가 유출될 경우의 피해를 최소화하고, 암호 시스템의 보안을 유지하기 위해 중요합니다.
  6. 안전한 키 저장소 사용: 키를 안전하게 저장하는데 사용되는 키 저장소는 보안 요구 사항을 충족해야 합니다. 예를 들어, iOS 애플리케이션의 경우 Keychain을 사용하여 키를 안전하게 저장할 수 있습니다. 이러한 키 저장소는 키를 암호화하고 안전한 환경에서 보호됩니다.

 

예제코드

View

import Foundation
import UIKit
import CryptoSwift
import RxSwift
import RxCocoa

final class CryptoViewController: UIViewController, UIViewControllerAttribute {
    let disposeBag = DisposeBag()
    let viewModel = CryptoViewModel()
    
    var navTitle: String?
    
    let cryptoTextField = UITextField().then {
        $0.borderStyle = .roundedRect
    }
    
    let cryptoButton = UIButton(type: .system).then {
        $0.setTitle("암호화", for: .normal)
    }
    
    lazy var originLabel = UILabel().then {
        $0.text = "기존 텍스트 :"
        $0.textAlignment = .left
        $0.numberOfLines = 0
        $0.textColor = .black
        $0.lineBreakMode = .byCharWrapping
    }
    
    lazy var encryptLabel = UILabel().then {
        $0.text = "암호화 :"
        $0.textAlignment = .left
        $0.numberOfLines = 0
        $0.textColor = .black
    }
    
    lazy var decryptLabel = UILabel().then {
        $0.text = "복호화 :"
        $0.textAlignment = .left
        $0.numberOfLines = 0
        $0.textColor = .black
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setNavigationBar()
        setUI()
        setAttributes()
        bindRx()
    }
    
    func setNavigationBar() {
        self.navigationItem.title = navTitle ?? ""
    }
    
    func setUI() {
        self.view.backgroundColor = .white
        self.view.addSubview(cryptoTextField)
        self.view.addSubview(cryptoButton)
        self.view.addSubview(encryptLabel)
        self.view.addSubview(decryptLabel)
        self.view.addSubview(originLabel)
    }
    
    func setAttributes() {
        cryptoTextField.snp.makeConstraints {
            $0.top.equalTo(self.view.safeAreaLayoutGuide).offset(30)
            $0.centerX.equalToSuperview()
            $0.width.equalTo(350)
            $0.height.equalTo(50)
        }
        
        cryptoButton.snp.makeConstraints {
            $0.top.equalTo(cryptoTextField.snp.bottom).offset(20)
            $0.centerX.equalTo(cryptoTextField.snp.centerX)
            $0.width.equalTo(200)
            $0.height.equalTo(40)
        }
        
        originLabel.snp.makeConstraints {
            $0.top.equalTo(cryptoButton.snp.bottom).offset(20)
            $0.left.equalTo(20)
            $0.right.equalTo(-20)
            $0.height.equalTo(100)
        }
        
        encryptLabel.snp.makeConstraints {
            $0.top.equalTo(originLabel.snp.bottom).offset(20)
            $0.left.equalTo(20)
            $0.right.equalTo(-20)
            $0.height.equalTo(originLabel.snp.height)
        }
        
        decryptLabel.snp.makeConstraints {
            $0.top.equalTo(encryptLabel.snp.bottom).offset(20)
            $0.left.equalTo(20)
            $0.right.equalTo(-20)
            $0.height.equalTo(originLabel.snp.height)
        }
    }
    
    func bindRx() {
        // cryptoBool에 따라 버튼title / textfield isEnable값 변경
        viewModel.cryptoBool
            .subscribe(onNext: {
                self.cryptoTextField.isEnabled = !$0
                $0 ? self.cryptoButton.setTitle("복호화", for: .normal) : self.cryptoButton.setTitle("암호화", for: .normal)
            })
            .disposed(by: disposeBag)
        
        // cryptoTextField TextDidChange
        cryptoTextField.rx.text
            .orEmpty
            .asObservable()
            .subscribe(onNext: {
                self.originLabel.text = "기존 텍스트 : \($0)"
                self.viewModel.inputText.accept($0)
            })
            .disposed(by: disposeBag)
        
        // cryptoButton 버튼 탭 / 버튼 제목에 따라 함수 분기처리
        cryptoButton.rx.tap
            .subscribe(onNext: {
                let title = self.cryptoButton.titleLabel?.text
                title == "암호화" ? self.viewModel.encrypt() : self.viewModel.decrypt()
            })
            .disposed(by: disposeBag)
        
        // viewModel에서 기존 텍스트에서 암호화된 텍스트 view에 bind
        viewModel.encryptedText
            .map { "암호화 텍스트 : \($0)" }
            .bind(to: encryptLabel.rx.text)
            .disposed(by: disposeBag)
        
        // viewModel에서 암호화 텍스트에서 복호화된 텍스트 view에 bind
        viewModel.decryptedText
            .map { "복호화 텍스트 : \($0)" }
            .bind(to: decryptLabel.rx.text)
            .disposed(by: disposeBag)
    }
}

RxSwift를 통한 비동기식 처리

 

ViewModel

import Foundation
import RxSwift
import RxCocoa
import CryptoSwift

final class CryptoViewModel {
    
    let inputText = BehaviorRelay<String>(value: "") // 기존 텍스트
    let encryptedText = BehaviorRelay<String>(value: "") // 암호화 텍스트
    let decryptedText = BehaviorRelay<String>(value: "") // 복호화 텍스트
    
    let cryptoBool = BehaviorRelay<Bool>(value: false) // false => 복호화 or 기존 텍스트 상태 / true => 암호화된 상태
    
    private let encryptionKey = "SecretKey123" // 키값, 해당 키값은 은닉 해야함
    
    /// 암호화
    func encrypt() {
        guard let input = inputText.value.data(using: .utf8) else {
            return
        }
        
        do {
            let password = Array(encryptionKey.utf8)
            let salt = Array("salt".utf8)
            let derivedKey = try PKCS5.PBKDF2(password: password, salt: salt).calculate()
            
            let aes = try AES(key: derivedKey, blockMode: ECB(), padding: .pkcs7)
            let encrypted = try aes.encrypt(input.bytes)
            let encryptedData = Data(encrypted)
            let encryptedString = encryptedData.base64EncodedString()
            
            encryptedText.accept(encryptedString)
            cryptoBool.accept(true)
        } catch {
            print("Encryption error: \(error)")
        }
    }
    
    /// 복호화
    func decrypt() {
        guard let input = Data(base64Encoded: encryptedText.value) else {
            return
        }
        
        do {
            let password = Array(encryptionKey.utf8)
            let salt = Array("salt".utf8)
            let derivedKey = try PKCS5.PBKDF2(password: password, salt: salt).calculate()
            
            let aes = try AES(key: derivedKey, blockMode: ECB(), padding: .pkcs7)
            let decrypted = try aes.decrypt(input.bytes)
            let decryptedData = Data(decrypted)
            
            if let decryptedString = String(data: decryptedData, encoding: .utf8) {
                decryptedText.accept(decryptedString)
                cryptoBool.accept(false)
            }
        } catch {
            print("Decryption error: \(error)")
        }
    }
}

 

 

 

결과

테스트 해보니 이모티콘도 같이 암호화 됨 ,, 😂

 

결론

개인정보 보호법이 강조되고 있는만큼 데이터 암호화도 같이 강조되고 있다. 유용한 라이브러리 같으니 알아두자 ㅎㅎ

728x90
반응형