Files
ziti/internal/cert/cert.go
T
2022-06-30 17:45:08 -04:00

284 lines
6.6 KiB
Go

/*
Copyright NetFoundry Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cert
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net"
"net/url"
"strings"
"time"
)
type SignFunc func([]byte, *SigningOpts) ([]byte, error)
type CertPem struct {
Cert *x509.Certificate
Pem []byte
}
type Signer interface {
SignCsr(*x509.CertificateRequest, *SigningOpts) ([]byte, error)
SigningCert() *x509.Certificate
Cert() *x509.Certificate
Signer() crypto.Signer
}
type SigningOpts struct {
// Subject Alternate Name values.
DNSNames []string
EmailAddresses []string
IPAddresses []net.IP
URIs []*url.URL
NotBefore *time.Time
NotAfter *time.Time
}
func (so *SigningOpts) Apply(c *x509.Certificate) {
c.DNSNames = so.DNSNames
c.EmailAddresses = so.EmailAddresses
c.IPAddresses = so.IPAddresses
c.URIs = so.URIs
if so.NotBefore != nil {
c.NotBefore = *so.NotBefore
}
if so.NotAfter != nil {
c.NotAfter = *so.NotAfter
}
}
type SerialGenerator interface {
Generate() *big.Int
}
type DefaultSerialGenerator struct{}
func (DefaultSerialGenerator) Generate() *big.Int {
//@todo this need to be better, this does not include negative numbers for 20bit values, nor is this managed
r, _ := rand.Int(rand.Reader, big.NewInt(524287))
return r
}
var _ Signer = &ServerSigner{}
type ServerSigner struct {
caCert *x509.Certificate
caKey crypto.PrivateKey
SerialGenerator SerialGenerator
}
func (s *ServerSigner) Cert() *x509.Certificate {
return s.caCert
}
func (s *ServerSigner) Signer() crypto.Signer {
return s.caKey.(crypto.Signer)
}
func NewServerSigner(caCert *x509.Certificate, caKey crypto.PrivateKey) *ServerSigner {
return &ServerSigner{
caCert: caCert,
caKey: caKey,
SerialGenerator: &DefaultSerialGenerator{},
}
}
func (s *ServerSigner) SigningCert() *x509.Certificate {
return s.caCert
}
func (s *ServerSigner) SignCsr(csr *x509.CertificateRequest, opts *SigningOpts) ([]byte, error) {
if err := csr.CheckSignature(); err != nil {
return nil, fmt.Errorf("CSR signature validation failed: %s", err)
}
// create client certificate template
certTemplate := x509.Certificate{
Signature: csr.Signature,
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
SerialNumber: s.SerialGenerator.Generate(),
Issuer: s.caCert.Subject,
Subject: csr.Subject,
NotBefore: time.Now().Add(-time.Minute),
NotAfter: time.Now().AddDate(1, 0, 0), //@todo make this an option?
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageDataEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
IsCA: false,
}
if opts != nil {
opts.Apply(&certTemplate)
}
cert, err := x509.CreateCertificate(rand.Reader, &certTemplate, s.caCert, csr.PublicKey, s.caKey)
if err != nil {
return nil, fmt.Errorf("could not sign cert: %s", err)
}
return cert, nil
}
var _ Signer = &ClientSigner{}
type ClientSigner struct {
caCert *x509.Certificate
caKey crypto.PrivateKey
SerialGenerator SerialGenerator
}
func (s *ClientSigner) Cert() *x509.Certificate {
return s.caCert
}
func (s *ClientSigner) Signer() crypto.Signer {
return s.caKey.(crypto.Signer)
}
func NewClientSigner(caCert *x509.Certificate, caKey crypto.PrivateKey) *ClientSigner {
return &ClientSigner{
caCert: caCert,
caKey: caKey,
SerialGenerator: &DefaultSerialGenerator{},
}
}
func (s *ClientSigner) SigningCert() *x509.Certificate {
return nil
}
func (s *ClientSigner) SignCsr(csr *x509.CertificateRequest, opts *SigningOpts) ([]byte, error) {
if err := csr.CheckSignature(); err != nil {
return nil, fmt.Errorf("CSR signature validation failed: %s", err)
}
// create client certificate template
certTemplate := x509.Certificate{
Signature: csr.Signature,
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
SerialNumber: s.SerialGenerator.Generate(),
Issuer: s.caCert.Subject,
Subject: csr.Subject,
NotBefore: time.Now().Add(-time.Minute),
NotAfter: time.Now().AddDate(1, 0, 0),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageDataEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
IsCA: false,
}
if opts != nil {
opts.Apply(&certTemplate)
}
cert, err := x509.CreateCertificate(rand.Reader, &certTemplate, s.caCert, csr.PublicKey, s.caKey)
if err != nil {
return nil, fmt.Errorf("could not sign cert: %s", err)
}
return cert, nil
}
func RawToPem(raw []byte) ([]byte, error) {
cert := bytes.NewBuffer(make([]byte, 0))
err := pem.Encode(cert, &pem.Block{Type: "CERTIFICATE", Bytes: raw})
if err != nil {
return nil, fmt.Errorf("could not create pem encoding: %s", err)
}
return cert.Bytes(), nil
}
func ParseCsrPem(csrPem []byte) (*x509.CertificateRequest, error) {
if len(csrPem) == 0 {
return nil, errors.New("csrPem must not be null or empty")
}
pemBlock, remainder := pem.Decode(csrPem)
if pemBlock == nil {
return nil, fmt.Errorf("could not decode csrPem as PEM")
}
if remainder == nil || len(remainder) != 0 {
return nil, fmt.Errorf("unexpected PEM blocks at end of CSR")
}
return x509.ParseCertificateRequest(pemBlock.Bytes)
}
func PemChain2Blocks(pemBuff string) ([]*pem.Block, error) {
remainder := []byte(strings.TrimSpace(pemBuff))
var b *pem.Block
numBlock := 0
var blocks []*pem.Block
for len(remainder) > 0 {
numBlock++
b, remainder = pem.Decode(remainder)
if b == nil {
return nil, fmt.Errorf("could not parse block #%d", numBlock)
}
if b.Type != "CERTIFICATE" {
return nil, fmt.Errorf("block #%d is not a certificate", numBlock)
}
blocks = append(blocks, b)
}
return blocks, nil
}
func Blocks2Certs(blocks []*pem.Block) ([]*x509.Certificate, error) {
var certs []*x509.Certificate
numCert := 0
for _, b := range blocks {
numCert++
c, err := x509.ParseCertificate(b.Bytes)
if err != nil {
return nil, fmt.Errorf("could not parse block #%d as an x509 certificate", numCert)
}
certs = append(certs, c)
}
return certs, nil
}