본문 바로가기

Language/Go

[GO] https 제공을 위한 인증된 SSL과 서버 개인 키 생성하기

SSL(Secure Socket Layer) - 데이터 암호와 인증에 대한 프로토콜, 주로 클라이언트 통신 간에 사용

 

- SSL/TLS 인증은 데이터를 제공할 때 암호화하거나 인증을 한다.

- SSL 인증 시 데이터는 X.509 포맷을 따르며 이때 공개 키는 서버에 저장된다.

- SSL인증은 인증 기관(CA)에 의해 서명된 것을 사용.

- 클라이언트가 서버에 요청할 때, 서버는 인증서를 반환해 준다.

 

https 제공을 위한 code

package main

import (
	"net/http"
)

func main() {
	server := http.Server {
    	Addr: "127.0.0.1:8080",
        Handler: nil,
    }
    server.ListenAndServeTLS("cert.pem", "key.pem")
}

http 전송을 할 때에는 .ListnAndServer() method를 사용하지만 https 전송을 위해서는 SSL 인증서인 cert.pem 파일과 서버를 위한 개인 키인 key.pem 파일이 필요하다.

 

 

개인키 생성하기

-  crypto/x509 라이브러리를 이용해서 인증서를 생성

- 인증서 생성을 위해 개인 키가 요청되면 개인 키를 이용해 인증서를 만들고 파일로 저장

 

이 때 인증서의 시리얼번호가 필요한데, 이 값은 실제로는 유일한 번호로 인증기관 CA에 의해 발행되는 것

현재는 테스트이기 때문에 랜덤한 매우 큰 정수를 생성하고, subject를 설정해준다.

 

max := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, _ := rand.Int(rand.Reader, max)

subject := pkix.Name{
  Organization:       [] string {"test Organization"},
  OrganizationalUnit: [] string {"test"},
  CommonName:         "Go Web Programming",
}

 

인증서의 구조

template := x509.Certificate{
  SerialNumber:serialNumber,
  Subject:subject,
  NotBefore:time.Now(),
  NotAfter:time.Now().Add(365 * 24 * time.Hour),
  KeyUsage:x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  ExtKeyUsage:[] x509.ExtKeyUsage {x509.ExtKeyUsageServerAuth},
  IPAddresses:[] net.IP {net.ParseIP("127.0.0.1")},
}

발급 유효 기간을 현재부터 1년뒤로 설정해준다. (NotBefore, NotAfter)

KeyUsage, ExtKeyUsage 필드는 X.509 인증서를 가리키며 서버 인증을 위해 사용 된다.

또한 로컬에서만 동작하도록 127.0.0.1에서만 동작하는 인증서를 설정

 

구조를 만들었으면, crypto/rsa 라이브러리를 이용하여 RSA 개인 키를 생성

pk, _ := rsa.GenerateKey(rand.Reader, 2048)

 

encoding/pem 라이브러리를 이용하여 인증서를 cert.pem 파일로 인코딩

derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &pk.PublicKey, pk)
certOut, _ := os.Create("cert.pem")
pem.Encode(certOut, &pem.Block{Type:"CERTIFICATE", Bytes:derBytes})
certOut.Close()

 

 

key.pem 파일을 만들고, 우리가 생성했던 키를 저장한다.

keyOut, _ := os.Create("key.pem")
pem.Encode(keyOut, &pem.Block{Type:"RSA PRIVATE KEY", Bytes:x509.MarshalPKCS1PrivateKey(pk)})
keyOut.Close()

 

전체 코드를 실행하면 

다음과 같이 cert.pem, key.pem 파일이 생성된 것을 확인할 수 있다.

 

key 생성 전체 코드

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"os"
	"time"
)

func main() {
	max := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, _ := rand.Int(rand.Reader, max)

	subject := pkix.Name{
		Organization:       [] string {"test Organization"},
		OrganizationalUnit: [] string {"test"},
		CommonName:         "Go Web Programming",
	}

	template := x509.Certificate{
		SerialNumber:serialNumber,
		Subject:subject,
		NotBefore:time.Now(),
		NotAfter:time.Now().Add(365 * 24 * time.Hour),
		KeyUsage:x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:[] x509.ExtKeyUsage {x509.ExtKeyUsageServerAuth},
		IPAddresses:[] net.IP {net.ParseIP("127.0.0.1")},
	}

	pk, _ := rsa.GenerateKey(rand.Reader, 2048)

	derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &pk.PublicKey, pk)
	certOut, _ := os.Create("cert.pem")
	pem.Encode(certOut, &pem.Block{Type:"CERTIFICATE", Bytes:derBytes})
	certOut.Close()

	keyOut, _ := os.Create("key.pem")
	pem.Encode(keyOut, &pem.Block{Type:"RSA PRIVATE KEY", Bytes:x509.MarshalPKCS1PrivateKey(pk)})
	keyOut.Close()
}

 

 

이제 https 전송을 해보자

package main

import (
	"fmt"
	"net/http"
)

type handler struct {}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello world!")
}

func main() {
	handler := handler{}
	server := http.Server {
		Addr: "127.0.0.1:8080",
		Handler: &handler,
	}
	server.ListenAndServeTLS("cert.pem", "key.pem")
}

위 코드를 실행해보면,

 

인증 받지 않을 key이기 때문에 위험표시가 나오고, [고급] - [연결]을 해주면 접속할 수 있다.

 

 

테스트용 https 전송이 된것을 확인할 수 있다.