Compare commits

...

13 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
15 changed files with 609 additions and 190 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,5 @@
.env .env
viper.default.yaml viper.default.yaml
.vscode .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: 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

View File

@@ -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%
``` ```

View File

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

View File

@@ -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
View File

@@ -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

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

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 ( 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,
} }

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

View File

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