티스토리 뷰

golang

gRPC SSL/TLS 3. 실제 구현

fistful 2019. 7. 1. 18:47
반응형

개요

 

1) 암호에 대해 간단히 흝어보았고

2) SSL/TLS 동작 원리를 이해했다.

3) 이제 gRPC 에서 SSL/TLS 어떻게 다루면 알아보자.

- 사실 handshake gRPC 알아서 해준다.

 

참고링크

 

공식사이트 설명: https://grpc.io/docs/guides/auth/

Udemy gRPC 강좌

- https://www.udemy.com/grpc-golang/learn/lecture/11018820#overview

- https://www.udemy.com/grpc-golang/learn/lecture/11018822#overview

매우 좋은 OpenSSL 활용 한글 설명

- https://www.lesstif.com/pages/viewpage.action?pageId=6979614

- https://www.lesstif.com/pages/viewpage.action?pageId=7635159

 

이론

 

gRPC 실제 서비스를 할때는 암호화는 기본적으로 해주자. SSL 사용하면 된다.

SSL 사용하면 중간에서 데이터를 가로채어 보는게 불가능해진다. MITM (Man In The Middle)

gRPC SSL 암호화를 있고, 인증도 있다. (encryption, authentication) - 여기서는 encryption 보겠음

 

SSL TLS 관계는?

 

SSL 1.0, 2.0, 3.0 까지 이어지다가

TLS 바뀌며 1.0, 1.1, 1.2, 1.3 까지 업데이트 되고 있다.

* SSL 3.0 까지도 해킹 방법이 있는 걸로 알고 있음

캡처 이미지 출처: https://www.wikiwand.com/en/Transport_Layer_Security

 

이제 실제로 구현해보자

 

1) CA (Certificate Authority) 설정

2) 서버Certificate 설정

3) 서버 Certificate 서명

 

4) 서버에서 TLS 사용하도록 구현

5) 클라이언트가 TLS 통해 서버와 연결하도록 구현

 

관련 링크

- https://grpc.io/docs/guides/auth/

- https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-auth-support.md

 

키와 서명들을 만들어보자

 

Udemy 강좌에 나와있는 설명들을 참고하여 만들었다.

 

생성 파일

공유 여부

설명

rootca.key

절대 외부 유출 불가

Certificate Authority Private key. 외부의 CA 이용한다면 고객인 우리는 절대 없는 키값이다.

rootca.crt

클라이언트에게 공유됨

Certificate Authority trust certificate. CA 클라이언트 들에게 나눠주는 것이다.

- 이걸로 인증서가 CA 에서 인증해준게 맞는지를 verify 있다.

server.key

절대 외부 유출 불가

Server   Private key. 서버측 운영자 말고는 아무도 모르는 맞다.

server.csr

서버가 CA 에게 보냄

Server CA 에게 인증서를 만들어달라면서 보내는 것이다.

- "서버인 내가 나의 정보와 공개키를 보내줄테니"

- "CA 너가 나의 인증서를 만들어 주렴"

server.crt

서버가 클라이언트에게 공유

CA 서명해준 서버의 인증서.

Server 가지고 있다가 클라이언트에게 공유해줌.

" 인증받은 서버이니깐 확인해봐"

server.pem

절대 외부 유출 불가

Server   Private key server.key gRPC 좋아하는 포맷으로 변환한 .

이걸 가지고 클라이언트에서 보내온 메시지를 복호화 것이다

 

Openssl 윈도우에 설치하기

 

빌드된 바이너리는 다음에서 찾을 있다: https://wiki.openssl.org/index.php/Binaries

중에서 여기를 선택하여 Win64_OpenSSL v1.1.0k 설치하였다. https://slproweb.com/products/Win32OpenSSL.html

 

만드는 방법 (on Windows 10)

 

 

참고 링크

 

- OpenSSL 모든 명령들: https://www.openssl.org/docs/man1.1.0/man1/

- OpenSSL 명령 전반을 이해할 있는 CheetSheet: https://www.freecodecamp.org/news/openssl-command-cheatsheet-b441be1e8c4a/

- 매우 좋은 OpenSSL 활용 한글 설명

