연습

SSL Handshake, Cipher Suite

onaeonae1 2024. 5. 14. 23:36

handshake 과정에 대해 구체적으로 파악 및 Cipher Suite에 대해 알아보자

1. SSL 통신 복습, handshake 중점으로

  • SSL 통신은 공개키와 대칭키를 혼합해서 사용
    • 대칭키로 실제 데이터를 암호화
    • SSL 인증서의 웹 서버 공개키로 대칭키를 안전하게 공유
  • CA의 SSL 인증서를 통해 다음과 같은 기본적인 보안기능 수행
    1. 신뢰할 수 있는 웹서비스 확인 → SSL 인증서가 CA 공개키로 decode 가능
    2. 통신내용 암호화 → 대칭키 생성 및 Server 공개키로 안전하게 공유
  • SSL 통신 과정은 다음과 같은 순서로 이루어진다
    • handshake, session, session 종료
  • 여기서 handshake에서는 다음과 같은 역할을 수행
    1. 암호화 알고리즘 결정둘의 교집합을 Server 에서 정해서 알려줌
    2. Client 와 Server 가 서로 가능한 암호화 알고리즘을 공유
    3. 암호화에 사용할 대칭키(=session-key 생성)Client 측에서 서로의 random data로 pre-master-key 생성, 암호화Server에서는 pre-master-key를 Server 비공개키로 decode둘 사이에 공유된 master-key가 session-key 라고 불림. 데이터 대칭키 암호화에 사용
    4. 이를 master-key로 확정, Client 와 Sever 공유됨(Client가 생성, server 가 decode)
    5. 암호화된 pre-master-key를 Server에 전송
    6. Client 와 Server 각각에서 random data 를 생성, Server에서 Client에 random data 넘김
  • 어제는 개념적으로 이에 대해 파악했는데 좀 더 구체적으로 알아보자

2. Handshake 과정

handshake 과정

우선 handshake 의 목적은 크게 2개

  1. (대칭키) 암호화 알고리즘(Cipher Suite) 결정
  2. (대칭키) 암호화 알고리즘에 사용할 키 공유

2-1. Client Hello

  • handshake의 시작 단계
  • Server 에 연결을 시작하며 보내는 패킷
  • 다음과 같은 데이터를 전송
    1. SSL Protocol Version
    2. Session ID
    3. 클라이언트 측 암호화 알고리즘 목록(Cipher Suites)

2-2. Server Hello

  • ClientHello로 packet 이 전송되면 수행됨
  • Client 로부터 받은 Cipher Suites 에서 하나를 선택 = 서로 사용할 암호화 알고리즘을 선택한 것
  • random data를 생성(이후 대칭키 생성에 사용됨)

2-3. Certificate

  • Server 가 자신의 SSL 인증서를 Client 에 전달
  • 인증서에는 Server 정보(도메인 등), Server 공개키가 들어있음 CA 비공개키로 encode된 상태
  • Client는 SSL 인증서를 CA 공개키로 decode, 신뢰할 수 있는 서비스임을 확인(=전자 서명)
  • (사진)

2-4. Server Key Exchange, ServerHello Done

  • Server Key Exchange: Server 공개키를 Client 에게 전달하기 위해 필요한 과정.
  • Server Key Exchage 는 필수 아님. (SSL 인증서에 Server 공개키가 없는 경우에만 사용하므로)
  • SSL 인증서 내부에 있으면 Client 가 알아서 CA 공개키로 가져가므로 Server Key Exchange X
  • Server Key Exchange 가 실행 혹은 생략되었으면 ServerHello Done

2-5. Client Key Excnahge

  • 실제 데이터를 암호화할 목적으로 사용하는 대칭키를 공유하기 위한 것 (생략불가)
  • 대칭키(=pre-master-key)를 Client 측에서 생성 혹은 생성에 필요한 데이터 제공
    • 어제 공부한 내용에서는 간단히 random-data 를 조합한다고 정리
    • 그러나 실제로는 알고리즘마다 약간의 차이 존재
    • RSA 알고리즘 에서는 알려진 대로 client 측에서 random-data 조합
    • Diffie-Hellman(DH, DHE), ECDHE 알고리즘 등을 사용하면 Server 한테 생성 데이터 보냄
      • 보내준 데이터로 Client-Server 에서 서로 동일한 데이터(=대칭키) 생성 가능한게 특징
  • Server 공개키로 암호화해서 전달

2-6. ChangeCipherSpec, Finished

  • Client - Server 모두에게 보내는 packet
  • Packet으로 교환해야할 정보를 모두 교환했고, 이제부터 암호화 통신 시작할수 있다는 뜻

이렇게 SSL Handshake 에 대해 간단한 정리 완료. 나중에 실제 packet 을 뜯어보는게 도움 될 듯

3. Cipher Suites

3-1. 설명

  • ClientHello ServerHello 과정 전달되는 packet 에서는 Cipher Suite(s)가 전달된다.
  • 우선 packet을 살펴보자

Client Hello 에서는 Cipher Suites, 즉 Cipher Suite 의 목록을 보낸다.

 

Server 에서는 단일의 Cipher Suite를 보낸다. 즉 선택을 완료했다는 뜻

  • Cipher Suite는 사용할, 혹은 가능한 암호화 알고리즘에 대한 것이다.
  • 아까 handshake 에 대해서 설명하면서 상당히 간략하게 "암호화" 한다라고 설명
  • 그러나 이러한 암호화를 어케 할 것인지 다양한 알고리즘이 존재. 이들을 특정해줘야 함

3-2. Cipher Suites 구조

ServerHello의 Cipher Suite: 를 확인해보면 적당히 다음과 같은 구조임을 확인 가능

 

💡 Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)

 

