Compare commits
13 Commits
99c2ebe055
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e77a67a3de | |||
| d815b12719 | |||
| 7660cc57fd | |||
| 25526ac993 | |||
| 0bf26d2019 | |||
| b049c6a779 | |||
| 0bf7b63286 | |||
| 89136cae59 | |||
| 72ffe8456d | |||
| 8b1d063ffe | |||
| bb9df2fe8d | |||
| ef2112534c | |||
| 8d9c1542dd |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
.env
|
.env
|
||||||
viper.default.yaml
|
viper.default.yaml
|
||||||
.vscode
|
.vscode
|
||||||
|
certs
|
||||||
|
coverage
|
||||||
8
LICENSE
Normal file
8
LICENSE
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---- Definitions ----
|
||||||
|
license means right to use
|
||||||
|
|
||||||
|
|
||||||
|
Everybody is invited to contribute to improve this project and the main idea.
|
||||||
|
This idea which is to help the community to develop more secure code.
|
||||||
|
|
||||||
|
By the grace of YAHWEH
|
||||||
7
Makefile
7
Makefile
@@ -1,6 +1,13 @@
|
|||||||
|
COVERAGE_DIR=coverage
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
golangci-lint run ./...
|
golangci-lint run ./...
|
||||||
goreportcard:
|
goreportcard:
|
||||||
goreportcard-cli -v
|
goreportcard-cli -v
|
||||||
test:
|
test:
|
||||||
go test ./...
|
go test ./...
|
||||||
|
test-coverage:
|
||||||
|
rm -rf ${COVERAGE_DIR}
|
||||||
|
mkdir ${COVERAGE_DIR}
|
||||||
|
go test -v -coverprofile ${COVERAGE_DIR}/cover.out ./...
|
||||||
|
go tool cover -html ${COVERAGE_DIR}/cover.out -o ${COVERAGE_DIR}/cover.html
|
||||||
30
README.md
30
README.md
@@ -8,7 +8,7 @@ I had some trouble during TLS communication between both of my gRPC server and c
|
|||||||
## TODO:
|
## TODO:
|
||||||
- [ ] Create intermediate authority to sign certificates on behalf CA to add more security. If intermediate is hacked then you can revoke from CA and generate new intermediates keeping CA isolated from beeing hacked.
|
- [ ] Create intermediate authority to sign certificates on behalf CA to add more security. If intermediate is hacked then you can revoke from CA and generate new intermediates keeping CA isolated from beeing hacked.
|
||||||
|
|
||||||
- [ ] Complete tests
|
- ~~[x] Complete tests~~
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
If you are on `dev` environment, like I've been doing, you must create `.env` file similar as `.env.example` in this repo:
|
If you are on `dev` environment, like I've been doing, you must create `.env` file similar as `.env.example` in this repo:
|
||||||
@@ -59,21 +59,37 @@ Then you can just run
|
|||||||
go run main.go
|
go run main.go
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## tests
|
||||||
|
Just simply run make command and watch coverage results on `cover.html` within `coverage`
|
||||||
|
```shell
|
||||||
|
make test-coverage
|
||||||
|
rm -rf coverage
|
||||||
|
mkdir coverage
|
||||||
|
go test -v -coverprofile coverage/cover.out ./...
|
||||||
|
=== RUN TestCredentialsFromKeyWithPasswd
|
||||||
|
--- PASS: TestCredentialsFromKeyWithPasswd (0.37s)
|
||||||
|
=== RUN TestCredentialsFromKeyWithPasswdError
|
||||||
|
--- PASS: TestCredentialsFromKeyWithPasswdError (0.46s)
|
||||||
|
PASS
|
||||||
|
coverage: 90.9% of statements
|
||||||
|
ok gitea.urkob.com/urko/go-grpc-certificate/pkg/credentials 0.839s coverage: 90.9% of statements
|
||||||
|
go tool cover -html coverage/cover.out -o coverage/cover.html
|
||||||
|
```
|
||||||
|
|
||||||
## goreportcard
|
## goreportcard
|
||||||
```bash
|
```bash
|
||||||
make goreportcard
|
make goreportcard
|
||||||
```
|
```
|
||||||
output:
|
output:
|
||||||
```bash
|
```bash
|
||||||
goreportcard-cli -v
|
➜ go-cert-gen git:(main) goreportcard-cli -v
|
||||||
Grade ........... A+ 94.1%
|
Grade .......... A+ 100.0%
|
||||||
Files .................. 9
|
Files ................. 12
|
||||||
Issues ................. 1
|
Issues ................. 0
|
||||||
gofmt ............... 100%
|
gofmt ............... 100%
|
||||||
go_vet .............. 100%
|
go_vet .............. 100%
|
||||||
gocyclo ............. 100%
|
gocyclo ............. 100%
|
||||||
ineffassign ......... 100%
|
ineffassign ......... 100%
|
||||||
license ............... 0%
|
license ............. 100%
|
||||||
|
|
||||||
misspell ............ 100%
|
misspell ............ 100%
|
||||||
```
|
```
|
||||||
51
cmd/main.go
51
cmd/main.go
@@ -27,9 +27,9 @@ var envConfig struct {
|
|||||||
|
|
||||||
var writer pkgio.WriterIface
|
var writer pkgio.WriterIface
|
||||||
|
|
||||||
func intEnvConfig(isProd bool) {
|
func intEnvConfig(envFilePath string) {
|
||||||
if !isProd {
|
if envFilePath != "" {
|
||||||
err := godotenv.Load(util.RootDir() + "/.env")
|
err := godotenv.Load(envFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("environment variable ENV is empty and an error occurred while loading the .env file: %s\n", err)
|
log.Fatalf("environment variable ENV is empty and an error occurred while loading the .env file: %s\n", err)
|
||||||
}
|
}
|
||||||
@@ -87,32 +87,59 @@ var rootCmd = &cobra.Command{
|
|||||||
log.Fatalf("rootCA.WithClientCert: %s", err)
|
log.Fatalf("rootCA.WithClientCert: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
exportPem("root-ca.pem", rootCA.PEM())
|
outputPath, err := exportPem("root-ca.pem", rootCA.PEM())
|
||||||
exportPem("root-key.pem", rootCA.Key())
|
if err != nil {
|
||||||
|
log.Fatalf("exportPem: %s\n", err)
|
||||||
|
}
|
||||||
|
log.Printf("file created successfully: %s\n", outputPath)
|
||||||
|
|
||||||
exportPem("client-cert.pem", clientCert.PEM())
|
outputPath, err = exportPem("root-key.pem", rootCA.Key())
|
||||||
exportPem("client-key.pem", clientCert.Key())
|
if err != nil {
|
||||||
|
log.Fatalf("exportPem: %s\n", err)
|
||||||
|
}
|
||||||
|
log.Printf("file created successfully: %s\n", outputPath)
|
||||||
|
|
||||||
|
outputPath, err = exportPem("client-cert.pem", clientCert.PEM())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("exportPem: %s\n", err)
|
||||||
|
}
|
||||||
|
log.Printf("file created successfully: %s\n", outputPath)
|
||||||
|
|
||||||
|
outputPath, err = exportPem("client-key.pem", clientCert.Key())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("exportPem: %s\n", err)
|
||||||
|
}
|
||||||
|
log.Printf("file created successfully: %s\n", outputPath)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExtKeyUsage(intKeyUsageSlice []int) []x509.ExtKeyUsage {
|
func getExtKeyUsage(intKeyUsageSlice []int) []x509.ExtKeyUsage {
|
||||||
extKeyUsage := make([]x509.ExtKeyUsage, len(intKeyUsageSlice))
|
if intKeyUsageSlice == nil || len(intKeyUsageSlice) <= 0 {
|
||||||
|
return []x509.ExtKeyUsage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
extKeyUsage := make([]x509.ExtKeyUsage, 0, len(intKeyUsageSlice))
|
||||||
for _, v := range intKeyUsageSlice {
|
for _, v := range intKeyUsageSlice {
|
||||||
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsage(v))
|
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsage(v))
|
||||||
}
|
}
|
||||||
return extKeyUsage
|
return extKeyUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportPem(filename string, data []byte) {
|
func exportPem(filename string, data []byte) (string, error) {
|
||||||
outputPath, err := writer.WriteFile(filename, data)
|
outputPath, err := writer.WriteFile(filename, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("rootCA.WithClientCert: %s", err)
|
return "", fmt.Errorf("rootCA.WithClientCert: %s", err)
|
||||||
}
|
}
|
||||||
log.Printf("file created successfully: %s\n", outputPath)
|
return outputPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
intEnvConfig(false)
|
envFile := ""
|
||||||
|
if os.Getenv("ENV") != "prod" {
|
||||||
|
envFile = "./.env"
|
||||||
|
}
|
||||||
|
|
||||||
|
intEnvConfig(envFile)
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,62 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gitlab.com/urkob/go-cert-gen/internal/io"
|
||||||
|
)
|
||||||
|
|
||||||
func TestExecute(t *testing.T) {
|
func TestExecute(t *testing.T) {
|
||||||
Execute()
|
Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_getExtKeyUsage(t *testing.T) {
|
||||||
|
intKeyUsageSlice := make([]int, 0, 1)
|
||||||
|
intKeyUsageSlice = append(intKeyUsageSlice, int(x509.ExtKeyUsageClientAuth))
|
||||||
|
|
||||||
|
keyUsage := getExtKeyUsage(intKeyUsageSlice)
|
||||||
|
assert.Len(t, keyUsage, len(intKeyUsageSlice))
|
||||||
|
assert.Equal(t, keyUsage[0], x509.ExtKeyUsageClientAuth)
|
||||||
|
|
||||||
|
intKeyUsageSlice = make([]int, 0)
|
||||||
|
keyUsage = getExtKeyUsage(intKeyUsageSlice)
|
||||||
|
assert.Len(t, keyUsage, 0)
|
||||||
|
|
||||||
|
keyUsage = getExtKeyUsage(nil)
|
||||||
|
assert.Len(t, keyUsage, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var testFile = "test-file.txt"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("os.Getwd: %s\n", err)
|
||||||
|
}
|
||||||
|
writer = io.NewWriter(wd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_exportPem(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
os.Remove(testFile)
|
||||||
|
err := os.Remove(testFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
data := []byte("test data")
|
||||||
|
outputPath, err := exportPem(testFile, data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, outputPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_exportPemError(t *testing.T) {
|
||||||
|
data := []byte("test data")
|
||||||
|
outputPath, err := exportPem("", data)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Empty(t, outputPath)
|
||||||
|
}
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -7,15 +7,18 @@ require (
|
|||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/spf13/viper v1.15.0
|
github.com/spf13/viper v1.15.0
|
||||||
|
github.com/stretchr/testify v1.8.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/spf13/afero v1.9.3 // indirect
|
github.com/spf13/afero v1.9.3 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
|
|||||||
80
internal/cert/client_cert.go
Normal file
80
internal/cert/client_cert.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package cert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlab.com/urkob/go-cert-gen/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientCert struct {
|
||||||
|
certPEM []byte
|
||||||
|
keyPEM []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientCert) Key() []byte {
|
||||||
|
return c.keyPEM
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientCert) PEM() []byte {
|
||||||
|
return c.certPEM
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientCert(config *client.ClientCertConfig, rootCA *x509.Certificate, rootKeyPEM []byte) ([]byte, []byte, error) {
|
||||||
|
template := &x509.Certificate{
|
||||||
|
SerialNumber: config.Serial,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{config.Subject.Organization},
|
||||||
|
Country: []string{config.Subject.Country},
|
||||||
|
Province: []string{config.Subject.Province},
|
||||||
|
Locality: []string{config.Subject.Locality},
|
||||||
|
StreetAddress: []string{config.Subject.StreetAddress},
|
||||||
|
PostalCode: []string{config.Subject.PostalCode},
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(config.Duration),
|
||||||
|
SubjectKeyId: config.SubjectKeyId,
|
||||||
|
ExtKeyUsage: config.ExtKeyUsage,
|
||||||
|
KeyUsage: config.KeyUsage,
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(rootKeyPEM)
|
||||||
|
if block == nil {
|
||||||
|
return nil, nil, errors.New("pem.Decode")
|
||||||
|
}
|
||||||
|
|
||||||
|
caPrivKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("x509.ParsePKCS8PrivateKey: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := newPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("newPrivateKey: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
der, err := x509.CreateCertificate(rand.Reader, template, rootCA, &priv.PublicKey, caPrivKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("x509.CreateCertificate: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
err = pem.Encode(out, &pem.Block{Type: CERTIFICATE, Bytes: der})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("pem.Encode: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certPEM := out.Bytes()
|
||||||
|
keyPEM, err := encodePrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("encodePrivateKey: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return certPEM, keyPEM, nil
|
||||||
|
}
|
||||||
36
internal/cert/client_cert_test.go
Normal file
36
internal/cert/client_cert_test.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package cert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_newClientCert(t *testing.T) {
|
||||||
|
ca, err := NewRootCA(&rootTestConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, ca)
|
||||||
|
|
||||||
|
require.NotNil(t, ca.Key())
|
||||||
|
require.Greater(t, len(ca.Key()), 0)
|
||||||
|
|
||||||
|
require.NotNil(t, ca.PEM())
|
||||||
|
require.Greater(t, len(ca.PEM()), 0)
|
||||||
|
|
||||||
|
x509RootCA, err := parseCertificate(ca.PEM())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pem, key, err := newClientCert(&clientTestConfig, x509RootCA, ca.Key())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NotNil(t, pem)
|
||||||
|
require.Greater(t, len(pem), 0)
|
||||||
|
|
||||||
|
require.NotNil(t, key)
|
||||||
|
require.Greater(t, len(key), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_newClientCertErrr(t *testing.T) {
|
||||||
|
_, _, err := newClientCert(&clientTestConfig, nil, []byte{})
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
@@ -8,15 +8,104 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitlab.com/urkob/go-cert-gen/pkg/ca"
|
"gitlab.com/urkob/go-cert-gen/pkg/ca"
|
||||||
|
|
||||||
"gitlab.com/urkob/go-cert-gen/pkg/client"
|
"gitlab.com/urkob/go-cert-gen/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CERTIFICATE = "CERTIFICATE"
|
||||||
|
PRIVATE_KEY = "PRIVATE KEY"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rootCA struct {
|
||||||
|
caPEM []byte
|
||||||
|
keyPEM []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rootCA) Key() []byte {
|
||||||
|
return r.keyPEM
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rootCA) PEM() []byte {
|
||||||
|
return r.caPEM
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rootCA) WithClientCert(config *client.ClientCertConfig) (client.ClientCertIface, error) {
|
||||||
|
x509RootCA, err := parseCertificate(r.caPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parseCertificate: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCertPEM, clientKeyPEM, err := newClientCert(config, x509RootCA, r.keyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("newClientCert: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &clientCert{
|
||||||
|
certPEM: clientCertPEM,
|
||||||
|
keyPEM: clientKeyPEM,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a self-signed certificate.
|
||||||
|
func newRootCA(config *ca.CaConfig) ([]byte, []byte, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, nil, errors.New("ca.CaConfig config is nil")
|
||||||
|
}
|
||||||
|
priv, err := newPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("newPrivateKey: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: config.SerialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{config.Subject.Organization},
|
||||||
|
CommonName: config.Subject.CommonName,
|
||||||
|
},
|
||||||
|
NotBefore: time.Now().Add(-time.Minute),
|
||||||
|
NotAfter: time.Now().Add(config.Duration),
|
||||||
|
IsCA: true,
|
||||||
|
KeyUsage: config.KeyUsage,
|
||||||
|
ExtKeyUsage: config.ExtKeyUsage,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("x509.CreateCertificate: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
err = pem.Encode(out, &pem.Block{Type: CERTIFICATE, Bytes: der})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("pem.Encode: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
caPEM := out.Bytes()
|
||||||
|
keyPEM, err := encodePrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("encodePrivateKey: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return caPEM, keyPEM, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRootCA(config *ca.CaConfig) (ca.RootCACertificateIface, error) {
|
||||||
|
caPEM, keyPEM, err := newRootCA(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("newRootCA: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rootCA{
|
||||||
|
caPEM: caPEM,
|
||||||
|
keyPEM: keyPEM,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a new 512bit private key.
|
// Creates a new 512bit private key.
|
||||||
func newPrivateKey() (*ecdsa.PrivateKey, error) {
|
func newPrivateKey() (*ecdsa.PrivateKey, error) {
|
||||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
@@ -45,155 +134,16 @@ func encodePrivateKey(priv *ecdsa.PrivateKey) ([]byte, error) {
|
|||||||
out := &bytes.Buffer{}
|
out := &bytes.Buffer{}
|
||||||
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("marshal: %s", err)
|
return nil, fmt.Errorf("x509.MarshalPKCS8PrivateKey: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pem.Encode(out, &pem.Block{
|
err = pem.Encode(out, &pem.Block{
|
||||||
Type: "PRIVATE KEY",
|
Type: PRIVATE_KEY,
|
||||||
Bytes: privBytes,
|
Bytes: privBytes,
|
||||||
})
|
})
|
||||||
return out.Bytes(), err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
// Create a self-signed certificate.
|
|
||||||
func newRootCA(config *ca.CaConfig) ([]byte, []byte, error) {
|
return out.Bytes(), nil
|
||||||
priv, err := newPrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("newPrivateKey: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
template := x509.Certificate{
|
|
||||||
SerialNumber: config.SerialNumber,
|
|
||||||
Subject: pkix.Name{
|
|
||||||
Organization: []string{config.Subject.Organization},
|
|
||||||
CommonName: config.Subject.CommonName,
|
|
||||||
},
|
|
||||||
NotBefore: time.Now().Add(-time.Minute),
|
|
||||||
NotAfter: time.Now().Add(config.Duration),
|
|
||||||
IsCA: true,
|
|
||||||
KeyUsage: config.KeyUsage,
|
|
||||||
ExtKeyUsage: config.ExtKeyUsage,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
}
|
|
||||||
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("x509.CreateCertificate: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
err = pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("pem.Encode: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
caPEM := out.Bytes()
|
|
||||||
keyPEM, err := encodePrivateKey(priv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("encodePrivateKey: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return caPEM, keyPEM, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClientCert(config *client.ClientCertConfig, rootCA *x509.Certificate, rootKeyPEM []byte) ([]byte, []byte, error) {
|
|
||||||
template := &x509.Certificate{
|
|
||||||
SerialNumber: config.Serial,
|
|
||||||
Subject: pkix.Name{
|
|
||||||
Organization: []string{config.Subject.Organization},
|
|
||||||
Country: []string{config.Subject.Country},
|
|
||||||
Province: []string{config.Subject.Province},
|
|
||||||
Locality: []string{config.Subject.Locality},
|
|
||||||
StreetAddress: []string{config.Subject.StreetAddress},
|
|
||||||
PostalCode: []string{config.Subject.PostalCode},
|
|
||||||
},
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: time.Now().Add(config.Duration),
|
|
||||||
SubjectKeyId: config.SubjectKeyId,
|
|
||||||
ExtKeyUsage: config.ExtKeyUsage,
|
|
||||||
KeyUsage: config.KeyUsage,
|
|
||||||
}
|
|
||||||
|
|
||||||
block, _ := pem.Decode(rootKeyPEM)
|
|
||||||
caPrivKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("x509.ParsePKCS8PrivateKey: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, err := newPrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("newPrivateKey: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
der, err := x509.CreateCertificate(rand.Reader, template, rootCA, &priv.PublicKey, caPrivKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("x509.CreateCertificate: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
err = pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("pem.Encode: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
certPEM := out.Bytes()
|
|
||||||
keyPEM, err := encodePrivateKey(priv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("encodePrivateKey: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return certPEM, keyPEM, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type rootCA struct {
|
|
||||||
caPEM []byte
|
|
||||||
keyPEM []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type clientCert struct {
|
|
||||||
certPEM []byte
|
|
||||||
keyPEM []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientCert) Key() []byte {
|
|
||||||
return c.keyPEM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientCert) PEM() []byte {
|
|
||||||
return c.certPEM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rootCA) WithClientCert(config *client.ClientCertConfig) (client.ClientCertIface, error) {
|
|
||||||
x509RootCA, err := parseCertificate(r.caPEM)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parseCertificate: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
clientCertPEM, clientKeyPEM, err := newClientCert(config, x509RootCA, r.keyPEM)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("newClientCert: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &clientCert{
|
|
||||||
certPEM: clientCertPEM,
|
|
||||||
keyPEM: clientKeyPEM,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rootCA) Key() []byte {
|
|
||||||
return r.keyPEM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rootCA) PEM() []byte {
|
|
||||||
return r.caPEM
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRootCA(config *ca.CaConfig) (ca.RootCACertificateIface, error) {
|
|
||||||
caPEM, keyPEM, err := newRootCA(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("newRootCA: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &rootCA{
|
|
||||||
caPEM: caPEM,
|
|
||||||
keyPEM: keyPEM,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
143
internal/cert/root_ca_test.go
Normal file
143
internal/cert/root_ca_test.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
package cert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/x509"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gitlab.com/urkob/go-cert-gen/pkg/ca"
|
||||||
|
"gitlab.com/urkob/go-cert-gen/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
const year = time.Hour * 24 * 365
|
||||||
|
|
||||||
|
var rootTestConfig = ca.CaConfig{
|
||||||
|
SerialNumber: big.NewInt(12321),
|
||||||
|
Subject: ca.CaSubject{
|
||||||
|
Organization: "test-organization",
|
||||||
|
CommonName: "test-organization",
|
||||||
|
},
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
|
x509.ExtKeyUsageServerAuth,
|
||||||
|
x509.ExtKeyUsageClientAuth,
|
||||||
|
},
|
||||||
|
Duration: year,
|
||||||
|
}
|
||||||
|
|
||||||
|
var clientTestConfig = client.ClientCertConfig{
|
||||||
|
Serial: big.NewInt(12321),
|
||||||
|
Subject: client.Subject{
|
||||||
|
Organization: rootTestConfig.Subject.Organization,
|
||||||
|
Country: "REML",
|
||||||
|
Province: "REML",
|
||||||
|
Locality: "REML",
|
||||||
|
StreetAddress: "c/o Sovereign 7 rural free delivery",
|
||||||
|
PostalCode: "[Near 777]",
|
||||||
|
},
|
||||||
|
Duration: year,
|
||||||
|
SubjectKeyId: []byte{1, 2, 3, 4, 6},
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
|
x509.ExtKeyUsageServerAuth,
|
||||||
|
x509.ExtKeyUsageClientAuth,
|
||||||
|
},
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_newPrivateKey(t *testing.T) {
|
||||||
|
privKey, err := newPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NotEmpty(t, privKey.PublicKey.Params().Name)
|
||||||
|
require.Equal(t, elliptic.P256().Params().Name, privKey.PublicKey.Params().Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_encodePrivateKey(t *testing.T) {
|
||||||
|
privKey, err := newPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bytes, err := encodePrivateKey(privKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NotNil(t, bytes)
|
||||||
|
require.Greater(t, len(bytes), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_encodePrivateKeyError(t *testing.T) {
|
||||||
|
key := ecdsa.PrivateKey{}
|
||||||
|
_, err := encodePrivateKey(&key)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_newRootCA(t *testing.T) {
|
||||||
|
caPEM, keyPEM, err := newRootCA(&rootTestConfig)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, caPEM)
|
||||||
|
require.Greater(t, len(caPEM), 0)
|
||||||
|
require.NotNil(t, keyPEM)
|
||||||
|
require.Greater(t, len(keyPEM), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_newRootCAError(t *testing.T) {
|
||||||
|
_, _, err := newRootCA(&ca.CaConfig{})
|
||||||
|
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseCertificate(t *testing.T) {
|
||||||
|
caPEM, _, err := newRootCA(&rootTestConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rootCert, err := parseCertificate(caPEM)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, rootCert)
|
||||||
|
require.Equal(t, rootCert.SignatureAlgorithm, x509.ECDSAWithSHA256)
|
||||||
|
require.Equal(t, rootCert.Issuer.Organization, []string{rootTestConfig.Subject.Organization})
|
||||||
|
require.Equal(t, rootCert.Issuer.CommonName, rootTestConfig.Subject.CommonName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseCertificateError(t *testing.T) {
|
||||||
|
_, err := parseCertificate([]byte{})
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRootCA(t *testing.T) {
|
||||||
|
rootCert, err := NewRootCA(&rootTestConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, rootCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRootCAERror(t *testing.T) {
|
||||||
|
_, err := NewRootCA(nil)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_rootCA_WithClientCert(t *testing.T) {
|
||||||
|
rootCert, err := NewRootCA(&rootTestConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, rootCert)
|
||||||
|
|
||||||
|
clientSrv, err := rootCert.WithClientCert(&clientTestConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, clientSrv)
|
||||||
|
|
||||||
|
require.NotNil(t, clientSrv.Key())
|
||||||
|
require.Greater(t, len(clientSrv.Key()), 0)
|
||||||
|
|
||||||
|
require.NotNil(t, clientSrv.PEM())
|
||||||
|
require.Greater(t, len(clientSrv.PEM()), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_rootCA_WithClientCertEror(t *testing.T) {
|
||||||
|
rootCert := rootCA{
|
||||||
|
caPEM: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := rootCert.WithClientCert(&clientTestConfig)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package io
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gitlab.com/urkob/go-cert-gen/pkg/io"
|
"gitlab.com/urkob/go-cert-gen/pkg/io"
|
||||||
@@ -11,22 +12,33 @@ type writer struct {
|
|||||||
dirPath string
|
dirPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteFile writes file into `w writer` directory.
|
||||||
|
// Returns outputPath and error
|
||||||
func (w writer) WriteFile(filename string, data []byte) (string, error) {
|
func (w writer) WriteFile(filename string, data []byte) (string, error) {
|
||||||
|
if filename == "" {
|
||||||
|
return "", fmt.Errorf("filename cannot be empty")
|
||||||
|
}
|
||||||
if w.dirPath == "" {
|
if w.dirPath == "" {
|
||||||
return "", fmt.Errorf("export directory cannot be empty")
|
return "", fmt.Errorf("export directory cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(w.dirPath, 0o755); err != nil {
|
if err := os.MkdirAll(w.dirPath, 0o755); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("to write file")
|
||||||
outputPath := w.dirPath + "/" + filename
|
outputPath := w.dirPath + "/" + filename
|
||||||
if err := os.WriteFile(outputPath, data, 0o600); err != nil {
|
if err := os.WriteFile(outputPath, data, 0o600); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
log.Println("file written")
|
||||||
return outputPath, nil
|
return outputPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWriter(dirPath string) io.WriterIface {
|
func NewWriter(dirPath string) io.WriterIface {
|
||||||
|
return newWriter(dirPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWriter(dirPath string) *writer {
|
||||||
return &writer{
|
return &writer{
|
||||||
dirPath: dirPath,
|
dirPath: dirPath,
|
||||||
}
|
}
|
||||||
|
|||||||
94
internal/io/writer_test.go
Normal file
94
internal/io/writer_test.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package io
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testWriterPath = "testPath"
|
||||||
|
testWriterPathError = "test Path, @:ººººº\\/.Ç*⁺´+"
|
||||||
|
testFileContent = []byte("test data")
|
||||||
|
testFileName = "test-file.txt"
|
||||||
|
workingDir = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
func deleteAllDirs() error {
|
||||||
|
return os.RemoveAll(testWriterPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
err := deleteAllDirs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("deleteAllDirs: ", err)
|
||||||
|
}
|
||||||
|
workingDir, err = os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("os.Getwd: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_newWriter(t *testing.T) {
|
||||||
|
w := newWriter(testWriterPath)
|
||||||
|
require.NotNil(t, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewWriter(t *testing.T) {
|
||||||
|
w := NewWriter(testWriterPath)
|
||||||
|
require.NotNil(t, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_writer_WriteFile(t *testing.T) {
|
||||||
|
err := deleteAllDirs()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func(t *testing.T) {
|
||||||
|
err := deleteAllDirs()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}(t)
|
||||||
|
|
||||||
|
w := newWriter(testWriterPath)
|
||||||
|
wgot, err := w.WriteFile(testFileName, testFileContent)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
btsReaded, err := os.ReadFile(wgot)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, btsReaded)
|
||||||
|
require.Equal(t, string(btsReaded), string(testFileContent))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_writer_WriteFileError(t *testing.T) {
|
||||||
|
err := deleteAllDirs()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func(t *testing.T) {
|
||||||
|
err := deleteAllDirs()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}(t)
|
||||||
|
|
||||||
|
w := newWriter("")
|
||||||
|
_, err = w.WriteFile(testFileName, testFileContent)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
w = newWriter(testWriterPath)
|
||||||
|
_, err = w.WriteFile("", testFileContent)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
err = os.MkdirAll(testWriterPath, fs.ModeDir)
|
||||||
|
require.NoError(t, err, "mkdir should not throw error")
|
||||||
|
|
||||||
|
err = os.MkdirAll(testWriterPath+"/"+testFileName, fs.ModeDir)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = w.WriteFile(testFileName, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
w = newWriter(testWriterPathError)
|
||||||
|
_, err = w.WriteFile(testFileName, testFileContent)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
@@ -29,26 +29,10 @@ type Subject struct {
|
|||||||
PostalCode string
|
PostalCode string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
var (
|
var (
|
||||||
subjectKeyId = []byte{1, 2, 3, 4, 6}
|
subjectKeyId = []byte{1, 2, 3, 4, 6}
|
||||||
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
|
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
|
||||||
keyUsage = x509.KeyUsageDigitalSignature
|
keyUsage = x509.KeyUsageDigitalSignature
|
||||||
)
|
)
|
||||||
|
*/
|
||||||
func NewDefaultConfig() *ClientCertConfig {
|
|
||||||
return &ClientCertConfig{
|
|
||||||
Serial: big.NewInt(12321),
|
|
||||||
Subject: Subject{
|
|
||||||
Organization: "",
|
|
||||||
Country: "",
|
|
||||||
Province: "",
|
|
||||||
Locality: "",
|
|
||||||
StreetAddress: "",
|
|
||||||
PostalCode: "",
|
|
||||||
},
|
|
||||||
Duration: time.Duration(time.Hour * 24 * 365),
|
|
||||||
SubjectKeyId: subjectKeyId,
|
|
||||||
ExtKeyUsage: extKeyUsage,
|
|
||||||
KeyUsage: keyUsage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package io
|
package io
|
||||||
|
|
||||||
type WriterIface interface {
|
type WriterIface interface {
|
||||||
|
// WriteFile writes file into `w writer` directory.
|
||||||
|
// Returns outputPath and error
|
||||||
WriteFile(filename string, data []byte) (string, error)
|
WriteFile(filename string, data []byte) (string, error)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user