- https://www.lesstif.com/pages/viewpage.action?pageId=6979614

- https://www.lesstif.com/pages/viewpage.action?pageId=7635159

 

 

1) rootca.key

 

우리가 CA, 인증기관이라고 하면, 인증하는 서명을 해주려면 비대칭키가 필요하다.

genrsa 인증기관의 개인키를 만들어준다.

 

명령어

openssl genrsa -aes256 -out rootca.key 2048

https://www.openssl.org/docs/man1.1.0/man1/openssl-genrsa.html

설명

genrsa

RSA 방식의 비대칭키 (=공개키, 개인키 ) 생성하라. RSA 말고 대표적인 방식으로는 ECDSA 있다.

-aes256

- 생성한 키를 대칭키 암호화 방식인  AES256 으로 한번 암호화 하는 것이다.

- 다른 암호 (DES3 등등) 수도 있겠지만 AES256 이면 무난.

- 명령줄에 이를 위한 대칭키 암호를 설정해 넣을 수도 있지만, 이렇게만 명령해도 암호를 뭘로 물어본다.

-out rootca.key

생성되는 개인키의 이름이다.

(참고) 개인키만 생성하면 거기에 짝이 되는 공개키를 생성할 있다.

2048

생성되는 개인키의 비트수이다

 

 

2. rootca.crt

 

RootCA 에게 인증서를 발급해줄 인증기관이 없으니, 자기자신에게 이렇게 발급해준다.

인증서를 클라이언트에게 주면, 서버로부터 오는 (RootCA 인증한) 인증서를 verify 있다.

 

명령어

set SERVER_CN=localhost

openssl req -new -x509 -days 365 -key rootca.key -out rootca.crt -subj "/CN=%SERVER_CN%"

https://www.openssl.org/docs/man1.1.0/man1/req.html

설명

SERVER_CN

인증서를 발급할 서버의 이름이 Common name 이며,

여기서는 종종 쓰이기에 환경변수 SERVER_CN 으로 설정해서 쓰기로 한다. (윈도우 환경임)

req

1) 인증서를 발급해달라 요청하는 Certificate reqeust 생성해주는 기능을 한다.

2) 그런데 Root CA 경우는 인증서를 발급해줄 데가 자기자신 밖에 없다.

- 이런걸 self signed certificate 라고 하며, 명령이 그걸 하는 거다.

-new

새로운 certificate request 생성해준다.

하지만 아래 -x509 설정해주면 certificate request 아닌 self signed certificate 생성해준다

-x509

옵션을 걸면, self signed certificate 생성해준다

-days 365

유효기간을 365일로  설정하였다.

-key rootca.key

인증서를 발급하려면 private key 필요하다.

만들어둔 Root CA 개인키인 rootca.key 사용하겠다는 의미이다.

-out rootca.crt

만들어질 인증서 파일의 이름

-subj "/CN=%SERVER_CN%"

인증서에는 다양한 정보를 담을 있는데 /CN 이라면 Common name 이라는 키를 말하고,

localhost 값으로 설정하였다. 만약 서비스를 제공하는 서버가 123.123.123.123 이라면 값이 들어갈거다.

 

명령으로 만든 rootca.crt 파일을 열어보았다.

 

이전 포스팅에서 보아서 익숙한 X509 포맷이다.

 

1) X509 버전이 V3 이고

2) 일련번호는 만들어준 인증서의 일련번호

3) 서명에는 SHA256 RSA 사용되었다.

4) 여기서는 발급자와 주체가 같다. CA Server 같다는

5) 유효기간은 1년으로 위에서 -days 365 설정한것과 같다.

6) 주체는 localhost 이다

 

아예 아래와 같이 .conf 파일을 만들어서 정보를 담은 인증서를 만들 수도 있다.

캡처한 원본 링크: https://www.lesstif.com/pages/viewpage.action?pageId=6979614

 

 

3) server.key / server.csr / server.crt

 

아래 과정은 명령만 공유하겠음. 내용을 찬찬히 따라왔다면 충분히 이해가 것임

cli command

설명

openssl genrsa -aes256 -out server.key 2048

1) server 개인키인 server.key 생성하고

set SERVER_CN=localhost

