248 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package mtls
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto"
 | 
						|
	"crypto/ed25519"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/tls"
 | 
						|
	"crypto/x509"
 | 
						|
	"crypto/x509/pkix"
 | 
						|
	"encoding/pem"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
var bp = newBPool()
 | 
						|
 | 
						|
type bpool struct {
 | 
						|
	pool sync.Pool
 | 
						|
}
 | 
						|
 | 
						|
func newBPool() *bpool {
 | 
						|
	var bp bpool
 | 
						|
	bp.pool.New = alloc
 | 
						|
	return &bp
 | 
						|
}
 | 
						|
 | 
						|
func alloc() interface{} {
 | 
						|
	return &bytes.Buffer{}
 | 
						|
}
 | 
						|
 | 
						|
func (bp *bpool) Get() *bytes.Buffer {
 | 
						|
	return bp.pool.Get().(*bytes.Buffer)
 | 
						|
}
 | 
						|
 | 
						|
func (bp *bpool) Put(buf *bytes.Buffer) {
 | 
						|
	buf.Reset()
 | 
						|
	bp.pool.Put(buf)
 | 
						|
}
 | 
						|
 | 
						|
// NewCA creates new CA keypair
 | 
						|
func NewCA(opts ...CertificateOption) ([]byte, crypto.PrivateKey, error) {
 | 
						|
	options := NewCertificateOptions(opts...)
 | 
						|
 | 
						|
	crtreq := &x509.CertificateRequest{
 | 
						|
		Subject: pkix.Name{
 | 
						|
			Organization:       options.Organization,
 | 
						|
			OrganizationalUnit: options.OrganizationalUnit,
 | 
						|
			CommonName:         options.CommonName,
 | 
						|
		},
 | 
						|
		SignatureAlgorithm: options.SignatureAlgorithm,
 | 
						|
	}
 | 
						|
 | 
						|
	pemcsr, pemkey, err := newCsr(crtreq)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	pemcrt, err := SignCSR(pemcsr, nil, pemkey, opts...)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return pemcrt, pemkey, nil
 | 
						|
}
 | 
						|
 | 
						|
func NewIntermediate(cacrt *x509.Certificate, cakey crypto.PrivateKey, opts ...CertificateOption) ([]byte, crypto.PrivateKey, error) {
 | 
						|
	options := &CertificateOptions{}
 | 
						|
	for _, o := range opts {
 | 
						|
		o(options)
 | 
						|
	}
 | 
						|
 | 
						|
	crtreq := &x509.CertificateRequest{
 | 
						|
		Subject: pkix.Name{
 | 
						|
			Organization:       options.Organization,
 | 
						|
			OrganizationalUnit: options.OrganizationalUnit,
 | 
						|
			CommonName:         options.CommonName,
 | 
						|
		},
 | 
						|
		SignatureAlgorithm: options.SignatureAlgorithm,
 | 
						|
	}
 | 
						|
 | 
						|
	pemcsr, pemkey, err := newCsr(crtreq)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	pemcrt, err := SignCSR(pemcsr, cacrt, cakey)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return pemcrt, pemkey, nil
 | 
						|
}
 | 
						|
 | 
						|
// SignCSR sign certificate request and return signed pubkey
 | 
						|
