-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
38ce199
commit 0a3e607
Showing
11 changed files
with
373 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,8 @@ | ||
dist | ||
serveit | ||
serveit | ||
*.key | ||
*.crt | ||
*.csr | ||
*.srl | ||
*.pem | ||
*.ext |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package security | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
) | ||
|
||
// CertFilename is the application certificate filename. | ||
var CertFilename = "serveit.crt" | ||
|
||
// CSRFilename is the application certificate signing request filename. | ||
var CSRFilename = "serveit.csr" | ||
|
||
// ExtFilename is the application certificate extensions filename. | ||
var ExtFilename = "serveit.ext" | ||
|
||
// EnsureCert creates an application X.509 certificate if it doesn't already | ||
// exist. | ||
func EnsureCert() error { | ||
_, err := os.Stat(CertFilename) | ||
if os.IsNotExist(err) { | ||
if err := createCSR(); err != nil { | ||
return err | ||
} | ||
if err = createExtFile(); err != nil { | ||
return err | ||
} | ||
return CreateCert() | ||
} | ||
return err | ||
} | ||
|
||
// CreateCert creates an application X.509 certificate. | ||
func CreateCert() error { | ||
cmd := exec.Command( | ||
"openssl", "x509", | ||
"-req", | ||
"-in", CSRFilename, | ||
"-CA", RootCACertFilename, | ||
"-CAkey", RootCAKeyFilename, | ||
"-passin", "pass:serveit", | ||
"-CAcreateserial", | ||
"-out", CertFilename, | ||
"-days", "3650", | ||
"-sha256", | ||
"-extfile", ExtFilename, | ||
) | ||
_, err := cmd.CombinedOutput() | ||
return err | ||
} | ||
|
||
// createCSR creates a certificate signing request. | ||
func createCSR() error { | ||
cmd := exec.Command( | ||
"openssl", "req", | ||
"-new", | ||
"-key", KeyFilename, | ||
"-subj", "/C=CA/ST=Ontario/L=Ottawa/O=samherrmann/CN=serveit", | ||
"-out", CSRFilename, | ||
) | ||
_, err := cmd.CombinedOutput() | ||
return err | ||
} | ||
|
||
// createExtFile creates a certificate extensions file. | ||
func createExtFile() error { | ||
content := []byte( | ||
"authorityKeyIdentifier=keyid,issuer\n" + | ||
"basicConstraints=CA:FALSE\n" + | ||
"keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment\n" + | ||
"subjectAltName = @alt_names\n" + | ||
"[alt_names]\n" + | ||
"DNS.1 = localhost\n", | ||
) | ||
return ioutil.WriteFile(ExtFilename, content, 0644) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package security | ||
|
||
import ( | ||
"os" | ||
"os/exec" | ||
) | ||
|
||
// KeyFilename is the application RSA key filename. | ||
var KeyFilename = "serveit.key" | ||
|
||
// EnsureKey creates an RSA key if it doesn't already exist. | ||
func EnsureKey() error { | ||
_, err := os.Stat(KeyFilename) | ||
if os.IsNotExist(err) { | ||
return CreateKey() | ||
} | ||
return err | ||
} | ||
|
||
// CreateKey creates an RSA key. | ||
func CreateKey() error { | ||
cmd := exec.Command( | ||
"openssl", "genrsa", | ||
"-out", KeyFilename, | ||
"2048", | ||
) | ||
_, err := cmd.CombinedOutput() | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package security | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// EnsureKeyPairs creates an RSA key and a X.509 certificate for both the | ||
// certificate authority (CA) and the application if they don't already exist. | ||
func EnsureKeyPairs() error { | ||
err := EnsureRootCAKey() | ||
if err != nil { | ||
return fmt.Errorf("Error creating %v: %w", RootCAKeyFilename, err) | ||
} | ||
err = EnsureRootCACert() | ||
if err != nil { | ||
return fmt.Errorf("Error creating %v: %w", RootCACertFilename, err) | ||
} | ||
err = EnsureKey() | ||
if err != nil { | ||
return fmt.Errorf("Error creating %v: %w", KeyFilename, err) | ||
} | ||
err = EnsureCert() | ||
if err != nil { | ||
return fmt.Errorf("Error creating %v: %w", CertFilename, err) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package security_test | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/samherrmann/serveit/security" | ||
) | ||
|
||
func TestEnsureKeyPairs(t *testing.T) { | ||
// Start with a clean slate. | ||
if err := removeAllFiles(); err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if err := security.EnsureKeyPairs(); err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if err := verifyKey(security.RootCAKeyFilename); err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if err := verifyCert(security.RootCACertFilename); err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if err := verifyKey(security.KeyFilename); err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if err := verifyCert(security.CertFilename); err != nil { | ||
t.Error(err) | ||
} | ||
|
||
// Clean up. | ||
if err := removeAllFiles(); err != nil { | ||
t.Error(err) | ||
} | ||
} | ||
|
||
func verifyKey(filename string) error { | ||
return verifyFileContent( | ||
filename, | ||
"-----BEGIN RSA PRIVATE KEY-----", | ||
"-----END RSA PRIVATE KEY-----", | ||
) | ||
} | ||
|
||
func verifyCert(filename string) error { | ||
return verifyFileContent( | ||
filename, | ||
"-----BEGIN CERTIFICATE-----", | ||
"-----END CERTIFICATE-----", | ||
) | ||
} | ||
|
||
func verifyFileContent(filename string, prefix string, suffix string) error { | ||
// Read content from file. | ||
content, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
return fmt.Errorf("Error reading %v: %v", filename, err) | ||
} | ||
|
||
// Verify file content. | ||
contentStr := string(content) | ||
if !strings.HasPrefix(contentStr, prefix) && | ||
!strings.HasSuffix(contentStr, suffix) { | ||
return fmt.Errorf("%v does not have expected content, got %v", filename, contentStr) | ||
} | ||
return nil | ||
} | ||
|
||
func removeAllFiles() error { | ||
files := []string{ | ||
security.RootCAKeyFilename, | ||
security.RootCACertFilename, | ||
security.RootCACertSerialFilename, | ||
security.KeyFilename, | ||
security.CSRFilename, | ||
security.ExtFilename, | ||
security.CertFilename, | ||
} | ||
|
||
for _, file := range files { | ||
if err := os.Remove(file); err != nil && !errors.Is(err, os.ErrNotExist) { | ||
return fmt.Errorf("Error removing %v: %w", file, err) | ||
} | ||
} | ||
return nil | ||
} |
Oops, something went wrong.