4T2F / ThinkBig2

🌟씽크빅 2팀 스터디 🌟
2 stars 0 forks source link

암호화와 보안의 기본 개념, iOS 앱 보안을 위한 방안에 대해 설명해주세요. #22

Open Hminchae opened 7 months ago

Hminchae commented 7 months ago
Hminchae commented 7 months ago

🔐 대칭키 암호화와 비대칭키 암호화의 차이

1. 대칭키(Symmetric key) 암호화

대칭키(Symmetric key)방식은 암호문을 생성(암호화)할 때 사용하는 키와, 암호문으로부터 평문을 복원(복호화)할 때 사용하는 키가 동일한 암호 시스템으로 일반적으로 알고 있는 암호 시스템

3. 그래서 드디어 차이점 정리..!

대칭키 암호화 방식 비대칭키 암호화 방식
개념 - 암호화(비밀키) = 복호화(비밀키)
- 대칭구조
- 암호화(공개키)와 복호화(개인키)가 다르며 ,
이들 중 복호화키만 비밀로 간직
- 비대칭 구조
특징 - 대량의 Data 암호화 유리 - 전자서명, 공인인증서 등 다양한 이용
장점 - 연산 속도 빠르고 구현 용이
- 일반적으로 같은 양의 데이터를 암호화하기 위한 연산이 공개키 암호보다 현저히 빠름
- 쉽게 기밀성을 제공
- 키 분배/키 관리가 용이
- 사용자의 증가에 따라 관리할 키의 개수가 상대적으로 적음
- 키 변화의 빈도가 적음(공개키의 복호화키는 길고 복잡하기 때문)
- 기밀성, 무결성을 지원하고 특히 부인 방지 기능을 제공
단점 - 키 관리가 어려움
- 무결성 지원이 부분적으로만 가능하고, 부인방지 기능을 제공하지 않음
- 키의 길이가 길고 연산 속도가 느림
알고리즘 SEED, HIGHT, ARIA, LEA, DES, AES, RC4 등 RSA, Diffie- Hellman, ECC, digital signature

4. 서명

메시지를 보낸 소혜가 자신의 개인키로 서명해서 메시지 보낸 사람이 소혜 본인임을 입증하는 것

메시지 송신자(A)가 메시지 수신자(B)한테 메시지를 전송하려고 한다.  
A는 메시지를 A의 개인키로 암호화한 메시지를 만든다. (서명)  
A는 메시지를 B의 공개키로 암호화한다.  
B한테 암호화한 메시지와 서명문을 같이 전달한다.  
B는 B의 개인키로 암호화한 메시지를 복호화한다.(암호화 메시지 -> 메시지)  
또, B는 A의 공개키로 서명문을 복호화한다. (서명 -> 메시지')  
메시지와 메시지'가 같다면, A가 메시지를 보내었다는 것이 보장된다.

5. 덧붙여 iOS의 대칭키/비대칭키 활용 사례

애플은 암호화를 위해 CryptoKit 이라는 프레임워크 제공함

1) iOS 에서의 RSA 암호화

iOS 앱내에서 RSA 암호화 방식을 사용 하려면 두가지 방법이 있다. 방법에 대하여 자세한 설명은 여기 블로그에 잘 설명 되어 있슴다..

  1. RSA 암호화를 지원하는 라이브러리 사용(OpenSSL) a ) OpenSSL 정적 라이브러리를 만들고 b ) 라이브러리를 프로젝트에 추가하고 RSA_public_encrypt 함수를 이용하여 암호화
  2. 직접구현

    2) iOS에서 제공하는 암호화 프레임워크 'CryptoKit'

    암호화 작업을 안전하고 효율적으로 수행할 수 있도록 도와주는 애플 프레임워크 이 내용에 대해서는 아래 해시함수를 다루고 더 설명 드리겠음

    • 암호학적 해싱(Cryptographic hashing)제공
    • 공개키 암호화 : 디지털 서명(Digital Signature)작업, 키 교환
    • 대칭키 암호화 : 메시지 인증 및 데이터 암호화 작업에 사용
    • ECC 알고리즘을 독점적으로 제공
    • CryptoKit는 HMACAESChaChaPolyP256P384P512 Curve25519 등의 암호화 방법을 제공
      • Cryptographic Hash
        • SHA215
        • SHA384
        • SHA512
      • Message Authentication Code
        • HMAC
      • Cipher
        • AES
        • ChaChaPoly
      • Public Key
        • P256
        • P384
        • P512
        • Curve25519
let privateKey = Curve25519.Signing.PrivateKey() // 개인키 생성
let publicKey = privateKey.publicKey // 공개키 생성

let signature = try privateKey.signature(for: messageDigest) // 서명
publicKey.isValidSignature(signature, for: messageDigest) // 검증

🔐 해시 함수의 개념과 활용 사례

1. 해시함수

해시함수(hash function)는 임의의 길이는 데이터 message를 고정 길이의 데이터 hash value, hash code, digest, hash로 매핑하는 함수임. 동일한 input에 대해서는 같은 결과를 도출한다!

