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  }