Skip to content

Commit

Permalink
Merge pull request #6 from moshloop/updates
Browse files Browse the repository at this point in the history
Updates
  • Loading branch information
moshloop authored Jan 25, 2020
2 parents c9ee72e + 7521535 commit b6975ea
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 262 deletions.
66 changes: 66 additions & 0 deletions certs/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package certs

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"time"
)

type CertificateBuilder struct {
*Certificate
}

func NewCertificateBuilder(commonName string) *CertificateBuilder {
b := &CertificateBuilder{}
b.Certificate = &Certificate{}
b.Certificate.X509 = &x509.Certificate{
Subject: pkix.Name{
CommonName: commonName,
},
}
key, _ := rsa.GenerateKey(rand.Reader, 2048)
b.Certificate.PrivateKey = key
return b
}

func (b *CertificateBuilder) Organization(org string) *CertificateBuilder {
b.Certificate.X509.Subject.Organization = []string{org}
return b
}

func (b *CertificateBuilder) OrganizationUnit(ou string) *CertificateBuilder {
b.Certificate.X509.Subject.OrganizationalUnit = []string{ou}
return b
}

func (b *CertificateBuilder) AltName(names ...string) *CertificateBuilder {
b.Certificate.X509.DNSNames = append(b.Certificate.X509.DNSNames, names...)
return b
}

func (b *CertificateBuilder) Server() *CertificateBuilder {
b.Certificate.X509.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
return b
}

func (b *CertificateBuilder) Client() *CertificateBuilder {
b.Certificate.X509.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
return b
}

func (b *CertificateBuilder) CA() *CertificateBuilder {
b.Certificate.X509.KeyUsage = b.Certificate.X509.KeyUsage | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign
b.Certificate.X509.MaxPathLenZero = true
b.Certificate.X509.BasicConstraintsValid = true
b.Certificate.X509.MaxPathLen = 0
b.Certificate.X509.IsCA = true
return b
}

func (b *CertificateBuilder) ValidYears(years int) *CertificateBuilder {
b.Certificate.X509.NotAfter = time.Now().Add(time.Duration(years) * time.Hour * 24 * 365)
b.Certificate.X509.NotBefore = time.Now().Add(-2 * time.Hour)
return b
}
59 changes: 59 additions & 0 deletions certs/ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package certs

import (
"crypto/rand"
"crypto/x509"
"math"
"math/big"
"time"

"github.com/pkg/errors"
)

type CertificateAuthority interface {
SignCertificate(cert *Certificate, expiryYears int) (*Certificate, error)
Sign(cert *x509.Certificate, expiry time.Duration) (*x509.Certificate, error)
GetPublicChain() []*Certificate
}

func (c Certificate) GetPublicChain() []*Certificate {
return append(c.Chain, &Certificate{X509: c.X509})
}

func (ca *Certificate) SignCertificate(cert *Certificate, expiryYears int) (*Certificate, error) {
if cert.X509.PublicKey == nil && cert.PrivateKey != nil {
cert.X509.PublicKey = cert.PrivateKey.Public()
}
signed, err := ca.Sign(cert.X509, time.Hour*24*364*time.Duration(expiryYears))
if err != nil {
return nil, err
}

return &Certificate{
X509: signed,
PrivateKey: cert.PrivateKey,
}, nil
}

func (ca *Certificate) Sign(cert *x509.Certificate, expiry time.Duration) (*x509.Certificate, error) {
if cert.SerialNumber == nil {
serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, errors.Wrap(err, "failed to generate random integer for signed certificate")
}
cert.SerialNumber = serial
}

// Account for clock skew
cert.NotBefore = time.Now().Add(15 * time.Minute * -1).UTC()
cert.NotAfter = time.Now().Add(expiry).UTC()
if cert.KeyUsage == 0 {
cert.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
}

b, err := x509.CreateCertificate(rand.Reader, cert, ca.X509, cert.PublicKey, ca.PrivateKey)
if err != nil {
return nil, err
}
return x509.ParseCertificate(b)
}
113 changes: 113 additions & 0 deletions certs/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package certs

import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"strings"
)

// Certificate is a X509 certifcate / private key pair
type Certificate struct {
X509 *x509.Certificate
PrivateKey *rsa.PrivateKey
Chain []*Certificate
}

// DecryptCertificate decrypts a certificate / private key pair and returns a Certificate
func DecryptCertificate(cert []byte, privateKey []byte, password []byte) (*Certificate, error) {

var err error
var key *rsa.PrivateKey
block, _ := pem.Decode(privateKey)

var decrypted []byte
if decrypted, err = x509.DecryptPEMBlock(block, password); err != nil {
return nil, err
}
if key, err = parsePrivateKey(decrypted); err != nil {
return nil, err
}

x509, err := decodeCertPEM(cert)
if err != nil {
return nil, err
}

return &Certificate{
PrivateKey: key,
X509: x509,
}, nil
}

// DecodeCertificate decodes a certificate / private key pair and returns a Certificate
func DecodeCertificate(cert []byte, privateKey []byte) (*Certificate, error) {
x509, err := decodeCertPEM(cert)
if err != nil {
return nil, fmt.Errorf("cannot decode certificate %v", err)
}

key, err := decodePrivateKeyPEM(privateKey)
if err != nil {
return nil, fmt.Errorf("cannot decode private key %v", err)
}
return &Certificate{
PrivateKey: key,
X509: x509,
}, nil
}

// EncodedPrivateKey returns PEM-encoded private key data.
func (c Certificate) EncodedPrivateKey() []byte {
block := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(c.PrivateKey),
}
return pem.EncodeToMemory(&block)
}

// EncodedPublicKey returns PEM-encoded public key data.
func (c Certificate) EncodedPublicKey() []byte {

publicKey := c.PrivateKey.PublicKey

der, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
panic(err)
}

if len(der) == 0 {
panic("nil pub key")
}

block := pem.Block{
Type: "PUBLIC KEY",
Bytes: der,
}
return pem.EncodeToMemory(&block)
}

// GetHash returns the encoded sha256 hash for the certificate
func (c Certificate) GetHash() (string, error) {
certHash := sha256.Sum256(c.X509.RawSubjectPublicKeyInfo)
return "sha256:" + strings.ToLower(hex.EncodeToString(certHash[:])), nil
}

// EncodedCertificate returns PEM-endcoded certificate data.
func (c Certificate) EncodedCertificate() []byte {
block := pem.Block{
Type: "CERTIFICATE",
Bytes: c.X509.Raw,
}
return pem.EncodeToMemory(&block)
}

func (c *Certificate) AsTLSSecret() map[string][]byte {
return map[string][]byte{
"tls.crt": c.EncodedCertificate(),
"tls.key": c.EncodedPrivateKey(),
}
}
Loading

0 comments on commit b6975ea

Please sign in to comment.