1) 암호화 해시 함수
  • 해시함수의 일종, 해시값으로부터 원래의 입력값과의 관계를 찾기 어려운 성질을 가짐
  • 역상 저항성(pre-image resistance) : 해시값이 주어졌을 때, 해시값을 생성한 입력값을 찾는 것이 계산상 어려움. 제 1 역상 공격(주어진 해시값을 출력하는 입력값을 찾는 공격)에 대해 안전한 것을 말함
  • 제 2 역상 저항성(second pre-image resistance) : 해시값과 입력값이 주어졌을 때, 해시값을 생성하는 또 다른 입력값2를 찾는것이 계산상 어려움. 제 2 역상 공격(주어진 입력값과 같은 해시값을 출력하는 다른 입력값을 찾는 공격)에 대해 안전한 것을 말함
  • 충돌 저항성(collision resistance) : 같은 해시값을 생성하는 두 개의 입력값을 찾는 것이 계산상 어려워야 하며 해시충돌에 대해 안전해야 한다는 것을 말함
  • 여기서 제 2 역상 저항성과 충돌 저항성의 차이점은, 제 2 역상 저항성에서는 공격자가 입력값 외 동일한 해시값을 생성하는 입력값2를 찾는 것이고, 충돌 저항성은 공격자가 동일한 해시값을 생성하는 입력값 두개를 자유롭게 선택할 수 있음
  • 해시충돌이란? 해시 함수가 서로 다른 두개의 입력값에 대해 같은 출력값을 도출하는 상황. 무한한 입력값을 받아 유한한 출력값을 생성하는 경우, 비둘기집 원리에 의해 해시충돌은 항상 존재한다..!
  • 비둘기집 원리
  • n + 1개의 물건을 n개의 상자에 넣을 때 어느 한 상자에는 두 개 이상의 물건이 들어 있다는 원리
  • 5명의 사람을 서로 같은 팀이 되지 않도록 4개의 팀에 나누는 것은 불가능함
  • 생일 문제
  • 사람이 임의로 모였을 때 그 중에 생일이 같은 두명이 존재할 확률을 구하는 문제
  • 1년은 365일이므로 366명이 모인다면 비둘기집의 원리에 따라 생일이 같은 두 명이 반드시 존재함
  • 23명만 모여도 두 명이 생일이 같을 확률은 50%가 넘고, 57명이 모이면 99%를 넘어감
    2) 순환 중복 검사(CRC, cyclic redundancy check)
  • 네트워크 등을 통하여 데이터를 전송할 때, 전송된 데이터에 오류가 있는지 확인하는 방식
  • 데이터를 전송하기 전, 주어진 데이터에 대한 CRC 값을 계산하여 데이터에 붙여 전송함
  • 받은 데이터의 값으로 CRC 값을 계산해 오류가 덧붙여 전송되었는지 확인
  • 주어진 CRC값을 가지는 다른 데이터를 만들기 쉬움 => 제 2 역상 저항성, 충돌 저항성을 가지지 않음

2. 해시 - 알고리즘

위 같은 해시함수에 사용되는 알고리즘으로는 MD(Message-Digest Algorithm)과 SHA(Secure Hash Algorithm)등이 있음. 각 알고리즘은 심각한 해시 충돌 문제 등으로 인해 해시 함수를 개선하며, 발표된 순서대로 MDn, SHA-n 식으로 넘버링됨

1) MD(Message-Digest Algorithm)

3. iOS에서의 활용 사례들

Xcode 내에서 제공하는 함수를 이용하여 MD5 또는 SHA256 해시를 생성할 수 있음. 그리고 이제 Apple이 지원하는 CrytoKit을 사용하여 다양한 암호화가 가능해졌음. 사용자의 비밀번호를 저장하는 방법에 평문으로 저장하기 보다는 해시 값을 이용하여 저장하고, 파일의 해시 값을 비교하여 파일의 무결성을 검증하는 등으로 응용할 수 있음

1) 대상 통으로 해시 생성
스크린샷 2024-04-22 오후 8 18 29

위 코드들은 CommonDigest.h 의 헤더파일의 일부이고 위 함수와 구조체를 이용하여 해시값을 구할 수 있음

CC_MD5함수를 이용하여 해시 값을 구할 수 있음 unsigned char CC_MD5(const void source, CC_LONG len, unsigned char *dest)

CC_MD5함수와 CC_SHA256함수는 unsigned char * 를 리턴하고 있으나, 일반적으로 아래 예시처럼 리턴 값을 사용하고 있지 않지만 리턴 값은 md파라미터를 통해 전달된 포인터를 반환함

#import <CommonCrypto/CommonDigest.h>

NSString *md5(NSString *str) {
    const char *cStr = [str UTF8String];
    unsigned char result[CC_MD5_Digest_LENGTH];
    CC_MD5(cStr, strlen(cStr), result);
    return [NSString stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
    result[0], result[1],
    result[2], result[3],
    result[4], result[5],
    result[6], result[7],
    result[8], result[9], 
    result[10], result[11], 
    result[12], result[13], 
    result[14], result[15], 
    ];                        
}
NSString *digest = md5(@"test");
NSLog(@"MD5 TEST %@", digest);

