이번에 들어가게 되는 신규 프로젝트에서 유저관리 부분을 담당하게 되었다.
신규 프로젝트이다 보니 다양한 세팅에 대해서 신경 써야 했고, 그중 생소했던 암호화에 대해 글을 작성해보고자 한다.
우선 혼동했던 단어들에 대해서 정의를 내려보자.
1. Base64 인코딩
- 바이너리 데이터를 텍스트로 바꾸는 엔코딩을 의미한다. 64진법 ascii 문자열 들로만 이루어진 문자열로 변환하는 것
2. 암호화(Encryption) / 해시(Hashing)
- 암호화의 목적은 데이터의 기밀성을 보호하고, 비밀번호와 같은 개인 정보를 안전하게 저장 및 전송하기 위해 사용
- 암호화된 데이터는 암호화 알고리즘을 사용하여 암호문으로 변환한다. 변환된 암호문은 특정키를 필요로 하며, 키를 이용해 평문으로 복호화 가능하다.
- 해시의 목적은 데이터의 무결성을 확인하고, 데이터의 고유한 표현을 생성하기 위해 사용된다. (주로 비밀번호에 많이 사용됨)
- 해시는 해시함수를 이용하여 고정길이의 해시 값으로 변환된다. 이는 단방향 함수이며 원래 데이터로 역으로 복원 불가능하다.
3. Sha512, Aes256 뒤에 붙는 숫자와 차이
- Sha는 대표적인 해시함수이다. (Secure Hash Algorithm) 즉 64바이트, 32바이트 등 해시의 길이를 나타낸다. 당연히 숫자가 클수록 보안강도가 높으며 처리속도는 느리다.
- Aes는 대표적인 암호화 알고리즘이다. (Advanced Encryption Standard) 즉 암-복호화하는데 필요한 키의 길이를 나타낸다. 당연히 숫자가 클수록 보안강도가 높으며 처리속도는 느리다.
4. 고에서 제공되는 대표적인 암호화 알고리즘
- Aes "고급 암호화 표준" 암호화, 복호화에 동일한 키를 사용하는 대칭키 알고리즘을 의미한다.
- Des "데이터 암호화 표준" 암호화, 복화에 동일한 키를 사용하며 aes 암호화알고리즘 이전에 주로 사용되었다. (보안표준을 충족하지 못해 사용을 권장하지 않는다.)
- Rsa "비대칭 암호화" 위의 두 종류의 암호화 알고리즘과 달리 공개키, 비밀키를 이용해 암복호화를 하는 방법을 의미한다.
ex) A 가 B에게 정보를 보낼 때 B의 공개키를 이용해 암호화를 해 보내고, B는 B의 비밀키를 이용해 복화가 가능하다.
AES 암호화 방법
func encryptAes(token string) (string, error) {
cip, err := aes.NewCipher([]byte(sixteen))
if err != nil {
return "", err
}
length := (len(token) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, token)
pad := byte(len(plain) - len(token))
for i := len(token); i < len(plain); i++ {
plain[i] = pad
}
encrypted := make([]byte, len(plain))
for bs, be := 0, cip.BlockSize(); bs <= len(token); bs, be = bs+cip.BlockSize(), be+cip.BlockSize() {
cip.Encrypt(encrypted[bs:be], plain[bs:be])
}
return hex.EncodeToString(encrypted), nil
}
token 은 단순하게 json을 바이너리 데이터로 변경해서 넘긴 값을 의미한다.
cip, err := aes.NewCipher([]byte(sixteen))
aes 암호화 알고리즘의 NewCiper는 ciper.Block 인터페이스를 반환한다.
해당 인터페이스는 암호화, 복호화 그리고 블록의 사이즈를 함수로 구현을 정의하고 있다.
왜? 대칭키 암호화 이니깐 암-복호화 함수가 필요하다.
length := (len(token) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, token)
for i := len(token); i < len(plain); i++ {
plain[i] = pad
}
블록의 사이즈 즉 sixteen이라는 블록의 사이즈 단위로 데이터 암호화를 진행한다.
따라서 해당 sixteen 블록 사이즈에 맞게 plain이라는 바이너리 데이터를 생성하고 남은 공간(패딩)을 채워준다.
encrypted := make([]byte, len(plain))
for bs, be := 0, cip.BlockSize(); bs <= len(token); bs, be = bs+cip.BlockSize(), be+cip.BlockSize() {
cip.Encrypt(encrypted[bs:be], plain[bs:be])
}
선정된 블록단위로 (결괏값, 입력값) cip.Encrypt에 넘겨주는데 이 Encrypt는 ciper.Block 인터페이스의 함수중 하나이다.
이후 암호화된 바이트 슬라이스를 16진수 문자열로 반환한다.
암호화를 진행했으면 복호화도 작성해 보자.
Aes 복호화 방법
func decryptAes(hexStr string) (string, error) {
encrypted, err := hex.DecodeString(hexStr)
if err != nil {
return "", err
}
cip, err := aes.NewCipher([]byte(sixteen))
if err != nil {
return "", err
}
decrypted := make([]byte, len(encrypted))
for bs, be := 0, cip.BlockSize(); bs < len(encrypted); bs, be = bs+cip.BlockSize(), be+cip.BlockSize() {
cip.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return string(decrypted[:trim]), nil
}
16진수 인코딩 된 암호화 문자열을 바이트 슬라이스로 디코딩한다.
aes 알고리즘을 이용해 암호화를 진행했으니 복호화도 동일하게 aes의 사이퍼를 생성한다.
복호화 과정에서는 암호화 과정에서 진행한 패딩을 추가하는 작업이 필요치 않다.
이미 블록사이즈 단위로 패딩이 되어 있기 때문에 블록단위로 복호화를 진행하면 된다.
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
패딩을 제거해 줄 때는 패딩의 시작점부터 패딩의 사이즈가 기록된다.
암호화 과정 중에 pad를 남는 모든 공간에 채워주는 부분이 있는데 그것을 이렇게 활용한다.
사용방법
type user struct {
Id string
Tier string
Win int
Lose int
}
func main() {
u := user{"guiwoo", "gold", 10, 10}
b, _ := json.Marshal(&u)
rs, _ := encryptAes(string(b))
fmt.Println("encrypted \n", rs)
data, _ := decryptAes(rs)
var found user
json.Unmarshal([]byte(data), &found)
fmt.Printf("user : %+v", found)
}
1. json 엔코더를 이용해 바이너리 데이터를 작성했다.
2. encryptAes를 이용해 암호화 진행
결괏값
encrypted
b56e6b2a5bb2142d57eafba52696e5ac53912f5dbcb5e9b30c23fa2764e11c65ab182f6572f22445e0a479f434be549b6a10f8e053a
3. decryptAes를 이용해 복호화 진행
결괏값
user : {Id:guiwoo Tier:gold Win:10 Lose:10}
대칭키 암호화를 선정한 이유로는 데이터의 기밀성을 유지와 안전한 전송이 암호화의 목적이었으며,
비대칭키 암호화는 전자서명 및 키교환과 같은 특정한 용도에 적합하여 대칭키 암호화를 채택하였다.
'Go > Go Basic' 카테고리의 다른 글
Parser 성능개선 (pprof) (3) | 2024.10.29 |
---|---|
Env-Config (kelseyhightower/envconfig) (2) | 2023.10.16 |
Ultimate-Go-06 [에러처리] (0) | 2023.09.19 |
Ultimate-Go-04 [디커플링] (0) | 2023.09.13 |
Ultimate-Go-03 (2) | 2023.09.07 |