Compare commits

..

14 Commits

Author SHA1 Message Date
e77a67a3de feat: load env from file 2023-03-09 13:29:05 +01:00
d815b12719 feat: update README 2023-03-08 12:17:48 +01:00
7660cc57fd feat: writer test full coverage 2023-03-05 15:00:53 +01:00
25526ac993 feat: cmd add some tests 2023-03-05 15:00:37 +01:00
0bf26d2019 feat: fix force error after delete directory 2023-03-05 00:10:46 +01:00
b049c6a779 fix lint 2023-03-05 00:07:48 +01:00
0bf7b63286 feat: test coverage more than 85% 2023-03-05 00:05:00 +01:00
89136cae59 feat: test coverage more than 80% 2023-03-05 00:04:41 +01:00
72ffe8456d feat: update README 2023-03-03 22:46:10 +01:00
8b1d063ffe feat: add license 2023-03-03 22:45:40 +01:00
bb9df2fe8d feat: add more tests to increase coverage 2023-03-03 22:44:57 +01:00
ef2112534c feat: add tests 2023-03-03 22:31:10 +01:00
8d9c1542dd feat: update gitignore 2023-03-03 22:30:56 +01:00
99c2ebe055 fix README 2023-02-20 21:37:22 +01:00
15 changed files with 611 additions and 192 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
.env
viper.default.yaml
.vscode
certs
coverage

8
LICENSE Normal file
View 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

View File

@@ -1,6 +1,13 @@
COVERAGE_DIR=coverage
lint:
golangci-lint run ./...
goreportcard:
goreportcard-cli -v
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

View File

@@ -8,7 +8,7 @@ I had some trouble during TLS communication between both of my gRPC server and c
## 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.
- [ ] Complete tests
- ~~[x] Complete tests~~
## Configuration
If you are on `dev` environment, like I've been doing, you must create `.env` file similar as `.env.example` in this repo:
@@ -31,7 +31,7 @@ ca:
ext_key_usage:
- 1
- 2
duration: 518400 #1 year
duration: "8760h0m0s" #1 year
client:
serial_number: 12151232 # serial number
subject:
@@ -51,7 +51,7 @@ client:
ext_key_usage:
- 1
- 2
duration: 518400
duration: "8760h0m0s"
```
## Execution
Then you can just run
@@ -59,21 +59,37 @@ Then you can just run
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
```bash
make goreportcard
```
output:
```bash
goreportcard-cli -v
Grade ........... A+ 94.1%
Files .................. 9
Issues ................. 1
➜ go-cert-gen git:(main) goreportcard-cli -v
Grade .......... A+ 100.0%
Files ................. 12
Issues ................. 0
gofmt ............... 100%
go_vet .............. 100%
gocyclo ............. 100%
ineffassign ......... 100%
license ............... 0%
license ............. 100%
misspell ............ 100%
```

View File

@@ -27,9 +27,9 @@ var envConfig struct {
var writer pkgio.WriterIface
func intEnvConfig(isProd bool) {
if !isProd {
err := godotenv.Load(util.RootDir() + "/.env")
func intEnvConfig(envFilePath string) {
if envFilePath != "" {
err := godotenv.Load(envFilePath)
if err != nil {
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)
}
exportPem("root-ca.pem", rootCA.PEM())
exportPem("root-key.pem", rootCA.Key())
outputPath, err := exportPem("root-ca.pem", rootCA.PEM())
if err != nil {
log.Fatalf("exportPem: %s\n", err)
}
log.Printf("file created successfully: %s\n", outputPath)
exportPem("client-cert.pem", clientCert.PEM())
exportPem("client-key.pem", clientCert.Key())
outputPath, err = exportPem("root-key.pem", rootCA.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 {
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 {
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsage(v))
}
return extKeyUsage
}
func exportPem(filename string, data []byte) {
func exportPem(filename string, data []byte) (string, error) {
outputPath, err := writer.WriteFile(filename, data)
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() {
intEnvConfig(false)
envFile := ""
if os.Getenv("ENV") != "prod" {
envFile = "./.env"
}
intEnvConfig(envFile)
cobra.OnInitialize(initConfig)
}

View File

@@ -1,7 +1,62 @@
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) {
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
View File

@@ -7,15 +7,18 @@ require (
github.com/kelseyhightower/envconfig v1.4.0
github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // 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/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect

View 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
}

View 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)
}

View File

@@ -8,15 +8,104 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"log"
"time"
"gitlab.com/urkob/go-cert-gen/pkg/ca"
"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.
func newPrivateKey() (*ecdsa.PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@@ -45,155 +134,16 @@ func encodePrivateKey(priv *ecdsa.PrivateKey) ([]byte, error) {
out := &bytes.Buffer{}
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, fmt.Errorf("marshal: %s", err)
return nil, fmt.Errorf("x509.MarshalPKCS8PrivateKey: %s", err)
}
err = pem.Encode(out, &pem.Block{
Type: "PRIVATE KEY",
Type: PRIVATE_KEY,
Bytes: privBytes,
})
return out.Bytes(), err
}
// Create a self-signed certificate.
func newRootCA(config *ca.CaConfig) ([]byte, []byte, error) {
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
if err != nil {
return nil, err
}
return out.Bytes(), nil
}

View 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)
}

View File

@@ -2,6 +2,7 @@ package io
import (
"fmt"
"log"
"os"
"gitlab.com/urkob/go-cert-gen/pkg/io"
@@ -11,22 +12,33 @@ type writer struct {
dirPath string
}
// WriteFile writes file into `w writer` directory.
// Returns outputPath and error
func (w writer) WriteFile(filename string, data []byte) (string, error) {
if filename == "" {
return "", fmt.Errorf("filename cannot be empty")
}
if w.dirPath == "" {
return "", fmt.Errorf("export directory cannot be empty")
}
if err := os.MkdirAll(w.dirPath, 0o755); err != nil {
return "", err
}
log.Println("to write file")
outputPath := w.dirPath + "/" + filename
if err := os.WriteFile(outputPath, data, 0o600); err != nil {
return "", err
}
log.Println("file written")
return outputPath, nil
}
func NewWriter(dirPath string) io.WriterIface {
return newWriter(dirPath)
}
func newWriter(dirPath string) *writer {
return &writer{
dirPath: dirPath,
}

View 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)
}

View File

@@ -29,26 +29,10 @@ type Subject struct {
PostalCode string
}
/*
var (
subjectKeyId = []byte{1, 2, 3, 4, 6}
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
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,
}
}
*/

View File

@@ -1,5 +1,7 @@
package io
type WriterIface interface {
// WriteFile writes file into `w writer` directory.
// Returns outputPath and error
WriteFile(filename string, data []byte) (string, error)
}