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{ x: x, 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{ x: c.x, 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{ x: c.x, 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 }