github.com/micro/go-micro/v2@v2.9.1/util/pki/pki.go (about)

     1  // Package pki provides PKI all the PKI functions necessary to run micro over an untrusted network
     2  // including a CA
     3  package pki
     4  
     5  import (
     6  	"bytes"
     7  	"crypto/ed25519"
     8  	"crypto/rand"
     9  	"crypto/x509"
    10  	"encoding/pem"
    11  
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // GenerateKey returns an ed25519 key
    16  func GenerateKey() (ed25519.PublicKey, ed25519.PrivateKey, error) {
    17  	return ed25519.GenerateKey(rand.Reader)
    18  }
    19  
    20  // CA generates a self signed CA and returns cert, key in PEM format
    21  func CA(opts ...CertOption) ([]byte, []byte, error) {
    22  	opts = append(opts, IsCA())
    23  	options := CertOptions{}
    24  	for _, o := range opts {
    25  		o(&options)
    26  	}
    27  	template := &x509.Certificate{
    28  		SignatureAlgorithm:    x509.PureEd25519,
    29  		Subject:               options.Subject,
    30  		DNSNames:              options.DNSNames,
    31  		IPAddresses:           options.IPAddresses,
    32  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
    33  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
    34  		NotBefore:             options.NotBefore,
    35  		NotAfter:              options.NotAfter,
    36  		SerialNumber:          options.SerialNumber,
    37  		BasicConstraintsValid: true,
    38  	}
    39  	if options.IsCA {
    40  		template.IsCA = true
    41  		template.KeyUsage |= x509.KeyUsageCertSign
    42  	}
    43  	x509Cert, err := x509.CreateCertificate(rand.Reader, template, template, options.Pub, options.Priv)
    44  	if err != nil {
    45  		return nil, nil, err
    46  	}
    47  	cert, key := &bytes.Buffer{}, &bytes.Buffer{}
    48  	if err := pem.Encode(cert, &pem.Block{Type: "CERTIFICATE", Bytes: x509Cert}); err != nil {
    49  		return nil, nil, err
    50  	}
    51  	x509Key, err := x509.MarshalPKCS8PrivateKey(options.Priv)
    52  	if err != nil {
    53  		return nil, nil, err
    54  	}
    55  	if err := pem.Encode(key, &pem.Block{Type: "PRIVATE KEY", Bytes: x509Key}); err != nil {
    56  		return nil, nil, err
    57  	}
    58  
    59  	return cert.Bytes(), key.Bytes(), nil
    60  }
    61  
    62  // CSR generates a certificate request in PEM format
    63  func CSR(opts ...CertOption) ([]byte, error) {
    64  	options := CertOptions{}
    65  	for _, o := range opts {
    66  		o(&options)
    67  	}
    68  	csrTemplate := &x509.CertificateRequest{
    69  		Subject:            options.Subject,
    70  		SignatureAlgorithm: x509.PureEd25519,
    71  		DNSNames:           options.DNSNames,
    72  		IPAddresses:        options.IPAddresses,
    73  	}
    74  	out := &bytes.Buffer{}
    75  	csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, options.Priv)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csr}); err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	return out.Bytes(), nil
    84  }
    85  
    86  // Sign decodes a CSR and signs it with the CA
    87  func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) {
    88  	options := CertOptions{}
    89  	for _, o := range opts {
    90  		o(&options)
    91  	}
    92  	asn1CACrt, err := decodePEM(CACrt)
    93  	if err != nil {
    94  		return nil, errors.Wrap(err, "failed to decode CA Crt PEM")
    95  	}
    96  	if len(asn1CACrt) != 1 {
    97  		return nil, errors.Errorf("expected 1 CA Crt, got %d", len(asn1CACrt))
    98  	}
    99  	caCrt, err := x509.ParseCertificate(asn1CACrt[0].Bytes)
   100  	if err != nil {
   101  		return nil, errors.Wrap(err, "ca is not a valid certificate")
   102  	}
   103  	asn1CAKey, err := decodePEM(CAKey)
   104  	if err != nil {
   105  		return nil, errors.Wrap(err, "failed to decode CA  Key PEM")
   106  	}
   107  	if len(asn1CAKey) != 1 {
   108  		return nil, errors.Errorf("expected 1 CA Key, got %d", len(asn1CACrt))
   109  	}
   110  	caKey, err := x509.ParsePKCS8PrivateKey(asn1CAKey[0].Bytes)
   111  	if err != nil {
   112  		return nil, errors.Wrap(err, "ca key is not a valid private key")
   113  	}
   114  	asn1CSR, err := decodePEM(CSR)
   115  	if err != nil {
   116  		return nil, errors.Wrap(err, "failed to decode CSR PEM")
   117  	}
   118  	if len(asn1CSR) != 1 {
   119  		return nil, errors.Errorf("expected 1 CSR, got %d", len(asn1CSR))
   120  	}
   121  	csr, err := x509.ParseCertificateRequest(asn1CSR[0].Bytes)
   122  	if err != nil {
   123  		return nil, errors.Wrap(err, "csr is invalid")
   124  	}
   125  	template := &x509.Certificate{
   126  		SignatureAlgorithm:    x509.PureEd25519,
   127  		Subject:               csr.Subject,
   128  		DNSNames:              csr.DNSNames,
   129  		IPAddresses:           csr.IPAddresses,
   130  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   131  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   132  		NotBefore:             options.NotBefore,
   133  		NotAfter:              options.NotAfter,
   134  		SerialNumber:          options.SerialNumber,
   135  		BasicConstraintsValid: true,
   136  	}
   137  
   138  	x509Cert, err := x509.CreateCertificate(rand.Reader, template, caCrt, caCrt.PublicKey, caKey)
   139  	if err != nil {
   140  		return nil, errors.Wrap(err, "Couldn't sign certificate")
   141  	}
   142  	out := &bytes.Buffer{}
   143  	if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: x509Cert}); err != nil {
   144  		return nil, errors.Wrap(err, "couldn't encode cert")
   145  	}
   146  	return out.Bytes(), nil
   147  }
   148  
   149  func decodePEM(PEM []byte) ([]*pem.Block, error) {
   150  	var blocks []*pem.Block
   151  	var asn1 *pem.Block
   152  	var rest []byte
   153  	for {
   154  		asn1, rest = pem.Decode(PEM)
   155  		if asn1 == nil {
   156  			return nil, errors.New("PEM is not valid")
   157  		}
   158  		blocks = append(blocks, asn1)
   159  		if len(rest) == 0 {
   160  			break
   161  		}
   162  	}
   163  	return blocks, nil
   164  }