이는 "_" 를 기준으로 의미를 가지는데 위의 예제를 바탕으로 의미를 파악해보자.

 

"TLS"

=> 프로토콜

  • TLS/SSL 프로토콜임을 의미
  • Cipher Suites에서 이부분은 항상 TLS로 고정이므로 큰 의미는 없다.

"ECDHE"

=> 대칭키 교환 방식

  • 아까 Client Key Exchange 에서 언급되었던 ECDHE 알고리즘을 사용중인 케이스
  • 이 경우는 Client Key Exchange에서 Server로 생성에 필요한 데이터를 줄 것 같네

"RSA"

=> 인증서 검증 방식

  • SSL 인증서를 어떤 알고리즘으로 풀어야 하는지 알려줌
  • 당연히 전부 공개키 방식임은 고정인데 그 안에서 약간 갈리는듯
  • 알려준 알고리즘에 브라우저가 가지고 있는 CA 공개키를 사용하면 됨

WITH은 일단 생략, 무조건 들어가는 부분임 TLS 처럼

 

"AES_128"

=> 데이터 암호화에 사용할 대칭키 암호화 알고리즘

  • AES_128은 128 bit 단위로 암호화하는 블록 암호화 알고리즘임
  • 블록 암호화 알고리즘이 뭐냐면 특정 단위로 데이터를 암호화 한다는 것
  • 대칭키 알고리즘이니깐 같은 Key 로 encode, decode 가능
  • 세부적으로 보려면 암호학 관련 지식이 필요
  • 예전에 pycryptodome 으로 AES, CBC 모드 로 구현한게 있는데 첨부함 ㄱㄷ

"GCM"

=> 블록 암호화 알고리즘에서 사용할 모드

  • 블록 암호화 알고리즘에는 CBC, GCM, ECB, OFB 등 많은 모드들 존재
  • 우선은 블록 암호화 알고리즘에 대한 추가설명 정도로 이해할 수 있을 듯

"SHA384"

=> 무결성 확인에 사용할 hash 알고리즘

  • handshake 완료 후 암호화된 통신을 할 때에 추가적으로 무결성 검증을 함
  • 주고받는 암호화된 데이터가 누군가의 변조를 거치지 않았는지 hash로 검증
  • hash 에 대해서는 자구나 알고리즘 등에서 한번 접했을 것으로 예상
  • 입력값에 대해 동일한 출력값. 출력값에서 입력값으로 돌리기 ㅈㄴ 어려움
  • 비밀번호를 DB에 저장할 때 이 기능을 사용(rainbow attack 등이 존재하긴 함)

이렇게 Cipher Suite에 대해서 정리할 수 있을 것 같다.

자세하게 알기 위해서는 우선 암호학 관련 지식이 필요할 것 같다.

4. (추가) pycryptodome 을 사용한 AES CBC 코드

  • 8월 말에 CVE lao bomb 을 구현하면서 AES 관련 공부를 할 일이 생겼다.
  • 간단하게 pycryptodome 으로 AES CBC 코드를 구현해봄
  • 세부적인 암호화 알고리즘에 대해서는 아직 좀 부족해서 설명은 못하겠음
    • 블록 암호화라서 16 단위로 맞춰줘야함, 이를 위해서 padding 을 추가해줌
    • AES는 여러 bit 들이 존재하는데 16단위로 존재하므로 pycryptodome 에서 알아서 선택
    • iv, key가 같아야 encode, decode 가 모두 가능함
  • 일단 코드만 올림 나중에 설명할 수 있는 수준으로 공부해야겠다.
import random
import string

from Cryptodome.Cipher import AES

class AESCryptoCBC:
    def __init__(self, key):
        iv = bytes([0x00] * 16)
        self.crypto = AES.new(key, AES.MODE_CBC, iv)

    def encrypt(self, data: str) -> bytes:
        data = self.check_padding(data)

        data = bytes(str.encode(data, encoding="utf8"))
        enc = self.crypto.encrypt(data)
        return enc

    def decrypt(self, encrypted_data: bytes):
        dec = self.crypto.decrypt(encrypted_data).decode("utf8")
        return dec

    def check_padding(self, token:str):
        padding = "".join(
            random.choice(string.ascii_letters + string.digits) for _ in range(16)
        )
        if not len(token) % 16 == 0:
            pad_length = 16 - len(token) % 16
            pad = padding[:pad_length]
            token = token + pad
        return token

def local_test(payload):
    key = [
        0x10,
        0x01,
        0x15,
        0x1B,
        0xA1,
        0x11,
        0x57,
        0x72,
        0x6C,
        0x21,
        0x56,
        0x57,
        0x62,
        0x16,
        0x05,
        0x3D,
        0xFF,
        0xFE,
        0x11,
        0x1B,
        0x21,
        0x31,
        0x57,
        0x72,
        0x6B,
        0x21,
        0xA6,
        0xA7,
        0x6E,
        0xE6,
        0xE5,
        0x3F,
    ]
    data = payload
    origin_length = len(data)
    # 출력
    print("Data is " + str(data))

    # 키 생성
    aes1 = AESCryptoCBC(bytes(key))
    # 변경
    encrypted_data = aes1.encrypt(data)
    print("The encrypted value is " + str(list(encrypted_data)))

    # encrypt 와 decrypt 는 같은 cipher 에서 불가. 무조건 새로 생성해야함 (AES.new)
    aes2 = AESCryptoCBC(bytes(key))
    dec = aes2.decrypt(bytes(encrypted_data))[:origin_length]

    print(f"The Decrypted Value is: {''.join(dec)}")

local_test("abc def ghi jkl mno")

실행 결과 제대로 나오고 있다.

5. 참조

Cipher Suite

SSL HandShake