Introducing new PKI management

This commit is contained in:
nemunaire 2018-01-21 14:18:26 +01:00
commit 0259ae8f94
19 changed files with 857 additions and 53 deletions

133
admin/pki/ca.go Normal file
View file

@ -0,0 +1,133 @@
package pki
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"io/ioutil"
"math/big"
"os"
"path"
"time"
)
var passwordCA string
func SetCAPassword(pass string) {
passwordCA = pass
}
func CACertPath() string {
return path.Join(PKIDir, "shared", "ca.pem")
}
func CAPrivkeyPath() string {
return path.Join(PKIDir, "ca.key")
}
func GenerateCA(notBefore time.Time, notAfter time.Time) error {
ca := &x509.Certificate{
SerialNumber: big.NewInt(0),
Subject: pkix.Name{
Organization: []string{"EPITA"},
OrganizationalUnit: []string{"SRS laboratory"},
Country: []string{"FR"},
Locality: []string{"Paris"},
CommonName: "FIC CA",
},
NotBefore: notBefore,
NotAfter: notAfter,
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
}
// Ensure directories exists
os.Mkdir(PKIDir, 0777)
os.Mkdir(path.Join(PKIDir, "shared"), 0777)
pub, priv, err := GeneratePrivKey()
if err != nil {
return err
}
ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv)
if err != nil {
return err
}
// Save certificate to file
if err := saveCertificate(CACertPath(), ca_b); err != nil {
return err
}
// Save private key to file
if err := savePrivateKeyEncrypted(CAPrivkeyPath(), priv, passwordCA); err != nil {
return err
}
return nil
}
func LoadCA() (priv ecdsa.PrivateKey, ca x509.Certificate, err error) {
// Load certificate
if fd, errr := os.Open(CACertPath()); err != nil {
return priv, ca, errr
} else {
defer fd.Close()
if cert, errr := ioutil.ReadAll(fd); err != nil {
return priv, ca, errr
} else {
block, _ := pem.Decode(cert)
if block == nil || block.Type != "CERTIFICATE" {
return priv, ca, errors.New("failed to decode PEM block containing certificate")
}
if catmp, errr := x509.ParseCertificate(block.Bytes); err != nil {
return priv, ca, errr
} else if catmp == nil {
return priv, ca, errors.New("failed to parse certificate")
} else {
ca = *catmp
}
}
}
// Load private key
if fd, errr := os.Open(CAPrivkeyPath()); err != nil {
return priv, ca, errr
} else {
defer fd.Close()
if privkey, errr := ioutil.ReadAll(fd); err != nil {
return priv, ca, errr
} else {
block, _ := pem.Decode(privkey)
if block == nil || block.Type != "EC PRIVATE KEY" {
return priv, ca, errors.New("failed to decode PEM block containing EC private key")
}
var decrypted_der []byte
if x509.IsEncryptedPEMBlock(block) {
decrypted_der, err = x509.DecryptPEMBlock(block, []byte(passwordCA))
if err != nil {
return
}
} else {
decrypted_der = block.Bytes
}
if tmppriv, errr := x509.ParseECPrivateKey(decrypted_der); err != nil {
return priv, ca, errr
} else if tmppriv == nil {
return priv, ca, errors.New("failed to parse private key")
} else {
priv = *tmppriv
}
}
}
return
}

81
admin/pki/client.go Normal file
View file

@ -0,0 +1,81 @@
package pki
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"os"
"os/exec"
"path"
"time"
)
func ClientCertificatePath(serial int64) string {
return path.Join(PKIDir, fmt.Sprintf("%d", serial), "cert.pem")
}
func ClientPrivkeyPath(serial int64) string {
return path.Join(PKIDir, fmt.Sprintf("%d", serial), "privkey.pem")
}
func ClientP12Path(serial int64) string {
return path.Join(PKIDir, fmt.Sprintf("%d", serial), "team.p12")
}
func GenerateClient(serial int64, notBefore time.Time, notAfter time.Time, parent_cert *x509.Certificate, parent_priv *ecdsa.PrivateKey) error {
client := &x509.Certificate{
SerialNumber: big.NewInt(serial),
Subject: pkix.Name{
Organization: []string{"EPITA"},
OrganizationalUnit: []string{"SRS laboratory"},
Country: []string{"FR"},
Locality: []string{"Paris"},
CommonName: fmt.Sprintf("TEAM-%o", serial),
},
NotBefore: notBefore,
NotAfter: notAfter,
IsCA: false,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
pub, priv, err := GeneratePrivKey()
if err != nil {
return err
}
client_b, err := x509.CreateCertificate(rand.Reader, client, parent_cert, pub, parent_priv)
if err != nil {
return err
}
// Create intermediate directory
os.MkdirAll(path.Join(PKIDir, fmt.Sprintf("%d", serial)), 0777)
// Save certificate to file
if err := saveCertificate(ClientCertificatePath(serial), client_b); err != nil {
return err
}
// Save private key to file
if err := savePrivateKey(ClientPrivkeyPath(serial), priv); err != nil {
return err
}
return nil
}
func WriteP12(serial int64, password string) error {
cmd := exec.Command("/usr/bin/openssl", "pkcs12", "-export",
"-inkey", ClientPrivkeyPath(serial),
"-in", ClientCertificatePath(serial),
"-name", fmt.Sprintf("TEAM-%o", serial),
"-passout", "pass:" + password,
"-out", ClientP12Path(serial))
return cmd.Run()
}

76
admin/pki/common.go Normal file
View file

@ -0,0 +1,76 @@
package pki
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"os"
)
var PKIDir string
func GeneratePassword() (password string, err error) {
// This will make a 12 chars long password
b := make([]byte, 9)
if _, err = rand.Read(b); err != nil {
return
}
password = base64.StdEncoding.EncodeToString(b)
return
}
func GeneratePrivKey() (pub *ecdsa.PublicKey, priv *ecdsa.PrivateKey, err error) {
if priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader); err == nil {
pub = &priv.PublicKey
}
return
}
func saveCertificate(path string, cert []byte) error {
if certOut, err := os.Create(path); err != nil {
return err
} else {
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
certOut.Close()
}
return nil
}
func savePrivateKey(path string, private *ecdsa.PrivateKey) error {
if keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
return err
} else if key_b, err := x509.MarshalECPrivateKey(private); err != nil {
return err
} else {
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: key_b})
keyOut.Close()
}
return nil
}
func savePrivateKeyEncrypted(path string, private *ecdsa.PrivateKey, password string) error {
if password == "" {
return savePrivateKey(path, private)
}
if keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
return err
} else {
defer keyOut.Close()
if key_b, err := x509.MarshalECPrivateKey(private); err != nil {
return err
} else if key_c, err := x509.EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", key_b, []byte(password), x509.PEMCipherAES256); err != nil {
return err
} else {
pem.Encode(keyOut, key_c)
}
}
return nil
}