func SignCSR(rawcsr []byte, cacrt *x509.Certificate, cakey crypto.PrivateKey, opts ...CertificateOption) ([]byte, error) {
 | 
						|
	if cacrt == nil {
 | 
						|
		opts = append(opts, CertificateIsCA(true))
 | 
						|
	}
 | 
						|
 | 
						|
	options := NewCertificateOptions(opts...)
 | 
						|
 | 
						|
	csr, err := x509.ParseCertificateRequest(rawcsr)
 | 
						|
	if err == nil {
 | 
						|
		err = csr.CheckSignature()
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	tpl := &x509.Certificate{
 | 
						|
		Signature:             csr.Signature,
 | 
						|
		SignatureAlgorithm:    csr.SignatureAlgorithm,
 | 
						|
		PublicKeyAlgorithm:    csr.PublicKeyAlgorithm,
 | 
						|
		PublicKey:             csr.PublicKey,
 | 
						|
		SerialNumber:          options.SerialNumber,
 | 
						|
		OCSPServer:            options.OCSPServer,
 | 
						|
		IssuingCertificateURL: options.IssuingCertificateURL,
 | 
						|
		Subject:               csr.Subject,
 | 
						|
		NotBefore:             options.NotBefore,
 | 
						|
		NotAfter:              options.NotAfter,
 | 
						|
		KeyUsage:              options.KeyUsage,
 | 
						|
		ExtKeyUsage:           options.ExtKeyUsage,
 | 
						|
		BasicConstraintsValid: true,
 | 
						|
		IsCA:                  options.IsCA,
 | 
						|
	}
 | 
						|
 | 
						|
	if options.IsCA {
 | 
						|
		cacrt = tpl
 | 
						|
	} else {
 | 
						|
		tpl.Issuer = cacrt.Subject
 | 
						|
	}
 | 
						|
 | 
						|
	crt, err := x509.CreateCertificate(rand.Reader, tpl, cacrt, csr.PublicKey, cakey)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return crt, nil
 | 
						|
}
 | 
						|
 | 
						|
// NewCertificateRequest create new certificate signing request and return key, csr in byte slice and err
 | 
						|
func NewCertificateRequest(opts ...CertificateOption) ([]byte, crypto.PrivateKey, error) {
 | 
						|
	options := NewCertificateOptions(opts...)
 | 
						|
 | 
						|
	crtreq := &x509.CertificateRequest{
 | 
						|
		Subject: pkix.Name{
 | 
						|
			Organization:       options.Organization,
 | 
						|
			OrganizationalUnit: options.OrganizationalUnit,
 | 
						|
			CommonName:         options.CommonName,
 | 
						|
		},
 | 
						|
		SignatureAlgorithm: options.SignatureAlgorithm,
 | 
						|
	}
 | 
						|
 | 
						|
	return newCsr(crtreq)
 | 
						|
}
 | 
						|
 | 
						|
// newCsr returns CSR and private key
 | 
						|
func newCsr(crtreq *x509.CertificateRequest) ([]byte, crypto.PrivateKey, error) {
 | 
						|
	_, key, err := ed25519.GenerateKey(rand.Reader)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
	csr, err := x509.CreateCertificateRequest(rand.Reader, crtreq, key)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
	return csr, key, nil
 | 
						|
}
 | 
						|
 | 
						|
// ServerOptions holds server specific options
 | 
						|
type ServerOptions struct {
 | 
						|
	ServerName string
 | 
						|
	RootCAs    []string
 | 
						|
	ClientCAs  []string
 | 
						|
}
 | 
						|
 | 
						|
// ServerOption func signature
 | 
						|
type ServerOption func(*ServerOptions)
 | 
						|
 | 
						|
func NewServerConfig(src *tls.Config) *tls.Config {
 | 
						|
	dst := src.Clone()
 | 
						|
	// dst.InsecureSkipVerify = true
 | 
						|
	dst.MinVersion = tls.VersionTLS13
 | 
						|
	dst.ClientAuth = tls.VerifyClientCertIfGiven
 | 
						|
	return dst
 | 
						|
}
 | 
						|
 | 
						|
func DecodeCrtKey(rawcrt []byte, rawkey []byte) (*x509.Certificate, crypto.PrivateKey, error) {
 | 
						|
	var crt *x509.Certificate
 | 
						|
	var key crypto.PrivateKey
 | 
						|
	var err error
 | 
						|
 | 
						|
	crt, err = DecodeCrt(rawcrt)
 | 
						|
	if err == nil {
 | 
						|
		key, err = DecodeKey(rawkey)
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return crt, key, nil
 | 
						|
}
 | 
						|
 | 
						|
func DecodeCrt(rawcrt []byte) (*x509.Certificate, error) {
 | 
						|
	pemcrt, _ := pem.Decode(rawcrt)
 | 
						|
	return x509.ParseCertificate(pemcrt.Bytes)
 | 
						|
}
 | 
						|
 | 
						|
func EncodeCrt(crts ...*x509.Certificate) ([]byte, error) {
 | 
						|
	var err error
 | 
						|
	buf := bp.Get()
 | 
						|
	defer bp.Put(buf)
 | 
						|
	for _, crt := range crts {
 | 
						|
		if err = pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: crt.Raw}); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return buf.Bytes(), nil
 | 
						|
}
 | 
						|
 | 
						|
func EncodeCsr(csr *x509.Certificate) ([]byte, error) {
 | 
						|
	buf := bp.Get()
 | 
						|
	defer bp.Put(buf)
 | 
						|
	if err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csr.Raw}); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return buf.Bytes(), nil
 | 
						|
}
 | 
						|
 | 
						|
func DecodeKey(rawkey []byte) (crypto.PrivateKey, error) {
 | 
						|
	pemkey, _ := pem.Decode(rawkey)
 | 
						|
	return x509.ParsePKCS8PrivateKey(pemkey.Bytes)
 | 
						|
}
 | 
						|
 | 
						|
func EncodeKey(privkey crypto.PrivateKey) ([]byte, error) {
 | 
						|
	buf := bp.Get()
 | 
						|
	defer bp.Put(buf)
 | 
						|
	enckey, err := x509.MarshalPKCS8PrivateKey(privkey)
 | 
						|
	if err == nil {
 | 
						|
		err = pem.Encode(buf, &pem.Block{Type: "PRIVATE KEY", Bytes: enckey})
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return buf.Bytes(), nil
 | 
						|
}
 |