2022-11-04 19:38:34 +01:00
|
|
|
package certifer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/aes"
|
|
|
|
"crypto/cipher"
|
|
|
|
"crypto/sha1"
|
|
|
|
"crypto/sha256"
|
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509/pkix"
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/gob"
|
|
|
|
"encoding/pem"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Cert struct {
|
|
|
|
x *certifier
|
|
|
|
Template *x509.Certificate
|
|
|
|
Bytes []byte
|
|
|
|
Key *KeyParameters
|
|
|
|
Parent *Cert
|
|
|
|
CA *Cert
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *certifier) NewCA() (*Cert, error) {
|
|
|
|
ca := &x509.Certificate{
|
|
|
|
SerialNumber: big.NewInt(1337),
|
|
|
|
Subject: pkix.Name{
|
|
|
|
Organization: []string{x.Orga},
|
|
|
|
OrganizationalUnit: []string{"ca"},
|
|
|
|
CommonName: "root",
|
|
|
|
},
|
|
|
|
NotBefore: time.Now(),
|
|
|
|
NotAfter: time.Now().Add(time.Hour * 24 * 365),
|
|
|
|
IsCA: true,
|
|
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
|
|
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
|
|
|
BasicConstraintsValid: true,
|
|
|
|
// up to this point this should be the bare minimum for a Parent cert
|
|
|
|
}
|
|
|
|
|
|
|
|
pk := createKeyPair()
|
|
|
|
|
|
|
|
// create certificate.
|
|
|
|
caBytes, err := x509.CreateCertificate(getRandom(), ca, ca, &pk.PublicKey, pk)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Cert{
|
2022-11-04 20:53:51 +01:00
|
|
|
x: x,
|
2022-11-04 19:38:34 +01:00
|
|
|
Template: ca,
|
|
|
|
Bytes: caBytes,
|
|
|
|
Key: NewKeyParameters(pk),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cert) NewCert(cn string) (*Cert, error) {
|
|
|
|
cert := &x509.Certificate{
|
|
|
|
SerialNumber: big.NewInt(1337 + int64(rand.Uint64())),
|
|
|
|
Subject: pkix.Name{
|
|
|
|
Organization: []string{c.Template.Subject.Organization[0]},
|
|
|
|
OrganizationalUnit: []string{c.x.Thing},
|
|
|
|
CommonName: cn,
|
|
|
|
},
|
|
|
|
// DNSNames: []string{"bloq", "localhost"},
|
|
|
|
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
|
|
|
|
NotBefore: time.Now(),
|
|
|
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
|
|
|
// SubjectKeyId: []byte{1, 2, 3, 4, 6},
|
|
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
|
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
|
|
|
}
|
|
|
|
|
|
|
|
pk := createKeyPair()
|
|
|
|
|
|
|
|
hash := sha1.Sum(pk.PublicKey.X.Bytes())
|
|
|
|
cert.SubjectKeyId = hash[:]
|
|
|
|
|
|
|
|
// caBytes, err := x509.CreateCertificate(r, ca, ca, &pk.PublicKey, pk)
|
|
|
|
certBytes, err := x509.CreateCertificate(getRandom(), cert, c.Template, &pk.PublicKey, c.Key.Key())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ca := c.CA
|
|
|
|
if ca == nil {
|
|
|
|
ca = c
|
|
|
|
}
|
|
|
|
return &Cert{
|
2022-11-04 20:53:51 +01:00
|
|
|
x: c.x,
|
2022-11-04 19:38:34 +01:00
|
|
|
Template: cert,
|
|
|
|
Bytes: certBytes,
|
|
|
|
Key: NewKeyParameters(pk),
|
|
|
|
Parent: c,
|
|
|
|
CA: ca,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cert) CertAsPem() []byte {
|
|
|
|
p := pem.EncodeToMemory(&pem.Block{
|
|
|
|
Type: "CERTIFICATE",
|
|
|
|
Bytes: c.Bytes,
|
|
|
|
})
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cert) KeyAsPem() ([]byte, error) {
|
|
|
|
sec, err := x509.MarshalECPrivateKey(c.Key.Key())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
buf := pem.EncodeToMemory(&pem.Block{
|
|
|
|
Type: "EC PRIVATE KEY",
|
|
|
|
Bytes: sec,
|
|
|
|
})
|
|
|
|
return buf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithoutKeys returns a copy of this Cert tree without any private keys.
|
|
|
|
// Note: The result is NOT a deep copy of the original cert tree. Certificates, Templates and
|
|
|
|
// byte slices will still point to the original data, so be careful and do not modify!
|
|
|
|
func (c *Cert) WithoutKeys() *Cert {
|
|
|
|
ret := c.WithoutParentKeys()
|
|
|
|
ret.Key = nil
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithoutParentKeys retruns a copy of this Cert tree without keys of the parent and ca.
|
|
|
|
// Note: The result is NOT a deep copy of the original cert tree. Certificates, Templates and
|
|
|
|
// byte slices will still point to the original data, so be careful and do not modify!
|
|
|
|
func (c *Cert) WithoutParentKeys() *Cert {
|
|
|
|
p := c.Parent
|
|
|
|
if p != nil {
|
|
|
|
p = p.WithoutKeys()
|
|
|
|
}
|
|
|
|
ca := c.CA
|
|
|
|
if ca != nil {
|
|
|
|
ca.WithoutKeys()
|
|
|
|
}
|
|
|
|
return &Cert{
|
2022-11-04 20:53:51 +01:00
|
|
|
x: c.x,
|
2022-11-04 19:38:34 +01:00
|
|
|
Template: c.Template,
|
|
|
|
Bytes: c.Bytes,
|
|
|
|
Key: c.Key,
|
|
|
|
Parent: p,
|
|
|
|
CA: ca,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cert) Export(includeKey bool) ([]byte, error) {
|
|
|
|
exportCert := c
|
|
|
|
if !includeKey {
|
|
|
|
exportCert = c.WithoutParentKeys()
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
tGob := gob.NewEncoder(buf)
|
|
|
|
if err := tGob.Encode(exportCert); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
blob := buf.Bytes()
|
|
|
|
bloblen := uint32(len(blob))
|
|
|
|
padding := (aes.BlockSize - ((len(blob) + 4) % aes.BlockSize)) % aes.BlockSize
|
|
|
|
paddedBlob := make([]byte, len(blob)+4+padding)
|
|
|
|
binary.BigEndian.PutUint32(paddedBlob[0:4], bloblen)
|
|
|
|
copy(paddedBlob[4:], blob)
|
|
|
|
//for i := 0; i < padding; i++ {
|
|
|
|
// use padding spaces. Nul bytes would confuse the json unmarshaller
|
|
|
|
//paddedBlob[len(paddedBlob)+i] = ' '
|
|
|
|
//}
|
|
|
|
tKeydata := sha256.Sum256([]byte(c.x.key)) // gives 32 bytes, which is a multiple of the block size
|
|
|
|
tIVdata := tKeydata[:aes.BlockSize]
|
|
|
|
block, err := aes.NewCipher(tKeydata[:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cbc := cipher.NewCBCEncrypter(block, tIVdata)
|
|
|
|
cbc.CryptBlocks(paddedBlob, paddedBlob)
|
|
|
|
return paddedBlob, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cert) ExportToFile(includeKey bool, filename string) error {
|
|
|
|
cBlob, err := c.Export(includeKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := os.WriteFile(filename, cBlob, 0600); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cert) String() string {
|
|
|
|
ret := ""
|
|
|
|
if c.Parent != nil {
|
|
|
|
ret += fmt.Sprintf("%v->", c.Parent)
|
|
|
|
}
|
|
|
|
key := ""
|
|
|
|
if c.Key != nil {
|
|
|
|
key = "(*)"
|
|
|
|
}
|
|
|
|
ret += fmt.Sprintf("[%v%v]", c.Template.Subject, key)
|
|
|
|
return ret
|
|
|
|
}
|