Commit c5015c9d authored by Caro Axel's avatar Caro Axel Committed by Loïck Bonniot

US-176: Implementation

parent 7f0be09a
Pipeline #151 passed with stage
......@@ -44,13 +44,13 @@ func PEMToCertificateRequest(data []byte) (*x509.CertificateRequest, error) {
// GetCertificate builds a certificate from a certificate request and an authoritative certificate (CA), as a PEM-encoded array of bytes.
// This function assumes that the identity of the signee is valid.
//
// The serial has to be unique.
// The serial has to be unique and positive.
//
// The generated certificate can safely be distributed to unknown actors.
func GetCertificate(days int, serial int64, req *x509.CertificateRequest, parent *x509.Certificate, key *rsa.PrivateKey) ([]byte, error) {
func GetCertificate(days int, serial uint64, req *x509.CertificateRequest, parent *x509.Certificate, key *rsa.PrivateKey) ([]byte, error) {
template := &x509.Certificate{
SerialNumber: big.NewInt(serial),
SerialNumber: new(big.Int).SetUint64(serial),
Subject: req.Subject,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, days),
......@@ -71,13 +71,13 @@ func GetCertificate(days int, serial int64, req *x509.CertificateRequest, parent
// GetSelfSignedCertificate builds a CA certificate from a private key, as a PEM-encoded array of bytes.
//
// The serial has to be unique.
// The serial has to be unique and positive.
//
// The generated certificate should be distributed to any other actor in the network under this CA.
func GetSelfSignedCertificate(days int, serial int64, country, organization, unit, cn string, key *rsa.PrivateKey) ([]byte, error) {
func GetSelfSignedCertificate(days int, serial uint64, country, organization, unit, cn string, key *rsa.PrivateKey) ([]byte, error) {
template := &x509.Certificate{
SerialNumber: big.NewInt(serial),
SerialNumber: new(big.Int).SetUint64(serial),
Subject: pkix.Name{
Country: []string{country},
Organization: []string{organization},
......
......@@ -147,7 +147,7 @@ func ExampleGetCertificate() {
signerKey, _ := PEMToPrivateKey([]byte(keyFixture))
// Generate the certificate for 365 days with a serial of 0x10 (16)
cert, err := GetCertificate(365, int64(0x10), certificateRequest, signerCertificate, signerKey)
cert, err := GetCertificate(365, uint64(0x10), certificateRequest, signerCertificate, signerKey)
if cert == nil || err != nil {
fmt.Println(err)
......
......@@ -5,3 +5,6 @@ go get gopkg.in/mgo.v2
# grpc
go get google.golang.org/grpc
# uuid
go get github.com/pborman/uuid
\ No newline at end of file
package autority
import (
"crypto/rsa"
"crypto/x509"
"dfss/auth"
"github.com/pborman/uuid"
"io/ioutil"
"math/big"
"os/user"
"path/filepath"
)
const (
PkeyFileName = "dffsp_pkey.pem"
RootCAFileName = "dffsp_rootCA.pem"
)
type PlatformID struct {
pkey *rsa.PrivateKey
rootCA *x509.Certificate
}
// GetHomeDir determines the home directory of the current user.
func GetHomeDir() string {
usr, err := user.Current()
if err != nil {
panic(err)
}
return usr.HomeDir
}
// GenerateRootCA constructs a self-signed certificate, using a unique serial number randomly generated (see UUID)
func GenerateRootCA(days int, country, organization, unit, cn string, key *rsa.PrivateKey) ([]byte, error) {
// Generating and converting the uuid to fit our needs: an 8 bytes integer.
uuid := uuid.NewRandom()
var slice []byte
slice = uuid[:8]
// TODO: improve this conversion method/need
serial := new(big.Int).SetBytes(slice).Uint64()
cert, err := auth.GetSelfSignedCertificate(days, serial, country, organization, unit, cn, key)
if err != nil {
return nil, err
}
return cert, nil
}
// Initialize creates and saves the platform's private key and root certificate to a PEM format.
//
// The files are saved at the specified path.
func Initialize(bits, days int, country, organization, unit, cn, path string) error {
// Generating the private key.
key, err := auth.GeneratePrivateKey(bits)
if err != nil {
return err
}
// Generating the root certificate, using the private key.
cert, err := GenerateRootCA(days, country, organization, unit, cn, key)
if err != nil {
return err
}
// Converting the private key to a PEM format, and saving it.
keyPem := auth.PrivateKeyToPEM(key)
keyPath := filepath.Join(path, PkeyFileName)
err = ioutil.WriteFile(keyPath, keyPem, 0600)
if err != nil {
return err
}
// Saving the root certificate.
certPath := filepath.Join(path, RootCAFileName)
err = ioutil.WriteFile(certPath, cert, 0600)
if err != nil {
return err
}
return nil
}
// Fetch the platform's private rsa key and root certificate, and create a PlatformID accordingly.
//
// The specified path should not end by a separator.
//
// The files are fetched using their default name.
func Start(path string) (*PlatformID, error) {
keyPath := filepath.Join(path, PkeyFileName)
certPath := filepath.Join(path, RootCAFileName)
// Recovering the private rsa key from file.
keyBytes, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, err
}
key, err := auth.PEMToPrivateKey(keyBytes)
if err != nil {
return nil, err
}
// Recovering the root certificate from file.
certBytes, err := ioutil.ReadFile(certPath)
if err != nil {
return nil, err
}
cert, err := auth.PEMToCertificate(certBytes)
if err != nil {
return nil, err
}
res := &PlatformID{
pkey: key,
rootCA: cert}
return res, nil
}
package autority
import (
"crypto/rsa"
"dfss/auth"
"fmt"
"os"
"path/filepath"
"testing"
)
var pkey *rsa.PrivateKey
func TestMain(m *testing.M) {
pkey, _ = auth.GeneratePrivateKey(512)
os.Exit(m.Run())
}
func TestGetHomeDir(t *testing.T) {
res := GetHomeDir()
if res == "" {
t.Fatal("Result is empty")
}
}
func TestGenerateRootCA(t *testing.T) {
res, err := GenerateRootCA(365, "country", "organization", "unit", "cn", pkey)
if res == nil || err != nil {
t.Fatal(err)
}
if res[0] != '-' {
t.Fatalf("Bad format\n%s", res)
}
}
func TestInitialize(t *testing.T) {
path := os.TempDir()
keyPath := filepath.Join(path, PkeyFileName)
certPath := filepath.Join(path, RootCAFileName)
err := Initialize(1024, 365, "country", "organization", "unit", "cn", path)
if err != nil {
t.Fatal(err)
}
if _, err = os.Stat(keyPath); os.IsNotExist(err) {
t.Fatal("Private key file couldn't be found")
} else {
os.Remove(keyPath)
}
if _, err = os.Stat(certPath); os.IsNotExist(err) {
t.Fatal("Root certificate file couldn't be found")
} else {
os.Remove(certPath)
}
}
func ExampleInitialize() {
path := os.TempDir()
keyPath := filepath.Join(path, PkeyFileName)
certPath := filepath.Join(path, RootCAFileName)
err := Initialize(1024, 365, "country", "organization", "unit", "cn", path)
if err != nil {
fmt.Println(err)
}
if _, err = os.Stat(keyPath); os.IsNotExist(err) {
fmt.Println("Private key file couldn't be found")
} else {
fmt.Println("Private key file has been found")
err2 := os.Remove(keyPath)
if err2 != nil {
fmt.Println(err2)
} else {
fmt.Println("Private key file has been deleted")
}
}
if _, err = os.Stat(certPath); os.IsNotExist(err) {
fmt.Println("Certificate file couldn't be found")
} else {
fmt.Println("Certificate file has been found")
err2 := os.Remove(certPath)
if err2 != nil {
fmt.Println(err2)
} else {
fmt.Println("Certificate file has been deleted")
}
}
// Output:
// Private key file has been found
// Private key file has been deleted
// Certificate file has been found
// Certificate file has been deleted
}
func TestStart(t *testing.T) {
path := os.TempDir()
keyPath := filepath.Join(path, PkeyFileName)
certPath := filepath.Join(path, RootCAFileName)
_ = Initialize(1024, 365, "country", "organization", "unit", "cn", path)
pid, err := Start(path)
if err != nil {
t.Fatal(err)
}
if pid == nil || pid.pkey == nil || pid.rootCA == nil {
t.Fatal("Data was not recovered from saved files")
}
os.Remove(keyPath)
os.Remove(certPath)
}
......@@ -2,19 +2,32 @@ package main
import (
"dfss"
"dfss/dfssp/autority"
"flag"
"fmt"
"runtime"
)
var (
verbose bool
verbose bool
path, country, org, unit, cn string
keySize, validity int
pid *autority.PlatformID
)
func init() {
flag.BoolVar(&verbose, "v", false, "Print verbose messages")
flag.StringVar(&path, "path", autority.GetHomeDir(), "Path for the platform's private key and root certificate")
flag.StringVar(&country, "country", "France", "Country for the root certificate")
flag.StringVar(&org, "org", "DFSS", "Organization for the root certificate")
flag.StringVar(&unit, "unit", "INSA Rennes", "Organizational unit for the root certificate")
flag.StringVar(&cn, "cn", "dfssp", "Common name for the root certificate")
flag.IntVar(&keySize, "keySize", 512, "Encoding size for the private key")
flag.IntVar(&validity, "validity", 21, "Root certificate's validity duration (days)")
flag.Usage = func() {
fmt.Println("DFSS platform v" + dfss.Version)
fmt.Println("Users and contracts manager")
......@@ -23,6 +36,10 @@ func init() {
fmt.Println(" dfssp command [flags]")
fmt.Println("\nThe commands are:")
fmt.Println(" init [cn, country, keySize, org, path, unit, validity]")
fmt.Println(" create and save the platform's private key and root certificate")
fmt.Println(" start [path]")
fmt.Println(" start the platform after loading its private key and root certificate")
fmt.Println(" help print this help")
fmt.Println(" version print dfss client version")
......@@ -40,6 +57,22 @@ func main() {
switch command {
case "version":
fmt.Println("v"+dfss.Version, runtime.GOOS, runtime.GOARCH)
case "init":
err := autority.Initialize(keySize, validity, country, org, unit, cn, path)
if err != nil {
fmt.Println("An error occured during the initialization operation")
fmt.Println(err)
panic(err)
}
case "start":
pid, err := autority.Start(path)
if err != nil {
fmt.Println("An error occured during the start operation")
fmt.Println(err)
panic(err)
}
// TODO: use pid
_ = pid
default:
flag.Usage()
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment