모두의 프린터는 어떠한 경우에도 회원가입, 카드결제를 요구하지 않습니다.

[Go언어/GOLANG] RSA 암호화/복호화

암호화 관련된 자료들은 워낙 찾아보면 많이 나오는 편이라서;; 근데 또 막상 찾아서 쓸라 하면 또 GPL이고 그래서 짜려니 귀찮고 그런경우가 많다 말이죠 ..

그런이유로 -_-;; 간단히 막 짜서 쓸려고 만든것좀 공유해 봅니다.

Go
//
// Author: PIROGOM
// https://modu-print.com
// mop.pirogom@gmail.com
// MIT License
//
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"io/ioutil"
)

type RSAHelper struct {
	PriKey *rsa.PrivateKey
	PubKey *rsa.PublicKey
}

/**
* RSA Key 생성
**/
func (r *RSAHelper) GenerateKey(bits int) error {
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)

	if err != nil {
		r.PriKey = nil
		r.PubKey = nil
		return err
	}

	r.PriKey = privateKey
	r.PubKey = &privateKey.PublicKey

	return nil
}

func (r *RSAHelper) GetPrivate() *rsa.PrivateKey {
	return r.PriKey
}

func (r *RSAHelper) GetPublic() *rsa.PublicKey {
	return r.PubKey
}

/**
*	PrivateToBytePEM
*	PrivateToStringPEM
**/
func (r *RSAHelper) PrivateToBytePEM() ([]byte, error) {
	if r.PriKey == nil {
		return nil, errors.New("private rsa key is nil")
	}

	privateKeyBytes := x509.MarshalPKCS1PrivateKey(r.PriKey)
	privateKeyPEM := pem.EncodeToMemory(
		&pem.Block{
			Type:  "RSA PRIVATE KEY",
			Bytes: privateKeyBytes,
		},
	)
	return privateKeyPEM, nil
}

func (r *RSAHelper) PrivateToStringPEM() (string, error) {
	pemBuf, err := r.PrivateToBytePEM()

	if err != nil {
		return "", err
	}
	return string(pemBuf), nil
}

/**
*	PrivateFromBytePEM
*	PrivateFromFilePEM
**/
func (r *RSAHelper) PrivateFromBytePEM(privateKeyPEM []byte) error {
	block, _ := pem.Decode(privateKeyPEM)

	if block == nil {
		r.PriKey = nil
		return errors.New("invalid PEM Block(private key)")
	}

	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)

	if err != nil {
		r.PriKey = nil
		return err
	}

	r.PriKey = privateKey
	return nil
}

func (r *RSAHelper) PrivateFromStringPEM(privateKeyPEM string) error {
	return r.PrivateFromBytePEM([]byte(privateKeyPEM))
}

func (r *RSAHelper) PrivateFromFilePEM(filename string) error {
	fileBuf, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	err = r.PrivateFromBytePEM(fileBuf)
	return err
}

/**
*	PublicToBytePEM
* 	PublicToStringPEM
**/
func (r *RSAHelper) PublicToBytePEM() ([]byte, error) {
	if r.PubKey == nil {
		return nil, errors.New("public rsa key is nil")
	}
	publicKeyBytes, err := x509.MarshalPKIXPublicKey(r.PubKey)
	if err != nil {
		return nil, err
	}
	publicKeyPEM := pem.EncodeToMemory(
		&pem.Block{
			Type:  "RSA PUBLIC KEY",
			Bytes: publicKeyBytes,
		},
	)
	return publicKeyPEM, nil
}

func (r *RSAHelper) PublicToStringPEM() (string, error) {
	buf, err := r.PublicToBytePEM()
	if err != nil {
		return "", err
	}
	return string(buf), nil
}

/**
*	PublicFromBytePEM
**/
func (r *RSAHelper) PublicFromBytePEM(publicKeyPEM []byte) error {
	block, _ := pem.Decode(publicKeyPEM)
	if block == nil {
		r.PubKey = nil
		return errors.New("invalid PEM Block (public key)")
	}

	publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		r.PubKey = nil
		return err
	}

	switch pub := publicKey.(type) {
	case *rsa.PublicKey:
		r.PubKey = pub
		return nil
	default:
		return errors.New("invalid key type")
	}
}

func (r *RSAHelper) PublicFromStringPEM(publicKeyPEM []byte) error {
	return r.PublicFromBytePEM([]byte(publicKeyPEM))
}

func (r *RSAHelper) PublicFromFilePEM(filename string) error {
	fileBuf, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	err = r.PublicFromBytePEM(fileBuf)
	return err
}

/**
*	EncryptByte
**/
func (r *RSAHelper) EncryptByte(src []byte) ([]byte, error) {

	if r.PubKey == nil {
		return nil, errors.New("public key is nil")
	}

	ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, r.PubKey, src)

	if err != nil {
		return nil, err
	}
	return ciphertext, nil
}

func (r *RSAHelper) EncryptString(src string) (string, error) {
	enc, err := r.EncryptByte([]byte(src))
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(enc), nil
}

/**
* DecryptByte
**/
func (r *RSAHelper) DecryptByte(src []byte) ([]byte, error) {

	if r.PriKey == nil {
		return nil, errors.New("private key is nil")
	}

	plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, r.PriKey, src)

	if err != nil {
		return nil, err
	}
	return plaintext, nil
}

func (r *RSAHelper) DecryptString(src string) (string, error) {
	bsrc, err := hex.DecodeString(src)
	if err != nil {
		return "", err
	}
	dec, err := r.DecryptByte(bsrc)

	if err != nil {
		return "", err
	}

	return string(dec), nil
}

RSAHelper 라는 구조체와 그 구조체에 딸린 메소드들입니다. 

Go
package main

import "fmt"

func main() {
	rh := RSAHelper{}
	rh.GenerateKey(1024)

	priv_pem, _ := rh.PrivateToStringPEM()
	pub_pem, _ := rh.PublicToStringPEM()

	fmt.Println(priv_pem)
	fmt.Println(pub_pem)

	s := "PIROGOM|hahahaha|panichwanja|20220303"

	fmt.Println("plain", s)

	enc, _ := rh.EncryptString(s)

	fmt.Println(enc)

	dec, _ := rh.DecryptString(enc)

	fmt.Println(dec)
}

이런식으로 쓰시면 되겠습니다. 예제에선 오류체크를 전혀 안하고 있는데 .. 별로 좋은 습관은 아닌거 아시죠? ㅋㅋ 뭔가 잘 안될떈 _ 로 무시중인 error 리턴 값들을 확인 바랍니다.

GenerateKey 함수의 인자는 몇비트로 키를 만들꺼냐의 문제인데 값이 크면 클수록 키 생성과 암호화, 복호화에 시간이 많이 소요됩니다. 

나머지는 그리 어려운 구조의 코드는 아니니까 ;; 

필요하면 가따 쓰소서~ 

%d