Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Xcode
- Observable
- UITextView
- 리팩토링
- ios
- 애니메이션
- swift documentation
- combine
- MVVM
- 리펙터링
- 리펙토링
- Protocol
- Refactoring
- 스위프트
- clean architecture
- ribs
- tableView
- collectionview
- map
- UICollectionView
- uitableview
- swiftUI
- SWIFT
- 클린 코드
- rxswift
- uiscrollview
- RxCocoa
- HIG
- Human interface guide
- Clean Code
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] keychain에 암호화/복호화하여 저장 방법 (SecureEnclave) 본문
* keychain사용방법: KeychainAccess 프레임워크 사용 방법 참고 - ios-development.tistory.com/216
암호화 모듈
- keychain에 저장된 문자열은 모두 외부에서 디바이스만 있다면 조회가 가능하기 때문에 암호화/복호화가 필수
- SecureEnclave: prvate key를 이용하여 layer를 만들어 암호화하는 방법
- 앱에서는 BundleID를 가지고 암호화/복호화
- Crypto 클래스 정의
import Foundation
import Security
class Crypto {
static let privTag = Bundle.main.bundleIdentifier!
static let SecureEnclaveAccess = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.privateKeyUsage,
nil
)
static var EnclaveAttribute: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: privTag,
kSecAttrAccessControl as String: SecureEnclaveAccess!,
],
]
// kSecAttrAccessControl as String: access
static var NonEnclaveAttribute: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: privTag,
],
]
public class func getPubKey() -> SecKey {
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrApplicationTag as String: privTag,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecReturnRef as String: true,
]
var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status != errSecSuccess {
print("priv key get failed.. generate new key")
generateKey()
SecItemCopyMatching(query as CFDictionary, &result)
}
let privKey: SecKey = result as! SecKey
let pubKey = SecKeyCopyPublicKey(privKey)!
return pubKey
}
public class func getPrivKey() -> SecKey {
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrApplicationTag as String: privTag,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecReturnRef as String: true,
]
var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status != errSecSuccess {
print("priv key get failed.. generate new key")
SecItemDelete(query as CFDictionary)
generateKey()
let new = SecItemCopyMatching(query as CFDictionary, &result)
print(new)
}
let privKey: SecKey = result as! SecKey
return privKey
}
public class func deleteKey() {
let Privquery: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrApplicationTag as String: privTag,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecReturnRef as String: true,
]
let code = SecItemDelete(Privquery as CFDictionary)
if code == errSecSuccess {
print("Key Delete Complete!")
} else {
print("Delete Failed!!")
print(code)
}
}
public class func generateKeyNoEnclave() {
var error: Unmanaged<CFError>?
// 기기가 SecureEnclave를 지원하지 않는 경우, 일반 암호화 키 생성 후 키체인에 저장
_ = SecKeyCreateRandomKey(NonEnclaveAttribute as CFDictionary, &error)
}
public class func generateKey() {
var error: Unmanaged<CFError>?
var privKey = SecKeyCreateRandomKey(EnclaveAttribute as CFDictionary, &error) // Secure Enclave 키 생성
if privKey == nil { // 단말기가 Secure Enclave를 지원하지 않는 경우
print("Secure Enclave Not Supported.")
privKey = SecKeyCreateRandomKey(NonEnclaveAttribute as CFDictionary, &error)
}
}
public class func encrypt(input: String) -> String? {
let pubKey: SecKey = getPubKey()
print(pubKey)
var error: Unmanaged<CFError>?
let plain: CFData = input.data(using: .utf8)! as CFData
let encData = SecKeyCreateEncryptedData(pubKey, SecKeyAlgorithm.eciesEncryptionStandardX963SHA256AESGCM, plain, &error)
var tdata: Data
if encData == nil {
print("encrypt error!!!")
return nil
} else {
tdata = encData! as Data
}
let b64result = tdata.base64EncodedString()
return b64result
}
public class func decrypt(input: String) -> String? {
let privKey: SecKey = getPrivKey()
print(privKey)
guard let encData = Data(base64Encoded: input) else {
print("decrypt error!!!")
return nil
}
var error: Unmanaged<CFError>?
let decData = SecKeyCreateDecryptedData(privKey, SecKeyAlgorithm.eciesEncryptionStandardX963SHA256AESGCM, encData as CFData, &error)
var tdata: Data
if decData == nil {
print("decrypt error!!!")
return nil
} else {
tdata = decData! as Data
}
let decResult = String(data: tdata, encoding: .utf8)!
return decResult
}
}
Crypto 클래스 사용
- 암호화 할 땐, Crypto.encrypt(input:)를 사용하고 복호화 할 땐 Crypto.decrypt(input:)을 사용
- keychain을 사용하여 set, get할 때 각각 적용 (KeychainAccess를 사용하여 get, set 구현은 이곳 참고)
- get, set에 사용코드
// KeychainService.swift
...
// MARK: Save
public func save(key: String, value: String) {
do {
if let encryptValue = Crypto.encrypt(input: value) {
print("CRPYTO", "SAVE", key, value, "->", encryptValue)
try lockCredentials.set(encryptValue, key: key)
} else {
print("CRPYTO", "SAVE FAILED", key, value)
}
} catch let error {
print("KEYCHAIN SAVE :: Failed to save this value with this key \(key) => \(error)")
}
}
...
// MARK: Get
public func get(key: String) -> String? {
var decrypt: String?
var encrypt: String?
if let value = lockCredentials[string: key] {
encrypt = value
decrypt = Crypto.decrypt(input: value)
}
print("CRPYTO", "GET", key, String(describing: encrypt), "->", String(describing: decrypt))
return decrypt
}
* 참고
'iOS 응용 (swift)' 카테고리의 다른 글
[iOS - swift] app store로 이동 (버전 업데이트, 다시 돌아온 경우 처리, 현재 버전, 최신 버전 가져오는 방법) (6) | 2021.03.15 |
---|---|
[iOS - swift] tableView refresh (상단, 하단 새로고침) (0) | 2021.03.13 |
[iOS - swift] view위에 올라가있는 다른 view들을 하나의 tap gesture로 변경 (2) | 2021.03.09 |
[iOS - swift] textField의 placeholder색깔 변경 방법 (0) | 2021.03.09 |
[iOS - swift] mp4, gif 파일 재생 방법 (애니메이션, AVPlayer, Gifu 프레임워크) (0) | 2021.03.04 |
Comments