github.com/containerd/nerdctl@v1.7.7/pkg/testutil/testca/testca.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package testca 18 19 import ( 20 "crypto/rand" 21 "crypto/rsa" 22 "crypto/x509" 23 "crypto/x509/pkix" 24 "encoding/pem" 25 "fmt" 26 "math/big" 27 "net" 28 "os" 29 "path/filepath" 30 "testing" 31 "time" 32 33 "gotest.tools/v3/assert" 34 ) 35 36 type CA struct { 37 KeyPath string 38 CertPath string 39 40 t testing.TB 41 key *rsa.PrivateKey 42 cert *x509.Certificate 43 closeF func() error 44 } 45 46 func (ca *CA) Close() error { 47 return ca.closeF() 48 } 49 50 const keyLength = 4096 51 52 func New(t testing.TB) *CA { 53 key, err := rsa.GenerateKey(rand.Reader, keyLength) 54 assert.NilError(t, err) 55 56 cert := &x509.Certificate{ 57 SerialNumber: serialNumber(t), 58 Subject: pkix.Name{ 59 Organization: []string{"nerdctl test organization"}, 60 CommonName: fmt.Sprintf("nerdctl CA (%s)", t.Name()), 61 }, 62 NotBefore: time.Now(), 63 NotAfter: time.Now().Add(24 * time.Hour), 64 IsCA: true, 65 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, 66 KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 67 BasicConstraintsValid: true, 68 } 69 70 dir, err := os.MkdirTemp(t.TempDir(), "ca") 71 assert.NilError(t, err) 72 keyPath := filepath.Join(dir, "ca.key") 73 certPath := filepath.Join(dir, "ca.cert") 74 writePair(t, keyPath, certPath, cert, cert, key, key) 75 76 return &CA{ 77 KeyPath: keyPath, 78 CertPath: certPath, 79 t: t, 80 key: key, 81 cert: cert, 82 closeF: func() error { 83 return os.RemoveAll(dir) 84 }, 85 } 86 } 87 88 type Cert struct { 89 KeyPath string 90 CertPath string 91 closeF func() error 92 } 93 94 func (c *Cert) Close() error { 95 return c.closeF() 96 } 97 98 func (ca *CA) NewCert(host string) *Cert { 99 t := ca.t 100 101 key, err := rsa.GenerateKey(rand.Reader, keyLength) 102 assert.NilError(t, err) 103 104 cert := &x509.Certificate{ 105 SerialNumber: serialNumber(t), 106 Subject: pkix.Name{ 107 Organization: []string{"nerdctl test organization"}, 108 CommonName: fmt.Sprintf("nerdctl %s (%s)", host, t.Name()), 109 }, 110 NotBefore: time.Now(), 111 NotAfter: time.Now().Add(24 * time.Hour), 112 KeyUsage: x509.KeyUsageCRLSign, 113 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 114 DNSNames: []string{host}, 115 } 116 if ip := net.ParseIP(host); ip != nil { 117 cert.IPAddresses = append(cert.IPAddresses, ip) 118 } 119 120 dir, err := os.MkdirTemp(t.TempDir(), "cert") 121 assert.NilError(t, err) 122 certPath := filepath.Join(dir, "a.cert") 123 keyPath := filepath.Join(dir, "a.key") 124 writePair(t, keyPath, certPath, cert, ca.cert, key, ca.key) 125 126 return &Cert{ 127 CertPath: certPath, 128 KeyPath: keyPath, 129 closeF: func() error { 130 return os.RemoveAll(dir) 131 }, 132 } 133 } 134 135 func writePair(t testing.TB, keyPath, certPath string, cert, caCert *x509.Certificate, key, caKey *rsa.PrivateKey) { 136 keyF, err := os.Create(keyPath) 137 assert.NilError(t, err) 138 defer keyF.Close() 139 assert.NilError(t, pem.Encode(keyF, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})) 140 assert.NilError(t, keyF.Close()) 141 142 certB, err := x509.CreateCertificate(rand.Reader, cert, caCert, &key.PublicKey, caKey) 143 assert.NilError(t, err) 144 certF, err := os.Create(certPath) 145 assert.NilError(t, err) 146 defer certF.Close() 147 assert.NilError(t, pem.Encode(certF, &pem.Block{Type: "CERTIFICATE", Bytes: certB})) 148 assert.NilError(t, certF.Close()) 149 } 150 151 func serialNumber(t testing.TB) *big.Int { 152 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 60) 153 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 154 assert.NilError(t, err) 155 return serialNumber 156 }