github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/btcutil/certgen.go (about)

     1  // Copyright (c) 2013-2015 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package btcutil
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/ecdsa"
    10  	"crypto/elliptic"
    11  	"crypto/rand"
    12  	_ "crypto/sha512" // Needed for RegisterHash in init
    13  	"crypto/x509"
    14  	"crypto/x509/pkix"
    15  	"encoding/pem"
    16  	"errors"
    17  	"fmt"
    18  	"math/big"
    19  	"net"
    20  	"os"
    21  	"time"
    22  )
    23  
    24  // NewTLSCertPair returns a new PEM-encoded x.509 certificate pair
    25  // based on a 521-bit ECDSA private key.  The machine's local interface
    26  // addresses and all variants of IPv4 and IPv6 localhost are included as
    27  // valid IP addresses.
    28  func NewTLSCertPair(organization string, validUntil time.Time, extraHosts []string) (cert, key []byte, err error) {
    29  	now := time.Now()
    30  	if validUntil.Before(now) {
    31  		return nil, nil, errors.New("validUntil would create an already-expired certificate")
    32  	}
    33  
    34  	priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
    35  	if err != nil {
    36  		return nil, nil, err
    37  	}
    38  
    39  	// end of ASN.1 time
    40  	endOfTime := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
    41  	if validUntil.After(endOfTime) {
    42  		validUntil = endOfTime
    43  	}
    44  
    45  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    46  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
    47  	if err != nil {
    48  		return nil, nil, fmt.Errorf("failed to generate serial number: %s", err)
    49  	}
    50  
    51  	host, err := os.Hostname()
    52  	if err != nil {
    53  		return nil, nil, err
    54  	}
    55  
    56  	ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}
    57  	dnsNames := []string{host}
    58  	if host != "localhost" {
    59  		dnsNames = append(dnsNames, "localhost")
    60  	}
    61  
    62  	addIP := func(ipAddr net.IP) {
    63  		for _, ip := range ipAddresses {
    64  			if bytes.Equal(ip, ipAddr) {
    65  				return
    66  			}
    67  		}
    68  		ipAddresses = append(ipAddresses, ipAddr)
    69  	}
    70  	addHost := func(host string) {
    71  		for _, dnsName := range dnsNames {
    72  			if host == dnsName {
    73  				return
    74  			}
    75  		}
    76  		dnsNames = append(dnsNames, host)
    77  	}
    78  
    79  	addrs, err := interfaceAddrs()
    80  	if err != nil {
    81  		return nil, nil, err
    82  	}
    83  	for _, a := range addrs {
    84  		ipAddr, _, err := net.ParseCIDR(a.String())
    85  		if err == nil {
    86  			addIP(ipAddr)
    87  		}
    88  	}
    89  
    90  	for _, hostStr := range extraHosts {
    91  		host, _, err := net.SplitHostPort(hostStr)
    92  		if err != nil {
    93  			host = hostStr
    94  		}
    95  		if ip := net.ParseIP(host); ip != nil {
    96  			addIP(ip)
    97  		} else {
    98  			addHost(host)
    99  		}
   100  	}
   101  
   102  	template := x509.Certificate{
   103  		SerialNumber: serialNumber,
   104  		Subject: pkix.Name{
   105  			Organization: []string{organization},
   106  			CommonName:   host,
   107  		},
   108  		NotBefore: now.Add(-time.Hour * 24),
   109  		NotAfter:  validUntil,
   110  
   111  		KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature |
   112  			x509.KeyUsageCertSign,
   113  		IsCA:                  true, // so can sign self.
   114  		BasicConstraintsValid: true,
   115  
   116  		DNSNames:    dnsNames,
   117  		IPAddresses: ipAddresses,
   118  	}
   119  
   120  	derBytes, err := x509.CreateCertificate(rand.Reader, &template,
   121  		&template, &priv.PublicKey, priv)
   122  	if err != nil {
   123  		return nil, nil, fmt.Errorf("failed to create certificate: %v", err)
   124  	}
   125  
   126  	certBuf := &bytes.Buffer{}
   127  	err = pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
   128  	if err != nil {
   129  		return nil, nil, fmt.Errorf("failed to encode certificate: %v", err)
   130  	}
   131  
   132  	keybytes, err := x509.MarshalECPrivateKey(priv)
   133  	if err != nil {
   134  		return nil, nil, fmt.Errorf("failed to marshal private key: %v", err)
   135  	}
   136  
   137  	keyBuf := &bytes.Buffer{}
   138  	err = pem.Encode(keyBuf, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keybytes})
   139  	if err != nil {
   140  		return nil, nil, fmt.Errorf("failed to encode private key: %v", err)
   141  	}
   142  
   143  	return certBuf.Bytes(), keyBuf.Bytes(), nil
   144  }