github.com/decred/dcrlnd@v0.7.6/cert/selfsigned.go (about)

     1  package cert
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/ecdsa"
     6  	"crypto/elliptic"
     7  	"crypto/rand"
     8  	"crypto/x509"
     9  	"crypto/x509/pkix"
    10  	"encoding/pem"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"math/big"
    14  	"net"
    15  	"os"
    16  	"time"
    17  )
    18  
    19  var (
    20  	// End of ASN.1 time.
    21  	endOfTime = time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
    22  
    23  	// Max serial number.
    24  	serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
    25  )
    26  
    27  // ipAddresses returns the parserd IP addresses to use when creating the TLS
    28  // certificate. If tlsDisableAutofill is true, we don't include interface
    29  // addresses to protect users privacy.
    30  func ipAddresses(tlsExtraIPs []string, tlsDisableAutofill bool) ([]net.IP, error) {
    31  	// Collect the host's IP addresses, including loopback, in a slice.
    32  	ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}
    33  
    34  	// addIP appends an IP address only if it isn't already in the slice.
    35  	addIP := func(ipAddr net.IP) {
    36  		for _, ip := range ipAddresses {
    37  			if ip.Equal(ipAddr) {
    38  				return
    39  			}
    40  		}
    41  		ipAddresses = append(ipAddresses, ipAddr)
    42  	}
    43  
    44  	// To protect their privacy, some users might not want to have all
    45  	// their network addresses include in the certificate as this could
    46  	// leak sensitive information.
    47  	if !tlsDisableAutofill {
    48  		// Add all the interface IPs that aren't already in the slice.
    49  		addrs, err := net.InterfaceAddrs()
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  		for _, a := range addrs {
    54  			ipAddr, _, err := net.ParseCIDR(a.String())
    55  			if err == nil {
    56  				addIP(ipAddr)
    57  			}
    58  		}
    59  	}
    60  
    61  	// Add extra IPs to the slice.
    62  	for _, ip := range tlsExtraIPs {
    63  		ipAddr := net.ParseIP(ip)
    64  		if ipAddr != nil {
    65  			addIP(ipAddr)
    66  		}
    67  	}
    68  
    69  	return ipAddresses, nil
    70  }
    71  
    72  // dnsNames returns the host and DNS names to use when creating the TLS
    73  // ceftificate.
    74  func dnsNames(tlsExtraDomains []string, tlsDisableAutofill bool) (string, []string) {
    75  	// Collect the host's names into a slice.
    76  	host, err := os.Hostname()
    77  
    78  	// To further protect their privacy, some users might not want
    79  	// to have their hostname include in the certificate as this could
    80  	// leak sensitive information.
    81  	if err != nil || tlsDisableAutofill {
    82  		// Nothing much we can do here, other than falling back to
    83  		// localhost as fallback. A hostname can still be provided with
    84  		// the tlsExtraDomain parameter if the problem persists on a
    85  		// system.
    86  		host = "localhost"
    87  	}
    88  
    89  	dnsNames := []string{host}
    90  	if host != "localhost" {
    91  		dnsNames = append(dnsNames, "localhost")
    92  	}
    93  	dnsNames = append(dnsNames, tlsExtraDomains...)
    94  
    95  	// Because we aren't including the hostname in the certificate when
    96  	// tlsDisableAutofill is set, we will use the first extra domain
    97  	// specified by the user, if it's set, as the Common Name.
    98  	if tlsDisableAutofill && len(tlsExtraDomains) > 0 {
    99  		host = tlsExtraDomains[0]
   100  	}
   101  
   102  	// Also add fake hostnames for unix sockets, otherwise hostname
   103  	// verification will fail in the client.
   104  	dnsNames = append(dnsNames, "unix", "unixpacket")
   105  
   106  	// Also add hostnames for 'bufconn' which is the hostname used for the
   107  	// in-memory connections used on mobile.
   108  	dnsNames = append(dnsNames, "bufconn")
   109  
   110  	return host, dnsNames
   111  }
   112  
   113  // IsOutdated returns whether the given certificate is outdated w.r.t. the IPs
   114  // and domains given. The certificate is considered up to date if it was
   115  // created with _exactly_ the IPs and domains given.
   116  func IsOutdated(cert *x509.Certificate, tlsExtraIPs,
   117  	tlsExtraDomains []string, tlsDisableAutofill bool) (bool, error) {
   118  
   119  	// Parse the slice of IP strings.
   120  	ips, err := ipAddresses(tlsExtraIPs, tlsDisableAutofill)
   121  	if err != nil {
   122  		return false, err
   123  	}
   124  
   125  	// To not consider the certificate outdated if it has duplicate IPs or
   126  	// if only the order has changed, we create two maps from the slice of
   127  	// IPs to compare.
   128  	ips1 := make(map[string]net.IP)
   129  	for _, ip := range ips {
   130  		ips1[ip.String()] = ip
   131  	}
   132  
   133  	ips2 := make(map[string]net.IP)
   134  	for _, ip := range cert.IPAddresses {
   135  		ips2[ip.String()] = ip
   136  	}
   137  
   138  	// If the certificate has a different number of IP addresses, it is
   139  	// definitely out of date.
   140  	if len(ips1) != len(ips2) {
   141  		return true, nil
   142  	}
   143  
   144  	// Go through each IP address, and check that they are equal. We expect
   145  	// both the string representation and the exact IP to match.
   146  	for s, ip1 := range ips1 {
   147  		// Assert the IP string is found in both sets.
   148  		ip2, ok := ips2[s]
   149  		if !ok {
   150  			return true, nil
   151  		}
   152  
   153  		// And that the IPs are considered equal.
   154  		if !ip1.Equal(ip2) {
   155  			return true, nil
   156  		}
   157  	}
   158  
   159  	// Get the full list of DNS names to use.
   160  	_, dnsNames := dnsNames(tlsExtraDomains, tlsDisableAutofill)
   161  
   162  	// We do the same kind of deduplication for the DNS names.
   163  	dns1 := make(map[string]struct{})
   164  	for _, n := range cert.DNSNames {
   165  		dns1[n] = struct{}{}
   166  	}
   167  
   168  	dns2 := make(map[string]struct{})
   169  	for _, n := range dnsNames {
   170  		dns2[n] = struct{}{}
   171  	}
   172  
   173  	// If the number of domains are different, it is out of date.
   174  	if len(dns1) != len(dns2) {
   175  		return true, nil
   176  	}
   177  
   178  	// Similarly, check that each DNS name matches what is found in the
   179  	// certificate.
   180  	for k := range dns1 {
   181  		if _, ok := dns2[k]; !ok {
   182  			return true, nil
   183  		}
   184  	}
   185  
   186  	// Certificate was up-to-date.
   187  	return false, nil
   188  }
   189  
   190  // GenCertPair generates a key/cert pair to the paths provided. The
   191  // auto-generated certificates should *not* be used in production for public
   192  // access as they're self-signed and don't necessarily contain all of the
   193  // desired hostnames for the service. For production/public use, consider a
   194  // real PKI.
   195  //
   196  // This function is adapted from https://github.com/decred/dcrd and
   197  // https://github.com/decred/dcrd/dcrutil/v3
   198  func GenCertPair(org, certFile, keyFile string, tlsExtraIPs,
   199  	tlsExtraDomains []string, tlsDisableAutofill bool,
   200  	certValidity time.Duration) error {
   201  
   202  	now := time.Now()
   203  	validUntil := now.Add(certValidity)
   204  
   205  	// Check that the certificate validity isn't past the ASN.1 end of time.
   206  	if validUntil.After(endOfTime) {
   207  		validUntil = endOfTime
   208  	}
   209  
   210  	// Generate a serial number that's below the serialNumberLimit.
   211  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
   212  	if err != nil {
   213  		return fmt.Errorf("failed to generate serial number: %s", err)
   214  	}
   215  
   216  	// Get all DNS names and IP addresses to use when creating the
   217  	// certificate.
   218  	host, dnsNames := dnsNames(tlsExtraDomains, tlsDisableAutofill)
   219  	ipAddresses, err := ipAddresses(tlsExtraIPs, tlsDisableAutofill)
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	// Generate a private key for the certificate.
   225  	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   226  	if err != nil {
   227  		return err
   228  	}
   229  
   230  	// Construct the certificate template.
   231  	template := x509.Certificate{
   232  		SerialNumber: serialNumber,
   233  		Subject: pkix.Name{
   234  			Organization: []string{org},
   235  			CommonName:   host,
   236  		},
   237  		NotBefore: now.Add(-time.Hour * 24),
   238  		NotAfter:  validUntil,
   239  
   240  		KeyUsage: x509.KeyUsageKeyEncipherment |
   241  			x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
   242  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   243  		IsCA:                  true, // so can sign self.
   244  		BasicConstraintsValid: true,
   245  
   246  		DNSNames:    dnsNames,
   247  		IPAddresses: ipAddresses,
   248  	}
   249  
   250  	derBytes, err := x509.CreateCertificate(rand.Reader, &template,
   251  		&template, &priv.PublicKey, priv)
   252  	if err != nil {
   253  		return fmt.Errorf("failed to create certificate: %v", err)
   254  	}
   255  
   256  	certBuf := &bytes.Buffer{}
   257  	err = pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE",
   258  		Bytes: derBytes})
   259  	if err != nil {
   260  		return fmt.Errorf("failed to encode certificate: %v", err)
   261  	}
   262  
   263  	keybytes, err := x509.MarshalECPrivateKey(priv)
   264  	if err != nil {
   265  		return fmt.Errorf("unable to encode privkey: %v", err)
   266  	}
   267  	keyBuf := &bytes.Buffer{}
   268  	err = pem.Encode(keyBuf, &pem.Block{Type: "EC PRIVATE KEY",
   269  		Bytes: keybytes})
   270  	if err != nil {
   271  		return fmt.Errorf("failed to encode private key: %v", err)
   272  	}
   273  
   274  	// Write cert and key files.
   275  	if err = ioutil.WriteFile(certFile, certBuf.Bytes(), 0644); err != nil {
   276  		return err
   277  	}
   278  	if err = ioutil.WriteFile(keyFile, keyBuf.Bytes(), 0600); err != nil {
   279  		os.Remove(certFile)
   280  		return err
   281  	}
   282  
   283  	return nil
   284  }