투케이2K

49. (ios/swift) aes 암호화 파생 인코딩 , 디코딩 수행 실시 - secretkey , iv , salt 본문

IOS

49. (ios/swift) aes 암호화 파생 인코딩 , 디코딩 수행 실시 - secretkey , iv , salt

투케이2K 2021. 11. 18. 09:29

[개발 환경 설정]

개발 툴 : XCODE

개발 언어 : SWIFT

 

[소스 코드]

import Foundation


// MARK: - [필요 헤더 브릿지 : #import <CommonCrypto/CommonCrypto.h>]
struct C_AES256 {
    
    
    // MARK: - [클래스 설명]
    /*
    1. AES256 암호화 수행 파생 클래스
    2. 추가하는 값 KEY, IV, SALT 값
    3. CBC : 블록 암호화 운영 모드 중 보안 성이 제일 높은 암호화 방법으로 가장 많이 사용 (IV 사용)
    4. PKCS 5 : 8 바이트 패딩 (데이터 길이가 모자라다면 8 바이트까지 마지막 값 기준 채우고, 8 바이트 이상인 경우 뒤에 8바이트 패딩 추가)
    5. PKCS 7 : 16 바이트 패딩 (데이터 길이가 모자라다면 16 바이트까지 마지막 값 기준 채우고, 16 바이트 이상인 경우 뒤에 8바이트 패딩 추가)
    7. 참고 사이트 (aos) : https://www.tabnine.com/code/java/methods/javax.crypto.spec.PBEKeySpec/%3Cinit%3E
    8. 참고 사이트 (ios) : https://gist.github.com/hfossli/7165dc023a10046e2322b0ce74c596f8
    */
    
    
    
    // MARK: - [사용 방법 정의]
    /*
     DispatchQueue.main.async {
         do {
             // [데이터 지정]
             let data = "hello".data(using: .utf8)!
             
             // [필요 변수 선언]
             let secretKey = "0123456789abcdef0123456789abcdef" // [32 byte]
             let iv = "0123456789abcdef".data(using: .utf8)! // [16 byte]
             let salt = "0123456789abcdef0123456789abcdef".data(using: .utf8)! // [random byte]
             let keyLength = kCCKeySizeAES256 // [256 = 32 length]
             let iterationCount = 10000
             
             // [KeySpec]
             let key = try C_AES256.createKey(secretKey: secretKey.data(using: .utf8)!, salt: salt, keyLength: keyLength, iterationCount: UInt32(iterationCount))
             
             // [Cipher]
             let aes = try C_AES256(key: key, iv: iv)
             
             // [AES 데이터 인코딩 수행 실시]
             let encryptedBase64 = try aes.encrypt(data).base64EncodedString() // 데이터 인코딩
             print("")
             print("===============================")
             print("[C_AES256 >> :: AES 암호화 인코딩 수행 실시]")
             print("secretKey :: ", secretKey)
             print("iv :: ", String(decoding: iv, as: UTF8.self))
             print("salt :: ", String(decoding: salt, as: UTF8.self))
             print("keyLength :: ", keyLength)
             print("iterationCount :: ", iterationCount)
             print("원본 :: ", String(decoding: data, as: UTF8.self))
             print("인코딩 :: ", encryptedBase64)
             print("===============================")
             print("")
             
             // [AES 데이터 디코딩 수행 실시]
             let decryptedBase64 = String(data: try aes.decrypt(Data(base64Encoded: encryptedBase64)!), encoding: .utf8) ?? "" // 데이터 디코딩
             print("")
             print("===============================")
             print("[C_AES256 >> :: AES 암호화 디코딩 수행 실시]")
             print("secretKey :: ", secretKey)
             print("iv :: ", String(decoding: iv, as: UTF8.self))
             print("salt :: ", String(decoding: salt, as: UTF8.self))
             print("keyLength :: ", keyLength)
             print("iterationCount :: ", iterationCount)
             print("원본 :: ", encryptedBase64)
             print("디코딩 :: ", decryptedBase64)
             print("===============================")
             print("")
         }
         catch {
             print("")
             print("===============================")
             print("[C_AES256 >> :: AES 인코딩 및 디코딩 수행 실패]")
             print("error :: ", error)
             print("===============================")
             print("")
         }
     }
     */
    
    
    
    // MARK: - [결과 출력 예시]
    /*
     ===============================
     [C_AES256 >> :: AES 암호화 인코딩 수행 실시]
     secretKey ::  0123456789abcdef0123456789abcdef
     iv ::  0123456789abcdef
     salt ::  0123456789abcdef0123456789abcdef
     keyLength ::  32
     iterationCount ::  10000
     원본 ::  hello
     인코딩 ::  4KpINm+YV8sEqzj8ccXIkw==
     ===============================

     ===============================
     [C_AES256 >> :: AES 암호화 디코딩 수행 실시]
     secretKey ::  0123456789abcdef0123456789abcdef
     iv ::  0123456789abcdef
     salt ::  0123456789abcdef0123456789abcdef
     keyLength ::  32
     iterationCount ::  10000
     원본 ::  4KpINm+YV8sEqzj8ccXIkw==
     디코딩 ::  hello
     ===============================
     */
    
    
    
