IT한 것

python으로 SSL/TLS 사용시 인증서 오류와 대처법

lovian 2021. 4. 2. 22:24

이미 많이들 겪고 해결해서 아는 일들이겠지만, 최근에야 겨우 파이썬을 접해보면서 정리 삼아 남겨본다.

 

 

1. self signed certificate in certificate 오류

아래와 같이 서버인증서 검증에 실패했다고 나온다.

  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py", line 1040, in _create
    self.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1123)

SSL/TLS는 클라이언트가 서버의 인증서를 받아 검증하고, 검증이 통과한 경우에 다음 단계를 진행한다.

그러나 모종의 이유로 서버의 인증서를 올바르게 검증하지 못하는 상황이 발생하면, 위와 같은 오류가 발생 할 수 있다. (파이썬만 그런게 아니라 SSL/TLS 프로토콜 자체가 그렇다)

 

위에 상황은 모종의 이유중에서 개발 환경에서 발생하기 쉬운데, 그 이유는 공식적으로 신뢰되는 인증기관에서 생성한 인증서를 사용하지 않고 직접 인증서를 생성해서 사용하여 공식적으로 신뢰 할 수 없기 때문이다.

 

이를 해결하려면, 해당 SSL/TLS 서버의 인증서를 신뢰한다고 설정 해야한다.

파이썬에서는 라이브러리 부자? 답게 아래와같은 코드를 적용하면 쉽게 해결 할 수 있다.

※ SSL/TLS 서버의 ROOT 인증서를 전달 받아 직접 클라이언트 코드에 신뢰한다는 설정을 한다.

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations('../res/rootca.crt')

 

2. Hostname mismatch 오류

아래의 오류과 같이 서버인증서 검증에 실패했고, Hostname이 일치하지 않는다고 나온다

  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py", line 1040, in _create
    self.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'localhost'. (_ssl.c:1123)

SSL/TLS 프로토콜은 서버의 인증서를 받아... 하는 부분은 1번과 동일하다.

다만 인증서 검증 절차가 단순히 한가지가 아니다라는 점을 알아야 이 문제를 해결 할 수 있다.

 

우리가 사용하는 대부분의 인증서는 (기존의 공인인증서를 포함해서) X.509라는 표준 규격에 따라 만들어져 있고, SSL/TLS에 사용하는 서버인증서는 이 규격에서 특수한 필드를 더 사용한다.

Subject Alternative라는 필드인데 이 항목에 SSL/TLS 서버의 IP Address또는 domain name을 지정 하여 사용하고, 인증서에 이 필드가 존재하는 경우 현재 서버와 일치하는지 확인을 한다.

 

※ 요약하면 SSL/TLS 서버 인증서에는 IP, DOMAIN을 지정하기 때문에 이 값을 올바르게 맞춰야 함

 

실 서비스를 하는 경우 잘 맞추어 사용하면 되지만, 개발 환경에서는 귀찮은 일이 아닐 수 없다.

 

역시나 라이브러리 부자?는 모두 가지고 있었다.

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations('../res/rootca.crt')
context.check_hostname = False

아까 설정했던 부분에서 다음줄에 hostname 확인을 끄는 옵션을 추가했다.

 

보통 이정도면, 파이썬 환경에서 SSL/TLS로 통신할 때 귀찮은 설정을 대부분 통과 할 수 있을 것 같다.

 

※ 제가 작업한건 HTTPS가 아닌, 단순 SSL/TLS이므로 HTTPS 를 사용하는 분들은 제가 사용한 것과 비슷한 옵션을 찾아보시면 될 것 같습니다