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 }