    // MARK: - [전역 변수 선언 실시]
    private var key: Data
    private var iv: Data
    
    
    
    // MARK: - [enum 에러 타입 정의]
    enum Error: Swift.Error {
        case keyGeneration(status: Int)
        case cryptoFailed(status: CCCryptorStatus)
        case badKeyLength
        case badInputVectorLength
    }
    
    
    
    // MARK: - [클래스 생성자 초기화 실시]
    public init(key: Data, iv: Data) throws {
        // [secret key 32 바이트 체크 실시]
        guard key.count == kCCKeySizeAES256 else {
            throw Error.badKeyLength
        }
        // [iv 16 바이트 체크 실시]
        guard iv.count == kCCBlockSizeAES128 else {
            throw Error.badInputVectorLength
        }
        self.key = key
        self.iv = iv
    }
    
    
    
    // MARK: - [key 생성 수행 부분]
    static func createKey(secretKey: Data, salt: Data, keyLength:Int, iterationCount:UInt32) throws -> Data {
        // MARK: [알고리즘 변수 선언]
        let algo_1 = kCCPBKDF2
        let algo_2 = kCCPRFHmacAlgSHA1
        
        // MARK: [키 생성 상태 값 지정]
        var status = Int32(0)
        var derivedBytes = [UInt8](repeating: 0, count: keyLength)
        secretKey.withUnsafeBytes { (passwordBytes: UnsafePointer<Int8>!) in
            salt.withUnsafeBytes { (saltBytes: UnsafePointer<UInt8>!) in
                status = CCKeyDerivationPBKDF(
                    // MARK: [알고리즘 - 1]
                    CCPBKDFAlgorithm(algo_1), // [알고리즘]
                    
                    // MARK: [비밀키 지정]
                    passwordBytes, // [secretKey]
                    secretKey.count, // [secretKey Length]
                    
                    // MARK: [salt 지정]
                    saltBytes, // [salt]
                    salt.count, // [saltLength]
                    
                    // MARK: [알고리즘 - 2]
                    CCPseudoRandomAlgorithm(algo_2), // [알고리즘]
                    
                    // MARK: [iterationCount]
                    iterationCount,
                    
                    // MARK: [KeySpec : keyLength]
                    &derivedBytes, // [derivedKey]
                    keyLength // [keyLength]
                )
            }
        }
        guard status == 0 else {
            throw Error.keyGeneration(status: Int(status))
        }
        return Data(bytes: UnsafePointer<UInt8>(derivedBytes), count: keyLength)
    }

    
    
    // MARK: - [인코딩 수행 실시]
    func encrypt(_ digest: Data) throws -> Data {
        return try crypt(input: digest, operation: CCOperation(kCCEncrypt))
    }
    
    
    
    // MARK: - [디코딩 수행 실시]
    func decrypt(_ encrypted: Data) throws -> Data {
        return try crypt(input: encrypted, operation: CCOperation(kCCDecrypt))
    }
    
    
    
    // MARK: - [인코딩, 디코딩 데이터 생성]
    private func crypt(input: Data, operation: CCOperation) throws -> Data {
        // MARK: [SecretKeySpec : 인코딩, 디코딩 수행에서 AES 지정]
        let secretKeySpecType = kCCAlgorithmAES
        
        // MARK: [Cipher : 인코딩, 디코딩 수행에서 필요한 패딩 지정]
        let padding = kCCOptionPKCS7Padding
        
        // MARK: [인코딩 및 디코딩 수행 데이터]
        var outLength = Int(0)
        var outBytes = [UInt8](repeating: 0, count: input.count + kCCBlockSizeAES128)
        
        // MARK: [키 생성 상태 값 지정]
        var status: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
        input.withUnsafeBytes { (encryptedBytes: UnsafePointer<UInt8>!) -> () in
            iv.withUnsafeBytes { (ivBytes: UnsafePointer<UInt8>!) in
                key.withUnsafeBytes {
                    (keyBytes: UnsafePointer<UInt8>!) -> () in status = CCCrypt(
                        operation,
                        
                        // MARK: [SecretKeySpec : 인코딩, 디코딩 수행에서 AES 지정]
                        CCAlgorithm(secretKeySpecType),
                        
                        // MARK: [Cipher : 인코딩, 디코딩 수행에서 필요한 패딩 지정]
                        CCOptions(padding),
                        
                        // MARK: [Cipher : KeySpec + iv]
                        keyBytes, // [key]
                        key.count, // [keylength]
                        
                        // MARK: [Cipher : KeySpec + iv]
                        ivBytes, // [iv]
                        
                        // MARK: [inputData]
                        encryptedBytes, // [inputData]
                        input.count, // [inputDataLength]
                        
                        // MARK: [outputData]
                        &outBytes, // [outputData]
                        outBytes.count, // [outputDataLength]
                        &outLength // [dataOutMoved]
                    )
                }
            }
        }
        guard status == kCCSuccess else {
            throw Error.cryptoFailed(status: status)
        }
        return Data(bytes: UnsafePointer<UInt8>(outBytes), count: outLength)
    }
    
} // [클래스 종료]
 

[결과 출력]

 

 

반응형
Comments