이 코드는 MD5 해싱소스 코드임. 여기서 MD5를 SHA256으로 치환하고, result의 배열인덱스를 0~ 31 까지 받아내면 SHA256소스가 된다.

2) 버퍼를 이용하여 해시 생성

 예를 들어 크기가 큰 파일의 해시 값을 구한다고 가정할 때, 위의 방법을 이용하면 파일 전체를 메모리에 올려야 하기때문에 저사양 단말에서는 큰 부담을 가지게 될 수 있음! 따라서 파일의 일부를 버퍼를 이용하여 해시 값을 업데이트함으로써, 메모리사용량을 줄 일 수 있다고 함

static int md5useUpdate(char* data, long len, char** outData)
{
    unsigned char hashBuf[BUF_LEN] = {0,};       //업데이트에 사용할 버퍼
    long curSize = 0;                            //현재 까지 읽은 데이터 사이즈
    long remainSize = len;                       //남아있는 데이터 사이즈
    //md5 컨텍스트 선언 및 초기화
    CC_MD5_CTX md5;
    CC_MD5_Init(&md5);
    //더 읽어야할 데이터가 남아있다면
    while(curSize < len) {
        if(remainSize < BUF_LEN){ //남은 사이즈가 버퍼보다 작을 경우, 남아있는 사이즈만큼만 메모리 복사 및 해시 업데이트
            memcpy(hashBuf, (char *)data, remainSize);
            CC_MD5_Update(&md5, hashBuf, (CC_LONG)remainSize);
        }
        else { //일반적인 경우 버퍼사이즈(1024)만큼 메모리 복사 및 해시 업데이트
            memcpy(hashBuf, (char *)data, BUF_LEN);
            CC_MD5_Update(&md5, hashBuf, (CC_LONG)BUF_LEN);
        }
        data += BUF_LEN;                //메모리 읽은 후 포인터 이동
        curSize += BUF_LEN;             //현재까지 읽은 사이즈 계산
        remainSize -= BUF_LEN;          //남아있는 사이즈 계산
    }
    unsigned char digest[CC_MD5_DIGEST_LENGTH] = {0,};
    CC_MD5_Final (digest, &md5);
    char *tmpOut = *outData;
    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
    {
        sprintf(tmpOut, "%02x", digest[i]);
        tmpOut += 2;
    }
    return 0;
}

 정의한 함수 md5useUpdate는 해싱할 데이터와 사이즈, 해싱한 문자열을 기록할 데이터의 포인터를 입력받는다.

3) CryptoKit을 사용한 암호화

위 두가지방법을 예전엔 주로 사용한 것 같으나.. 이젠 Apple에서 지원한다.

  1. Normal Hashing Swift에서도 hashing을 제공하며, CryptoKit에서 제공하는 hashing 방법과 다른 메서드를 사용한다. Swift는 Hashable 프로토콜을 제공하고, Set이나 Dictionary 타입의 데이터를 Hashing하고 싶다면 Hashable 프로토콜을 따라야 한다. String이나 Int는 Hashable 프로토콜을 따르고 있어서 문자열, 숫자 데이터는 바로 hashing이 가능함
/// 리터럴 값을 해싱하면 hash value이 나온다. /// haser로 만든 hash value는 계산할 때마다 다른 값이 나온다. 
func hashItem(item: String) -> Int { 
    var hasher = Hasher() 
    item.hash(into: &hasher) 
    return hasher.finalize() 
} 

let hashvalue = hashItem(item: "brown fox")
  1. 1-2. Cyptrograhpic Hashing (SHA) CryptoKit이 제공하는 Cryptographic hashing은 SHA-2(Secure Hash Algorithm 2) 방식을 지원함 digest의 크기에 따라 3가지로 나뉜다. 예를 들어 SHA256의 digest는 256 비트를 가진다.

digest로 데이터 무결성을 확인할 수 있음

import CryptoKit /// 이미지 데이터를 받아온다. 
func getData(for item: String, of type: String) -> Data { 
    let filePath = Bundle.main.path(forResource: item, ofType: type)! 
    return FileManager.default.contents(atPath: filePath)! 
} 

let data = getData(for: "Baby", of: "png") 
UIImage(data: data) 

/// 256-bit의 digest를 만든다. 
let digest = SHA256.hash(data: data) 

// 송신자는 'data'와 'digest'를 수신자에게 보낸다.
// 수신자는 'data'를 cryptographic hashing한 값과 'digest'를 비교한다. 같다면 데이터는 무결한 상태이다. 
let receivedDataDigest = SHA256.hash(data: data) 
if digest == receivedDataDigest { 
    print("보낸 data == 받은 data") 
}
  1. 데이터 송신자는 dataCryptographic hashing의 결과 값 digest와 같이 실어 보낸다.
  2. 데이터 수신자는 data를 Cryptographic hashing한 값과 전달받은 digest를 비교한다. 값이 똑같으면 데이터에 손상 없고, 그렇지 않으면 손상이 있는 것

CrytoKit이 제공하는 암호화나 인증방식이 꽤나 다양하기 때문에 여기에서 필요할 때 공부하는 걸 추천한다 ㅎㅎ

참고