openssl req -new -key server.key -out server.csr -subj "/CN=%SERVER_CN%"

2) RootCA 에게 인증서 생성을 요청할 server.csr 만들고

- 아까 위에는 있던 -x509 빠져있다는게 다르다. 이러면 Certificate request 만들어진다.

- 이걸 만들어서 CA에게 주면 CA 인증서를 만들어주는 것이다.

openssl x509 -req -days 365 -in server.csr -CA rootca.crt -CAkey rootca.key -set_serial 01 -out server.crt

3) RootCA 인증서를 생성해준다.

- server.csr 받아서

- x509 형식으로

- 365 동안 유효한

- 일련번호가 01 인증서를

- RootCA 개인키인 rootca.kry 이용해서

- server.crt 인증서를 생성해준다

 

4) server.pem

 

- PKCS#8 개인키 정보 문법 표준이다. 개인키 값에 대한 문법을 정의해준다.

- 이걸 만드는 이유는 gRPC 에서 이걸 쓰기 때문이다.

 

명령어

openssl pkcs8 -topk8 -nocrypt -in server.key -out server.pem

https://www.openssl.org/docs/man1.1.0/man1/pkcs8.html

설명

genrsa

RSA 방식의 비대칭키 (=공개키, 개인키 ) 생성하라. RSA 말고 대표적인 방식으로는 ECDSA 있다.

-topk8

- 옵션이 없으면, pkcs8 은 PKCS#8 format key 를 입력 받아서 개인키로 출력해준다.

- 이 옵션이 있으면 개인키를 읽어서 PKCS#8 format key 로 출력해준다.

-nocrypt

암호화를 쓰지 않고 PrivateKeyInfo 구조가 출력된다.

-in server.key

-out server.pem

서버의 개인키인 server.key PKCS#8 포맷인 server.pem 으로 내보내겠다는

 

생성한 녀석들을 다시 정리해보자

 

생성 파일

누가 가지고 있나

설명

rootca.key

CA 가지고 있으면 된다.

인증기관인RootCA 라면 자신의 공개키, 개인키 쌍이 있어야 한다.

그래서 만든 개인키

rootca.crt

CA 자신이 인증한 인증서를

verify 필요가 있는 클라이언트에게 나눠준다.

인증기관이 만든 인증서는 인증기관의 공개키로 검증이 가능하다.

- 그래서 RootCA 자기 자신이 서명한 인증서를 만들어서 클라이언트에게 준다.

server.key

서버가 가지고 있는다.

서버도 클라이언트와 통신하기 위해서는 공개키, 개인키 쌍이 필요하다. 그래서 만든 개인키

이건 서버만이 알고 있어야 한다.

server.csr

서버가 하나 만들어서 CA 에게 준다.

, 한번 쓰고나면 실제 통신시에 일은 없다.

서버는 자신의 정보와 공개키를 가지고 인증서를 만들어달라는

.csr (Certificate request) 만들어서 CA 에게 준다.

server.crt

서버가 가지고 있다가 암호 통신을 원하는

클라이언트에게 전달

CA 서버에게 발급해주는 인증서이다.

서버가 접속을 요청하는 클라이언트에게 던져준다.

server.pem

서버가 가지고서 클라이언트가

서버의 공개키로 암호화한 메시지를 복호화 하는데 쓴다.

서버는 개인키를 가지고 있어야는데, gRPC 에서 쓰는.pem 포맷으로 변환한 것이다.

 

이렇게 생성하고 나면 실제 gRPC 통신시에 필요한 것은 3 뿐이다.

 

- 실제 코드를 보면 아래 파일만을 쓴다. (중복되는 부분이 있어도 반복학습이라 생각하고 양해를)

생성 파일

누가 어디에 쓰나?

rootca.crt

클라이언트가 가지고 있다가, 서버가 보내온 인증서를 verify 하는데 쓴다.

server.crt

서버가 가지고 있다가 암호 통신을 원하는 클라이언트에게 전달

server.pem

서버가 가지고서 클라이언트가 서버의 공개키로 암호화한 메시지를 복호화 하는데 쓴다.

 

 

Golang 구현

 

GitHub 구현코드 링크: https://github.com/nicewook/grpc-ssltls

 

