go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/cmd/selfsigned-cert/main.go (about) 1 // Copyright 2023 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Executable selfsigned-cert generates a self-signed TLS certificate. 16 // 17 // Largely based on https://go.dev/src/crypto/tls/generate_cert.go 18 package main 19 20 import ( 21 "crypto/rand" 22 "crypto/rsa" 23 "crypto/x509" 24 "crypto/x509/pkix" 25 "encoding/pem" 26 "flag" 27 "log" 28 "math/big" 29 "os" 30 "path/filepath" 31 "time" 32 ) 33 34 var ( 35 hostname = flag.String("hostname", "", "Server hostname (will be used as Subject CN), default to OS hostname") 36 duration = flag.Duration("duration", 5*365*24*time.Hour, "Duration that certificate is valid for") 37 rsaBits = flag.Int("rsa-bits", 4096, "Size of RSA key to generate") 38 keyOut = flag.String("key-out", "key.pem", "Where to store the private key") 39 certOut = flag.String("cert-out", "cert.pem", "Where to store the certificate") 40 ) 41 42 func main() { 43 flag.Parse() 44 if *hostname == "" { 45 var err error 46 if *hostname, err = os.Hostname(); err != nil { 47 log.Fatalf("Failed to get hostname: %s", err) 48 } 49 } 50 51 log.Printf("Generating %d bit RSA key", *rsaBits) 52 priv, err := rsa.GenerateKey(rand.Reader, *rsaBits) 53 if err != nil { 54 log.Fatalf("Failed to generate RSA key: %s", err) 55 } 56 privBytes, err := x509.MarshalPKCS8PrivateKey(priv) 57 if err != nil { 58 log.Fatalf("Unable to marshal private key: %s", err) 59 } 60 61 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 62 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 63 if err != nil { 64 log.Fatalf("Failed to generate serial number: %s", err) 65 } 66 67 log.Printf("Generating certificate for %q, SN %s", *hostname, serialNumber) 68 template := x509.Certificate{ 69 SerialNumber: serialNumber, 70 Subject: pkix.Name{CommonName: *hostname}, 71 DNSNames: []string{*hostname}, 72 NotBefore: time.Now().Add(-time.Hour), 73 NotAfter: time.Now().Add(*duration), 74 KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, 75 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 76 BasicConstraintsValid: true, 77 } 78 certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) 79 if err != nil { 80 log.Fatalf("Failed to create certificate: %s", err) 81 } 82 83 writePEM(*certOut, "CERTIFICATE", certBytes) 84 writePEM(*keyOut, "PRIVATE KEY", privBytes) 85 } 86 87 func writePEM(path, typ string, bytes []byte) { 88 log.Printf("Writing %s", path) 89 if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil { 90 log.Fatalf("Failed to create path to %s: %s", path, err) 91 } 92 f, err := os.Create(path) 93 if err != nil { 94 log.Fatalf("Failed to open %s: %s", path, err) 95 } 96 if err := pem.Encode(f, &pem.Block{Type: typ, Bytes: bytes}); err != nil { 97 log.Fatalf("Failed to write data to %s: %s", path, err) 98 } 99 if err := f.Close(); err != nil { 100 log.Fatalf("Error closing %s: %s", path, err) 101 } 102 }