GolangでPKI入門 - 2

#1.この記事の対象の人
- Golangで、自己署名CA証明書を作りたい人
- Golangで、自己署名CAで署名した「サーバ証明書」を作りたい人
- Golangで、自己署名CAで署名した「クライアント証明書」（クライアント認証用）を作りたい人

※作成した証明書と鍵はテスト用途でお使いください。
※作成した秘密鍵は適切に管理してください。

#2.Golangで自己署名CA証明書作成
以下エラー処理は省略しますので悪しからず

##2048bitのCA用RSA秘密鍵、公開鍵を作成
```go
	privateCaKey, err := rsa.GenerateKey(rand.Reader, 2048)
	publicCaKey := privateCaKey.Public()
```

##自己署名CA証明書のサブジェクト作成
CN: ca01
OU: Example Org Unit
O: Example Org
C: JP
に設定

```go
	// https://tools.ietf.org/html/rfc5280#section-4.1.2.6
	subjectCa := pkix.Name{
		CommonName:         "ca01",
		OrganizationalUnit: []string{"Example Org Unit"},
		Organization:       []string{"Example Org"},
		Country:            []string{"JP"},
	}
```
##自己署名CA証明書用構造体の設定

- CA証明書なので *IsCA* と *BasicConstraintsValid* は *true* に設定

[RFC5280 - Basic Constraints](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9)

```go
	caTpl := &x509.Certificate{
		SerialNumber:          big.NewInt(1),
		Subject:               subjectCa,
		NotAfter:              time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
		NotBefore:             time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
		IsCA:                  true,
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
		BasicConstraintsValid: true,
	}
```

##自己署名CA証明書作成

```go
	caCertificate, err := x509.CreateCertificate(rand.Reader, caTpl, caTpl, publicCaKey, privateCaKey)
```

##自己署名CA証明書をPEM形式で保存

```go
	var f *os.File
	f, err = os.Create("ca01.crt")
	err = pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: caCertificate})
	err = f.Close()
```

##自己署名CAの秘密鍵をPEM形式で保存

```go
	derCaPrivateKey := x509.MarshalPKCS1PrivateKey(privateCaKey)
	err = pem.Encode(f, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: derCaPrivateKey})
	err = f.Close()
```

#3.Golangでサーバ証明書作成
自己署名CAの秘密鍵で署名した「サーバ証明書」を作ります

##2048bitのサーバ証明書用RSA秘密鍵、公開鍵を作成
```go
	privateSslKey, err := rsa.GenerateKey(rand.Reader, 2048)
	publicSslKey := privateSslKey.Public()
```

##サーバ証明書のサブジェクト作成
CN: svr01
OU: Example Org Unit
O: Example Org
C: JP
に設定

```go
	// https://tools.ietf.org/html/rfc5280#section-4.1.2.6
	subjectSsl := pkix.Name{
		CommonName:         "svr01",
		OrganizationalUnit: []string{"Example Org Unit"},
		Organization:       []string{"Example Org"},
		Country:            []string{"JP"},
	}
```

##サーバ証明書用構造体の設定

- ブラウザでエラーが表示されないように、subjectAlternativeNameを指定します。

SubjectAlternativeNameを *svr01.example.org* に指定

>DNSNames:              []string{"svr01.example.org"}



- サーバ証明書用途なので、拡張キー使用法をサーバ認証に指定します。

>ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}


```go
	sslTpl := &x509.Certificate{
		SerialNumber:          big.NewInt(123),
		Subject:               subjectSsl,
		NotAfter:              time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
		NotBefore:             time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
		KeyUsage:              x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		DNSNames:              []string{"svr01.example.org"},
	}
```

##サーバ証明書作成

- サーバ証明書に署名する秘密鍵は「自己署名CAの秘密鍵」を指定します。

```go
	derSslCertificate, err := x509.CreateCertificate(rand.Reader, sslTpl, caTpl, publicSslKey, privateCaKey)
```


##サーバ証明書をPEM形式で保存

```go
	f, err = os.Create("svr01.crt")
	err = pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: derSslCertificate})
	err = f.Close()
```

##サーバ証明書の秘密鍵をPEM形式で保存

```go
	f, err = os.Create("svr01.key")
	derPrivateSslKey := x509.MarshalPKCS1PrivateKey(privateSslKey)
	err = pem.Encode(f, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: derPrivateSslKey})
	err = f.Close()
```

#4.Golangでクライアント証明書作成

自己署名CAの秘密鍵で署名した「クライアント証明書」を作ります

##2048bitのクライアント証明書用RSA秘密鍵、公開鍵を作成
```go
	privateClientKey, err := rsa.GenerateKey(rand.Reader, 2048)
	publicClientKey := privateClientKey.Public()
```

##クライアント証明書のサブジェクト作成
CN: client01
OU: Example Org Unit
O: Example Org
C: JP
に設定

```go
	// https://tools.ietf.org/html/rfc5280#section-4.1.2.6
	subjectClient := pkix.Name{
		CommonName:         "client01",
		OrganizationalUnit: []string{"Example Org Unit"},
		Organization:       []string{"Example Org"},
		Country:            []string{"JP"},
	}
```

##クライアント証明書用構造体の設定

- クライアント証明書用途なので、拡張キー使用法をクライアント認証に指定します。

>ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}


```go
	cliTpl := &x509.Certificate{
		SerialNumber:          big.NewInt(456),
		Subject:               subjectClient,
		NotAfter:              time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
		NotBefore:             time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	}
```


##クライアント証明書作成

- クライアント証明書に署名する秘密鍵は「自己署名CAの秘密鍵」を指定します。

```go
		derClientCertificate, err := x509.CreateCertificate(rand.Reader, cliTpl, caTpl, publicClientKey, privateCaKey)

```

##PKCS#12形式でクライアント証明書と秘密鍵を保存
クライアント認証用途なので、クライアント証明書とクライアント証明書の秘密鍵両方が必要です。ここでは、PKCS#12形式をつかって、クライアント証明書とクライアント証明書の秘密鍵をまとめて保存します。

- PKCS#12形式を作成するために　"software.sslmate.com/src/go-pkcs12"　packageをimport。

- PKCS#12形式のpasswordを *pincode* という文字列で指定



```go
	cert, err := x509.ParseCertificate(derClientCertificate)
	f, err = os.Create("client01.p12")
	//PKCS#12 [RFC7292] including client private keys, client certificates
	p12, err := pkcs12.Encode(rand.Reader, privateClientKey, cert, nil, "pincode")
	_, err = f.Write(p12)
	err = f.Close()
```

#5. コード

コードはこちら
https://github.com/tardevnull/gopkicookbook2