서버

 

- 서버의 개인키를 PKCS#8 포맷으로 변환한 server.pem

- 서버의 공개키와 정보를 담은, CA 만들어준 인증서인 server.crt 탑재해주면 끝이다. 핸드셰이킹은 gRPC 알아서 해준다.

 

- 아래 코드는 Secure mode tls 변수로 on/off 해준다. 개념만 이해하면 이처럼 구현이 쉽다.

 

 

클라이언트

 

클라이언트는 rootca.crt 파일을 가지고 있다.

이것은 RootCA 자기 자신을 cert 해준 파일이다.

클라이언트는 이걸로 서버가 보내주는 certificate (여기서는 server.crt 파일) verify 하는 것이다.

 

 

 

정리

 

서버가 자신의 certificate (RootCA 만들어준, 서버의 개인키와 정보가 들어있는 파일) 클라이언트에게 주면

클라이언트는 rootca.crt 파일로 이걸 검증할 있을 것이다. 그러면 핸드세이크 시작!

- tls 옵션을 true, false 조합으로 바꿔가며 테스트 해보면 동작여부를 더욱 알기 쉬울 것이다.

 

반응형
댓글
  • 프로필사진 김개발 정리를 굉장히 잘하셨네요. gRPC를 도입 하는데 TLS 설정하는게 생각보다 어렵더라구요. 많은 도움이 되었습니다. 감사합니다. 2021.03.16 09:31
  • 프로필사진 fistful 도움이 되셨다니 기쁩니다. 정작 저는 현업에서 안쓰니 다 까먹었네요 2021.03.16 10:02 신고
  • 프로필사진 ian go로 이것저것 하는데 아리송했던 ssl 개념이 잡혔어요. 쭉 보고 있는데 좋은 글 감사합니다!! 2022.02.08 17:21
  • 프로필사진 익명 비밀댓글입니다 2022.02.10 13:55
  • 프로필사진 fistful 조금 찾아보았습니다.
    일단 WithInsecure는 deprcated 되었습니다.
    https://pkg.go.dev/google.golang.org/grpc#WithInsecure

    이걸 보시면 감이 오실 듯 싶습니다.
    https://stackoverflow.com/a/70482635

    2022.02.10 16:53 신고
  • 프로필사진 fistful 설명을 조금 덧붙입니다.
    1. 서버는 자신이 인증받은 crt를 클라이언트에게 줍니다.
    2. 문제는 클라이언트는 이 crt를 인증한 기관인 CA를 믿을 수 없다는 것입니다.
    - HTTPS로 치면 외부의 믿을만한 CA들의 공개키(제 글에서는 rootca.crt)가 아미 브라우저에 들어가 있어서 그걸로 확인이 가능합니다.

    3. 그래서 예제처럼 직접 만든 rootCA로 인증한 서버의 server.crt를 클라이언트가 받으면 선택지는 두 가지 입니다.
    1) rootCA의 공개키(쉽게 이야기해 rootca.crt)를 클라이언트가 가지고 있게 하기
    2) 그냥 서버가 보내주는 server.crt를 믿고, 검증하기 않기
    2022.02.10 16:58 신고
  • 프로필사진 Hamster Fail to greet: rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: x509: certificate relies on legacy Common Name field, use SANs instead"

    동일하게 해봤는데 이런 오류가 발생하네요 ㅠㅠ
    2022.03.01 04:55
  • 프로필사진 fistful 찾아보니 Go1.5 이후부터 SAN을 써야 하는 모양입니다. 해법과 편법이 소개되어 있는 링크를 아래와 같이 공유드립니다. 제가 실제로 해볼 시간까지는 안되네요. https://jfrog.com/knowledge-base/general-what-should-i-do-if-i-get-an-x509-certificate-relies-on-legacy-common-name-field-error/ 2022.03.01 14:22 신고
  • 프로필사진 Hamster 헛 바로 찾아주시다니.. 정말 감동입니다. 에러 내용도 CN 대신 SAN을 쓰라는 내용이네요.
    글 내용 잘 정리해주셔서 이해가 잘 됐습니다. 감사합니다 :)
    2022.03.01 16:19
댓글쓰기 폼