Introducing new PKI management
This commit is contained in:
parent
69640506d0
commit
0259ae8f94
19 changed files with 857 additions and 53 deletions
133
admin/pki/ca.go
Normal file
133
admin/pki/ca.go
Normal 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
81
admin/pki/client.go
Normal 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
76
admin/pki/common.go
Normal 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
|
||||
}
|
||||
Reference in a new issue