Generate certs on the fly in runner, pass trusted cert to shim

Rather than using the pre-generated certificates, generate them on the
fly. This allows TLS stacks for which certificate validation and
verification are coupled to work as expected. Certificates and keys are
written to temporary files which are then passed to the shim, and
cleaned up on exit. This requires reworking how testCase passes
certs/keys by adding a new field, sendCertificate, rather than manually
setting the -cert-file and -key-file flags. Incidentally the
rsaChainCertificate is removed, since it was essentially unused, and all
tests that used it also work with rsaCertificate. Finally, include a
single SAN ("test") in all certificates, which fixes some TLS stacks
which require this to operate (such as rustls, which currently
regenerates all the certificates currently in the tree to add a SAN).

Additionally, add a new flag, -trust-cert, which tells the the shim
which certificates it should trust. Shims for TLS stacks which
can completely decouple validation and verification of X509 certificates
(like BoringSSL) can ignore this flag, but for stacks where this
functionality is somewhat more intertwined (like Go), this allows the
shim to properly process the sent certificates.

Change-Id: Ic5c63e18fb2b852cc693aacb3b06cfe7993bc90c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/62565
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/cert.pem b/ssl/test/runner/cert.pem
deleted file mode 100644
index c360dc7..0000000
--- a/ssl/test/runner/cert.pem
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDtTCCAp2gAwIBAgIJALW2IrlaBKUhMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQwHhcNMTYwNzA5MDQzODA5WhcNMTYwODA4MDQzODA5WjBF
-MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
-ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAugvahBkSAUF1fC49vb1bvlPrcl80kop1iLpiuYoz4Qptwy57+EWssZBc
-HprZ5BkWf6PeGZ7F5AX1PyJbGHZLqvMCvViP6pd4MFox/igESISEHEixoiXCzepB
-rhtp5UQSjHD4D4hKtgdMgVxX+LRtwgW3mnu/vBu7rzpr/DS8io99p3lqZ1Aky+aN
-lcMj6MYy8U+YFEevb/V0lRY9oqwmW7BHnXikm/vi6sjIS350U8zb/mRzYeIs2R65
-LUduTL50+UMgat9ocewI2dv8aO9Dph+8NdGtg8LFYyTTHcUxJoMr1PTOgnmET19W
-JH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABo4GnMIGkMB0GA1UdDgQWBBT5m6Vv
-zYjVYHG30iBE+j2XDhUE8jB1BgNVHSMEbjBsgBT5m6VvzYjVYHG30iBE+j2XDhUE
-8qFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
-BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALW2IrlaBKUhMAwGA1UdEwQF
-MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAD7Jg68SArYWlcoHfZAB90Pmyrt5H6D8
-LRi+W2Ri1fBNxREELnezWJ2scjl4UMcsKYp4Pi950gVN+62IgrImcCNvtb5I1Cfy
-/MNNur9ffas6X334D0hYVIQTePyFk3umI+2mJQrtZZyMPIKSY/sYGQHhGGX6wGK+
-GO/og0PQk/Vu6D+GU2XRnDV0YZg1lsAsHd21XryK6fDmNkEMwbIWrts4xc7scRrG
-HWy+iMf6/7p/Ak/SIicM4XSwmlQ8pPxAZPr+E2LoVd9pMpWUwpW2UbtO5wsGTrY5
-sO45tFNN/y+jtUheB1C2ijObG/tXELaiyCdM+S/waeuv0MXtI4xnn1A=
------END CERTIFICATE-----
diff --git a/ssl/test/runner/channel_id_key.pem b/ssl/test/runner/channel_id_key.pem
index 604752b..83eb53a 100644
--- a/ssl/test/runner/channel_id_key.pem
+++ b/ssl/test/runner/channel_id_key.pem
@@ -1,5 +1,5 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIPwxu50c7LEhVNRYJFRWBUnoaz7JSos96T5hBp4rjyptoAoGCCqGSM49
-AwEHoUQDQgAEzFSVTE5guxJRQ0VbZ8dicPs5e/DT7xpW7Yc9hq0VOchv7cbXuI/T
-CwadDjGWX/oaz0ftFqrVmfkwZu+C58ioWg==
------END EC PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/DG7nRzssSFU1Fgk
+VFYFSehrPslKiz3pPmEGniuPKm2hRANCAATMVJVMTmC7ElFDRVtnx2Jw+zl78NPv
+Glbthz2GrRU5yG/txte4j9MLBp0OMZZf+hrPR+0WqtWZ+TBm74LnyKha
+-----END PRIVATE KEY-----
diff --git a/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go
index f7dcb9b..656a8c1 100644
--- a/ssl/test/runner/cipher_suites.go
+++ b/ssl/test/runner/cipher_suites.go
@@ -28,8 +28,8 @@
 	// In the case that the key agreement protocol doesn't use a
 	// ServerKeyExchange message, generateServerKeyExchange can return nil,
 	// nil.
-	generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg, uint16) (*serverKeyExchangeMsg, error)
-	processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
+	generateServerKeyExchange(*Config, *CertificateChain, *clientHelloMsg, *serverHelloMsg, uint16) (*serverKeyExchangeMsg, error)
+	processClientKeyExchange(*Config, *CertificateChain, *clientKeyExchangeMsg, uint16) ([]byte, error)
 
 	// On the client side, the next two methods are called in order.
 
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 676826f..5bb533d 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -10,9 +10,12 @@
 	"crypto/ecdsa"
 	"crypto/rand"
 	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
 	"fmt"
 	"io"
 	"math/big"
+	"os"
 	"strings"
 	"sync"
 	"time"
@@ -439,18 +442,18 @@
 	// If Time is nil, TLS uses time.Now.
 	Time func() time.Time
 
-	// Certificates contains one or more certificate chains
+	// Chains contains one or more certificate chains
 	// to present to the other side of the connection.
 	// Server configurations must include at least one certificate.
-	Certificates []Certificate
+	Chains []CertificateChain
 
-	// NameToCertificate maps from a certificate name to an element of
-	// Certificates. Note that a certificate name can be of the form
+	// NameToChain maps from a certificate name to an element of
+	// Chains. Note that a certificate name can be of the form
 	// '*.example.com' and so doesn't have to be a domain name as such.
 	// See Config.BuildNameToCertificate
-	// The nil value causes the first element of Certificates to be used
+	// The nil value causes the first element of Chains to be used
 	// for all connections.
-	NameToCertificate map[string]*Certificate
+	NameToChain map[string]*CertificateChain
 
 	// RootCAs defines the set of root certificate authorities
 	// that clients use when verifying server certificates.
@@ -1844,7 +1847,7 @@
 
 	// RenegotiationCertificate, if not nil, is the certificate to use on
 	// renegotiation handshakes.
-	RenegotiationCertificate *Certificate
+	RenegotiationCertificate *CertificateChain
 
 	// ExpectNoCertificateAuthoritiesExtension, if true, causes the client to
 	// reject CertificateRequest with the CertificateAuthorities extension.
@@ -2132,12 +2135,12 @@
 }
 
 // getCertificateForName returns the best certificate for the given name,
-// defaulting to the first element of c.Certificates if there are no good
+// defaulting to the first element of c.Chains if there are no good
 // options.
-func (c *Config) getCertificateForName(name string) *Certificate {
-	if len(c.Certificates) == 1 || c.NameToCertificate == nil {
+func (c *Config) getCertificateForName(name string) *CertificateChain {
+	if len(c.Chains) == 1 || c.NameToChain == nil {
 		// There's only one choice, so no point doing any work.
-		return &c.Certificates[0]
+		return &c.Chains[0]
 	}
 
 	name = strings.ToLower(name)
@@ -2145,7 +2148,7 @@
 		name = name[:len(name)-1]
 	}
 
-	if cert, ok := c.NameToCertificate[name]; ok {
+	if cert, ok := c.NameToChain[name]; ok {
 		return cert
 	}
 
@@ -2155,13 +2158,13 @@
 	for i := range labels {
 		labels[i] = "*"
 		candidate := strings.Join(labels, ".")
-		if cert, ok := c.NameToCertificate[candidate]; ok {
+		if cert, ok := c.NameToChain[candidate]; ok {
 			return cert
 		}
 	}
 
 	// If nothing matches, return the first certificate.
-	return &c.Certificates[0]
+	return &c.Chains[0]
 }
 
 func (c *Config) signSignatureAlgorithms() []signatureAlgorithm {
@@ -2178,28 +2181,28 @@
 	return supportedSignatureAlgorithms
 }
 
-// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
+// BuildNameToCertificate parses c.Chains and builds c.NameToCertificate
 // from the CommonName and SubjectAlternateName fields of each of the leaf
 // certificates.
 func (c *Config) BuildNameToCertificate() {
-	c.NameToCertificate = make(map[string]*Certificate)
-	for i := range c.Certificates {
-		cert := &c.Certificates[i]
+	c.NameToChain = make(map[string]*CertificateChain)
+	for i := range c.Chains {
+		cert := &c.Chains[i]
 		x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
 		if err != nil {
 			continue
 		}
 		if len(x509Cert.Subject.CommonName) > 0 {
-			c.NameToCertificate[x509Cert.Subject.CommonName] = cert
+			c.NameToChain[x509Cert.Subject.CommonName] = cert
 		}
 		for _, san := range x509Cert.DNSNames {
-			c.NameToCertificate[san] = cert
+			c.NameToChain[san] = cert
 		}
 	}
 }
 
-// A Certificate is a chain of one or more certificates, leaf first.
-type Certificate struct {
+// A CertificateChain is a chain of one or more certificates, leaf first.
+type CertificateChain struct {
 	Certificate [][]byte
 	PrivateKey  crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey
 	// OCSPStaple contains an optional OCSP response which will be served
@@ -2214,6 +2217,15 @@
 	// processing for TLS clients doing client authentication. If nil, the
 	// leaf certificate will be parsed as needed.
 	Leaf *x509.Certificate
+	// ChainPath is the path to the temporary on disk copy of the certificate
+	// chain.
+	ChainPath string
+	// KeyPath is the path to the temporary on disk copy of the key.
+	KeyPath string
+	// RootPath is the path to the temporary on disk copy of the root of the
+	// certificate chain. If the chain only contains one certificate ChainPath
+	// and RootPath will be the same.
+	RootPath string
 }
 
 // A TLS record.
@@ -2425,3 +2437,90 @@
 	}
 	return true
 }
+
+var baseCertTemplate = &x509.Certificate{
+	SerialNumber: big.NewInt(57005),
+	Subject: pkix.Name{
+		CommonName:   "test cert",
+		Country:      []string{"US"},
+		Province:     []string{"Some-State"},
+		Organization: []string{"Internet Widgits Pty Ltd"},
+	},
+	NotBefore:             time.Now().Add(-time.Hour),
+	NotAfter:              time.Now().Add(time.Hour),
+	DNSNames:              []string{"test"},
+	IsCA:                  true,
+	BasicConstraintsValid: true,
+}
+
+var tmpDir string
+
+func generateSingleCertChain(template *x509.Certificate, key crypto.Signer, ocspStaple, sctList []byte) CertificateChain {
+	cert := generateTestCert(template, nil, key, ocspStaple, sctList)
+	tmpCertPath, tmpKeyPath := writeTempCertFile([]*x509.Certificate{cert}), writeTempKeyFile(key)
+	return CertificateChain{
+		Certificate:                    [][]byte{cert.Raw},
+		PrivateKey:                     key,
+		OCSPStaple:                     ocspStaple,
+		SignedCertificateTimestampList: sctList,
+		Leaf:                           cert,
+		ChainPath:                      tmpCertPath,
+		KeyPath:                        tmpKeyPath,
+		RootPath:                       tmpCertPath,
+	}
+}
+
+func writeTempCertFile(certs []*x509.Certificate) string {
+	f, err := os.CreateTemp(tmpDir, "test-cert")
+	if err != nil {
+		panic(fmt.Sprintf("failed to create temp file: %s", err))
+	}
+	for _, cert := range certs {
+		if _, err := f.Write(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})); err != nil {
+			panic(fmt.Sprintf("failed to write test certificate: %s", err))
+		}
+	}
+	tmpCertPath := f.Name()
+	if err := f.Close(); err != nil {
+		panic(fmt.Sprintf("failed to close test certificate temp file: %s", err))
+	}
+	return tmpCertPath
+}
+
+func writeTempKeyFile(privKey crypto.Signer) string {
+	f, err := os.CreateTemp(tmpDir, "test-key")
+	if err != nil {
+		panic(fmt.Sprintf("failed to create temp file: %s", err))
+	}
+	keyDER, err := x509.MarshalPKCS8PrivateKey(privKey)
+	if err != nil {
+		panic(fmt.Sprintf("failed to marshal test key: %s", err))
+	}
+	if _, err := f.Write(pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})); err != nil {
+		panic(fmt.Sprintf("failed to write test key: %s", err))
+	}
+	tmpKeyPath := f.Name()
+	if err := f.Close(); err != nil {
+		panic(fmt.Sprintf("failed to close test key temp file: %s", err))
+	}
+	return tmpKeyPath
+}
+
+func generateTestCert(template, issuer *x509.Certificate, key crypto.Signer, ocspStaple, sctList []byte) *x509.Certificate {
+	if template == nil {
+		template = baseCertTemplate
+	}
+	if issuer == nil {
+		issuer = template
+	}
+	der, err := x509.CreateCertificate(rand.Reader, template, issuer, key.Public(), key)
+	if err != nil {
+		panic(fmt.Sprintf("failed to create test certificate: %s", err))
+	}
+	cert, err := x509.ParseCertificate(der)
+	if err != nil {
+		panic(fmt.Sprintf("failed to parse test certificate: %s", err))
+	}
+
+	return cert
+}
diff --git a/ssl/test/runner/ecdsa_p224_cert.pem b/ssl/test/runner/ecdsa_p224_cert.pem
deleted file mode 100644
index 29246a2..0000000
--- a/ssl/test/runner/ecdsa_p224_cert.pem
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBvjCCAWygAwIBAgIJAPlkrPTq4HgnMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT
-AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
-aXRzIFB0eSBMdGQwHhcNMTcwMjI3MjAxOTIzWhcNMTkwMjI3MjAxOTIzWjBFMQsw
-CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu
-ZXQgV2lkZ2l0cyBQdHkgTHRkME4wEAYHKoZIzj0CAQYFK4EEACEDOgAE6dul6dL0
-+CyooFiKK4V+EYNYPbMZoLTxRcjRgrw3db6QzBAviDSxKADTVuyRmaVC74Mf6boB
-HDmjUDBOMB0GA1UdDgQWBBSMtlkUJ7SCZ4zRqkjXMWvOebSgpTAfBgNVHSMEGDAW
-gBSMtlkUJ7SCZ4zRqkjXMWvOebSgpTAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMC
-A0AAMD0CHHolWPktSLbVMy9ukQUb2E7+Jb3hcNFqAXh47pYCHQC+jv2EE6oOEZ9F
-tLkFLtap71+83P0NUEJX4Etu
------END CERTIFICATE-----
diff --git a/ssl/test/runner/ecdsa_p256_cert.pem b/ssl/test/runner/ecdsa_p256_cert.pem
deleted file mode 100644
index 50bcbf5..0000000
--- a/ssl/test/runner/ecdsa_p256_cert.pem
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBzzCCAXagAwIBAgIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC
-QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
-dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ
-BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l
-dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni
-v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa
-HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw
-HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ
-BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E
-BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=
------END CERTIFICATE-----
diff --git a/ssl/test/runner/ecdsa_p384_cert.pem b/ssl/test/runner/ecdsa_p384_cert.pem
deleted file mode 100644
index 1fd3fec..0000000
--- a/ssl/test/runner/ecdsa_p384_cert.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICZTCCAeugAwIBAgIJAN+/LubpDwxNMAkGByqGSM49BAEwRTELMAkGA1UEBhMC
-QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp
-dHMgUHR5IEx0ZDAeFw0xNjA3MDkwMDAxMzJaFw0xNjA4MDgwMDAxMzJaMEUxCzAJ
-BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l
-dCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQOdTJNqxiZ
-+B68tCZV4GEJwDJ18jK9gFzvefcEAQluBijjrMjflL+RZAT64ExWzedRMp9PD9CW
-Tz9hG/Kz4q/l952YsIhy7LTGXzwy7549WUOi+N3aW8psDjtwzWNZXqWjgacwgaQw
-HQYDVR0OBBYEFKmYPjADcOlogOMU6D9wlftIWMj6MHUGA1UdIwRuMGyAFKmYPjAD
-cOlogOMU6D9wlftIWMj6oUmkRzBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29t
-ZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkggkA378u
-5ukPDE0wDAYDVR0TBAUwAwEB/zAJBgcqhkjOPQQBA2kAMGYCMQDTfL0OkRGnS5Ze
-tsxagAuZqM2Zyv5a2g7u6eFLCx2rpTuQndWOtEnmVo3wjTDtkDcCMQCg+05XSqEF
-cqxdXMZJMhqj2jS+tWucdgDstp/1KzJkbsupSjBzIycjVBKLdRwtNg8=
------END CERTIFICATE-----
diff --git a/ssl/test/runner/ecdsa_p521_cert.pem b/ssl/test/runner/ecdsa_p521_cert.pem
deleted file mode 100644
index 8b9a1e8..0000000
--- a/ssl/test/runner/ecdsa_p521_cert.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICrzCCAhGgAwIBAgIJAMZT7oSqTg/lMAkGByqGSM49BAEwRTELMAkGA1UEBhMC
-QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp
-dHMgUHR5IEx0ZDAeFw0xNjA3MDkwMDAwNTBaFw0xNjA4MDgwMDAwNTBaMEUxCzAJ
-BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l
-dCBXaWRnaXRzIFB0eSBMdGQwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAFD0TiZ
-InCoCYDI66FvoaZ0tniUhaNk2YnjEPikfmYHDwstbqkTEqnGAq3pKF2y/MHTBXFd
-E0KcFOPs5Ju8EgIcqwGvCkwHPmZtvvOrf+nJNHdm1gZfBUy09f3fVe/mHwfM2++H
-dz54nHKxtQqvdafhmMV77R/rBuI24MLKbMihxRgQKaOBpzCBpDAdBgNVHQ4EFgQU
-mLWm6ZtGfvih6iFx0+duYfHjGsYwdQYDVR0jBG4wbIAUmLWm6ZtGfvih6iFx0+du
-YfHjGsahSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEw
-HwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDGU+6Eqk4P5TAMBgNV
-HRMEBTADAQH/MAkGByqGSM49BAEDgYwAMIGIAkIBL/oEXMMQH9fefVd8onvgQbWI
-/CJXYE+kLO15gO8satasGutVpXtYGeP8gZeuHrq+jWxzj7dGM7YzkW0pAu+wOAkC
-QgFJSvKFcmbreLIjuwYik5aXeaUnTCvTQumG07cF0hZRgCf/kTxxmu2ffRoGCDTz
-XoPqlxwaO7K+gaAS//CvR0E3lw==
------END CERTIFICATE-----
diff --git a/ssl/test/runner/ed25519_cert.pem b/ssl/test/runner/ed25519_cert.pem
deleted file mode 100644
index 308c2c9..0000000
--- a/ssl/test/runner/ed25519_cert.pem
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBkTCCAUOgAwIBAgIJAJwooam0UCDmMAUGAytlcDBFMQswCQYDVQQGEwJBVTET
-MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1owRTELMAkGA1UE
-BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
-ZGdpdHMgUHR5IEx0ZDAqMAUGAytlcAMhANdamAGCsQq31Uv+08lkBzoO4XLz2qYj
-Ja8CGmj3B1Eao1AwTjAdBgNVHQ4EFgQUoux7eV+fJK2v3ah6QPU/lj1/+7UwHwYD
-VR0jBBgwFoAUoux7eV+fJK2v3ah6QPU/lj1/+7UwDAYDVR0TBAUwAwEB/zAFBgMr
-ZXADQQBuCzqji8VP9xU8mHEMjXGChX7YP5J664UyVKHKH9Z1u4wEbB8dJ3ScaWSL
-r+VHVKUhsrvcdCelnXRrrSD7xWAL
------END CERTIFICATE-----
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index e6db3f4..9b57b59 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -1183,7 +1183,7 @@
 		return err
 	}
 
-	var chainToSend *Certificate
+	var chainToSend *CertificateChain
 	var certReq *certificateRequestMsg
 	if c.didResume {
 		// Copy over authentication from the session.
@@ -1691,7 +1691,7 @@
 		}
 	}
 
-	var chainToSend *Certificate
+	var chainToSend *CertificateChain
 	var certRequested bool
 	certReq, ok := msg.(*certificateRequestMsg)
 	if ok {
@@ -1769,7 +1769,7 @@
 		}
 
 		// Determine the hash to sign.
-		privKey := c.config.Certificates[0].PrivateKey
+		privKey := c.config.Chains[0].PrivateKey
 
 		if certVerify.hasSignatureAlgorithm {
 			certVerify.signatureAlgorithm, err = selectSignatureAlgorithm(c.vers, privKey, c.config, certReq.signatureAlgorithms)
@@ -2403,18 +2403,18 @@
 // selectClientCertificate selects a certificate for use with the given
 // certificate, or none if none match. It may return a particular certificate or
 // nil on success, or an error on internal error.
-func selectClientCertificate(c *Conn, certReq *certificateRequestMsg) (*Certificate, error) {
-	if len(c.config.Certificates) == 0 {
+func selectClientCertificate(c *Conn, certReq *certificateRequestMsg) (*CertificateChain, error) {
+	if len(c.config.Chains) == 0 {
 		return nil, nil
 	}
 
 	// The test is assumed to have configured the certificate it meant to
 	// send.
-	if len(c.config.Certificates) > 1 {
+	if len(c.config.Chains) > 1 {
 		return nil, errors.New("tls: multiple certificates configured")
 	}
 
-	return &c.config.Certificates[0], nil
+	return &c.config.Chains[0], nil
 }
 
 // clientSessionCacheKey returns a key used to cache sessionTickets that could
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index aeb950b..8ad49ca 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -36,7 +36,7 @@
 	finishedHash    finishedHash
 	masterSecret    []byte
 	certsFromClient [][]byte
-	cert            *Certificate
+	cert            *CertificateChain
 	finishedBytes   []byte
 	echHPKEContext  *hpke.Context
 	echConfigID     uint8
@@ -1587,11 +1587,11 @@
 	if len(hs.clientHello.serverName) > 0 {
 		c.serverName = hs.clientHello.serverName
 	}
-	if len(config.Certificates) == 0 {
+	if len(config.Chains) == 0 {
 		c.sendAlert(alertInternalError)
 		return errors.New("tls: no certificates configured")
 	}
-	hs.cert = &config.Certificates[0]
+	hs.cert = &config.Chains[0]
 	if len(hs.clientHello.serverName) > 0 {
 		hs.cert = config.getCertificateForName(hs.clientHello.serverName)
 	}
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
index 95ef531..e513852 100644
--- a/ssl/test/runner/key_agreement.go
+++ b/ssl/test/runner/key_agreement.go
@@ -39,7 +39,7 @@
 	exportKey     *rsa.PrivateKey
 }
 
-func (ka *rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
+func (ka *rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *CertificateChain, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
 	// Save the client version for comparison later.
 	ka.clientVersion = clientHello.vers
 
@@ -96,7 +96,7 @@
 	return skx, nil
 }
 
-func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *CertificateChain, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
 	preMasterSecret := make([]byte, 48)
 	_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
 	if err != nil {
@@ -453,7 +453,7 @@
 // keyAgreementAuthentication is a helper interface that specifies how
 // to authenticate the ServerKeyExchange parameters.
 type keyAgreementAuthentication interface {
-	signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error)
+	signParameters(config *Config, cert *CertificateChain, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error)
 	verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, params []byte, sig []byte) error
 }
 
@@ -461,7 +461,7 @@
 // agreement parameters.
 type nilKeyAgreementAuthentication struct{}
 
-func (ka *nilKeyAgreementAuthentication) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) {
+func (ka *nilKeyAgreementAuthentication) signParameters(config *Config, cert *CertificateChain, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) {
 	skx := new(serverKeyExchangeMsg)
 	skx.key = params
 	return skx, nil
@@ -479,7 +479,7 @@
 	peerSignatureAlgorithm signatureAlgorithm
 }
 
-func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) {
+func (ka *signedKeyAgreement) signParameters(config *Config, cert *CertificateChain, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) {
 	// The message to be signed is prepended by the randoms.
 	var msg []byte
 	msg = append(msg, clientHello.random...)
@@ -585,7 +585,7 @@
 	peerKey []byte
 }
 
-func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
+func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *CertificateChain, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
 	var curveid CurveID
 	preferredCurves := config.curvePreferences()
 
@@ -636,7 +636,7 @@
 	return ka.auth.signParameters(config, cert, clientHello, hello, serverECDHParams)
 }
 
-func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *CertificateChain, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
 	if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
 		return nil, errClientKeyExchange
 	}
@@ -703,11 +703,11 @@
 // exchange.
 type nilKeyAgreement struct{}
 
-func (ka *nilKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
+func (ka *nilKeyAgreement) generateServerKeyExchange(config *Config, cert *CertificateChain, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
 	return nil, nil
 }
 
-func (ka *nilKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+func (ka *nilKeyAgreement) processClientKeyExchange(config *Config, cert *CertificateChain, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
 	if len(ckx.ciphertext) != 0 {
 		return nil, errClientKeyExchange
 	}
@@ -755,7 +755,7 @@
 	identityHint string
 }
 
-func (ka *pskKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
+func (ka *pskKeyAgreement) generateServerKeyExchange(config *Config, cert *CertificateChain, clientHello *clientHelloMsg, hello *serverHelloMsg, version uint16) (*serverKeyExchangeMsg, error) {
 	// Assemble the identity hint.
 	bytes := make([]byte, 2+len(config.PreSharedKeyIdentity))
 	bytes[0] = byte(len(config.PreSharedKeyIdentity) >> 8)
@@ -782,7 +782,7 @@
 	return skx, nil
 }
 
-func (ka *pskKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+func (ka *pskKeyAgreement) processClientKeyExchange(config *Config, cert *CertificateChain, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
 	// First, process the PSK identity.
 	if len(ckx.ciphertext) < 2 {
 		return nil, errClientKeyExchange
diff --git a/ssl/test/runner/rsa_1024_cert.pem b/ssl/test/runner/rsa_1024_cert.pem
deleted file mode 100644
index 4de4f49..0000000
--- a/ssl/test/runner/rsa_1024_cert.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICWDCCAcGgAwIBAgIJAPuwTC6rEJsMMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDIzMjA1MDQwWhcNMTcwNDIyMjA1MDQwWjBF
-MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
-ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
-gQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92kWdGMdAQhLci
-HnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiFKKAnHmUcrgfV
-W28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQABo1AwTjAdBgNV
-HQ4EFgQUi3XVrMsIvg4fZbf6Vr5sp3Xaha8wHwYDVR0jBBgwFoAUi3XVrMsIvg4f
-Zbf6Vr5sp3Xaha8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA76Hht
-ldY9avcTGSwbwoiuIqv0jTL1fHFnzy3RHMLDh+Lpvolc5DSrSJHCP5WuK0eeJXhr
-T5oQpHL9z/cCDLAKCKRa4uV0fhEdOWBqyR9p8y5jJtye72t6CuFUV5iqcpF4BH4f
-j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg==
------END CERTIFICATE-----
diff --git a/ssl/test/runner/key.pem b/ssl/test/runner/rsa_2048_key.pem
similarity index 100%
rename from ssl/test/runner/key.pem
rename to ssl/test/runner/rsa_2048_key.pem
diff --git a/ssl/test/runner/rsa_chain_cert.pem b/ssl/test/runner/rsa_chain_cert.pem
deleted file mode 100644
index 8eb6e60..0000000
--- a/ssl/test/runner/rsa_chain_cert.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD
-QTAeFw0xNjAyMjgyMDI3MDNaFw0yNjAyMjUyMDI3MDNaMBgxFjAUBgNVBAMMDUNs
-aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRvaz8
-CC/cshpCafJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/
-kLRcH89M/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3
-tHb+xs2PSs8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+c
-IDs2rQ+lP7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1
-z7C8jU50Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9V
-iLeXANgZi+Xx9KgfAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI
-KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBFEVbmYl+2RtNw
-rDftRDF1v2QUbcN2ouSnQDHxeDQdSgasLzT3ui8iYu0Rw2WWcZ0DV5e0ztGPhWq7
-AO0B120aFRMOY+4+bzu9Q2FFkQqc7/fKTvTDzIJI5wrMnFvUfzzvxh3OHWMYSs/w
-giq33hTKeHEq6Jyk3btCny0Ycecyc3yGXH10sizUfiHlhviCkDuESk8mFDwDDzqW
-ZF0IipzFbEDHoIxLlm3GQxpiLoEV4k8KYJp3R5KBLFyxM6UGPz8h72mIPCJp2RuK
-MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC
-1ngWZ7Ih
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS
-b290IENBMB4XDTE2MDIyODIwMjcwM1oXDTI2MDIyNTIwMjcwM1owDzENMAsGA1UE
-AwwEQiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALsSCYmDip2D
-GkjFxw7ykz26JSjELkl6ArlYjFJ3aT/SCh8qbS4gln7RH8CPBd78oFdfhIKQrwtZ
-3/q21ykD9BAS3qHe2YdcJfm8/kWAy5DvXk6NXU4qX334KofBAEpgdA/igEFq1P1l
-HAuIfZCpMRfT+i5WohVsGi8f/NgpRvVaMONLNfgw57mz1lbtFeBEISmX0kbsuJxF
-Qj/Bwhi5/0HAEXG8e7zN4cEx0yPRvmOATRdVb/8dW2pwOHRJq9R5M0NUkIsTSnL7
-6N/z8hRAHMsV3IudC5Yd7GXW1AGu9a+iKU+Q4xcZCoj0DC99tL4VKujrV1kAeqsM
-cz5/dKzi6+cCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
-AQYwDQYJKoZIhvcNAQELBQADggEBAIIeZiEeNhWWQ8Y4D+AGDwqUUeG8NjCbKrXQ
-BlHg5wZ8xftFaiP1Dp/UAezmx2LNazdmuwrYB8lm3FVTyaPDTKEGIPS4wJKHgqH1
-QPDhqNm85ey7TEtI9oYjsNim/Rb+iGkIAMXaxt58SzxbjvP0kMr1JfJIZbic9vye
-NwIspMFIpP3FB8ywyu0T0hWtCQgL4J47nigCHpOu58deP88fS/Nyz/fyGVWOZ76b
-WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv
-XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4=
------END CERTIFICATE-----
diff --git a/ssl/test/runner/rsa_chain_key.pem b/ssl/test/runner/rsa_chain_key.pem
deleted file mode 100644
index d94d704..0000000
--- a/ssl/test/runner/rsa_chain_key.pem
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRvaz8CC/cshpC
-afJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/kLRcH89M
-/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3tHb+xs2P
-Ss8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+cIDs2rQ+l
-P7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1z7C8jU50
-Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9ViLeXANgZ
-i+Xx9KgfAgMBAAECggEBAK0VjSJzkyPaamcyTVSWjo7GdaBGcK60lk657RjR+lK0
-YJ7pkej4oM2hdsVZFsP8Cs4E33nXLa/0pDsRov/qrp0WQm2skwqGMC1I/bZ0WRPk
-wHaDrBBfESWnJDX/AGpVtlyOjPmgmK6J2usMPihQUDkKdAYrVWJePrMIxt1q6BMe
-iczs3qriMmtY3bUc4UyUwJ5fhDLjshHvfuIpYQyI6EXZM6dZksn9LylXJnigY6QJ
-HxOYO0BDwOsZ8yQ8J8afLk88i0GizEkgE1z3REtQUwgWfxr1WV/ud+T6/ZhSAgH9
-042mQvSFZnIUSEsmCvjhWuAunfxHKCTcAoYISWfzWpkCgYEA7gpf3HHU5Tn+CgUn
-1X5uGpG3DmcMgfeGgs2r2f/IIg/5Ac1dfYILiybL1tN9zbyLCJfcbFpWBc9hJL6f
-CPc5hUiwWFJqBJewxQkC1Ae/HakHbip+IZ+Jr0842O4BAArvixk4Lb7/N2Ct9sTE
-NJO6RtK9lbEZ5uK61DglHy8CS2UCgYEA4ZC1o36kPAMQBggajgnucb2yuUEelk0f
-AEr+GI32MGE+93xMr7rAhBoqLg4AITyIfEnOSQ5HwagnIHonBbv1LV/Gf9ursx8Z
-YOGbvT8zzzC+SU1bkDzdjAYnFQVGIjMtKOBJ3K07++ypwX1fr4QsQ8uKL8WSOWwt
-Z3Bym6XiZzMCgYADnhy+2OwHX85AkLt+PyGlPbmuelpyTzS4IDAQbBa6jcuW/2wA
-UE2km75VUXmD+u2R/9zVuLm99NzhFhSMqlUxdV1YukfqMfP5yp1EY6m/5aW7QuIP
-2MDa7TVL9rIFMiVZ09RKvbBbQxjhuzPQKL6X/PPspnhiTefQ+dl2k9xREQKBgHDS
-fMfGNEeAEKezrfSVqxphE9/tXms3L+ZpnCaT+yu/uEr5dTIAawKoQ6i9f/sf1/Sy
-xedsqR+IB+oKrzIDDWMgoJybN4pkZ8E5lzhVQIjFjKgFdWLzzqyW9z1gYfABQPlN
-FiS20WX0vgP1vcKAjdNrHzc9zyHBpgQzDmAj3NZZAoGBAI8vKCKdH7w3aL5CNkZQ
-2buIeWNA2HZazVwAGG5F2TU/LmXfRKnG6dX5bkU+AkBZh56jNZy//hfFSewJB4Kk
-buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi
-SxpiPQ8d/hmSGwn4ksrWUsJD
------END PRIVATE KEY-----
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 8ddb590..c787f05 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -18,6 +18,7 @@
 	"bytes"
 	"crypto"
 	"crypto/ecdsa"
+	"crypto/ed25519"
 	"crypto/elliptic"
 	"crypto/rand"
 	"crypto/rsa"
@@ -110,110 +111,79 @@
 	HalfRTTTickets: 2,
 }
 
-type testCert int
-
 const (
-	testCertRSA testCert = iota
-	testCertRSA1024
-	testCertRSAChain
-	testCertECDSAP224
-	testCertECDSAP256
-	testCertECDSAP384
-	testCertECDSAP521
-	testCertEd25519
-)
+	rsa2048KeyFile = "rsa_2048_key.pem"
+	rsa1024KeyFile = "rsa_1024_key.pem"
 
-const (
-	rsaCertificateFile       = "cert.pem"
-	rsa1024CertificateFile   = "rsa_1024_cert.pem"
-	rsaChainCertificateFile  = "rsa_chain_cert.pem"
-	ecdsaP224CertificateFile = "ecdsa_p224_cert.pem"
-	ecdsaP256CertificateFile = "ecdsa_p256_cert.pem"
-	ecdsaP384CertificateFile = "ecdsa_p384_cert.pem"
-	ecdsaP521CertificateFile = "ecdsa_p521_cert.pem"
-	ed25519CertificateFile   = "ed25519_cert.pem"
-)
-
-const (
-	rsaKeyFile       = "key.pem"
-	rsa1024KeyFile   = "rsa_1024_key.pem"
-	rsaChainKeyFile  = "rsa_chain_key.pem"
 	ecdsaP224KeyFile = "ecdsa_p224_key.pem"
 	ecdsaP256KeyFile = "ecdsa_p256_key.pem"
 	ecdsaP384KeyFile = "ecdsa_p384_key.pem"
 	ecdsaP521KeyFile = "ecdsa_p521_key.pem"
-	ed25519KeyFile   = "ed25519_key.pem"
+
+	ed25519KeyFile = "ed25519_key.pem"
+
 	channelIDKeyFile = "channel_id_key.pem"
 )
 
 var (
-	rsaCertificate       Certificate
-	rsa1024Certificate   Certificate
-	rsaChainCertificate  Certificate
-	ecdsaP224Certificate Certificate
-	ecdsaP256Certificate Certificate
-	ecdsaP384Certificate Certificate
-	ecdsaP521Certificate Certificate
-	ed25519Certificate   Certificate
-	garbageCertificate   Certificate
+	rsa1024Key rsa.PrivateKey
+	rsa2048Key rsa.PrivateKey
+
+	ecdsaP224Key ecdsa.PrivateKey
+	ecdsaP256Key ecdsa.PrivateKey
+	ecdsaP384Key ecdsa.PrivateKey
+	ecdsaP521Key ecdsa.PrivateKey
+
+	ed25519Key ed25519.PrivateKey
+
+	channelIDKey ecdsa.PrivateKey
 )
 
-var testCerts = []struct {
-	id                testCert
-	certFile, keyFile string
-	cert              *Certificate
-}{
-	{
-		id:       testCertRSA,
-		certFile: rsaCertificateFile,
-		keyFile:  rsaKeyFile,
-		cert:     &rsaCertificate,
-	},
-	{
-		id:       testCertRSA1024,
-		certFile: rsa1024CertificateFile,
-		keyFile:  rsa1024KeyFile,
-		cert:     &rsa1024Certificate,
-	},
-	{
-		id:       testCertRSAChain,
-		certFile: rsaChainCertificateFile,
-		keyFile:  rsaChainKeyFile,
-		cert:     &rsaChainCertificate,
-	},
-	{
-		id:       testCertECDSAP224,
-		certFile: ecdsaP224CertificateFile,
-		keyFile:  ecdsaP224KeyFile,
-		cert:     &ecdsaP224Certificate,
-	},
-	{
-		id:       testCertECDSAP256,
-		certFile: ecdsaP256CertificateFile,
-		keyFile:  ecdsaP256KeyFile,
-		cert:     &ecdsaP256Certificate,
-	},
-	{
-		id:       testCertECDSAP384,
-		certFile: ecdsaP384CertificateFile,
-		keyFile:  ecdsaP384KeyFile,
-		cert:     &ecdsaP384Certificate,
-	},
-	{
-		id:       testCertECDSAP521,
-		certFile: ecdsaP521CertificateFile,
-		keyFile:  ecdsaP521KeyFile,
-		cert:     &ecdsaP521Certificate,
-	},
-	{
-		id:       testCertEd25519,
-		certFile: ed25519CertificateFile,
-		keyFile:  ed25519KeyFile,
-		cert:     &ed25519Certificate,
-	},
+func initKeys() {
+	// Since key generation is not particularly cheap (especially RSA), and the
+	// runner is intended to run on systems which may be resource constrained,
+	// we load keys from disk instead of dynamically generating them. We treat
+	// key files the same as dynamically generated certificates, writing them
+	// out to temporary files before passing them to the shim.
+
+	for _, k := range []struct {
+		path string
+		key  *rsa.PrivateKey
+	}{
+		{rsa1024KeyFile, &rsa1024Key},
+		{rsa2048KeyFile, &rsa2048Key},
+	} {
+		key, err := loadPEMKey(k.path)
+		if err != nil {
+			panic(fmt.Sprintf("failed to load RSA test key: %s", err))
+		}
+		*k.key = *(key.(*rsa.PrivateKey))
+	}
+
+	for _, k := range []struct {
+		path string
+		key  *ecdsa.PrivateKey
+	}{
+		{ecdsaP224KeyFile, &ecdsaP224Key},
+		{ecdsaP256KeyFile, &ecdsaP256Key},
+		{ecdsaP384KeyFile, &ecdsaP384Key},
+		{ecdsaP521KeyFile, &ecdsaP521Key},
+		{channelIDKeyFile, &channelIDKey},
+	} {
+		key, err := loadPEMKey(k.path)
+		if err != nil {
+			panic(fmt.Sprintf("failed to load ECDSA test key: %s", err))
+		}
+		*k.key = *(key.(*ecdsa.PrivateKey))
+	}
+
+	k, err := loadPEMKey(ed25519KeyFile)
+	if err != nil {
+		panic(fmt.Sprintf("failed to load Ed25519 test key: %s", err))
+	}
+	ed25519Key = k.(ed25519.PrivateKey)
 }
 
-var channelIDKey *ecdsa.PrivateKey
 var channelIDBytes []byte
 
 var testOCSPResponse = []byte{1, 2, 3, 4}
@@ -224,31 +194,35 @@
 var testOCSPExtension = append([]byte{byte(extensionStatusRequest) >> 8, byte(extensionStatusRequest), 0, 8, statusTypeOCSP, 0, 0, 4}, testOCSPResponse...)
 var testSCTExtension = append([]byte{byte(extensionSignedCertificateTimestamp) >> 8, byte(extensionSignedCertificateTimestamp), 0, byte(len(testSCTList))}, testSCTList...)
 
-func initCertificates() {
-	for i := range testCerts {
-		cert, err := LoadX509KeyPair(path.Join(*resourceDir, testCerts[i].certFile), path.Join(*resourceDir, testCerts[i].keyFile))
-		if err != nil {
-			panic(err)
-		}
-		cert.OCSPStaple = testOCSPResponse
-		cert.SignedCertificateTimestampList = testSCTList
-		*testCerts[i].cert = cert
-	}
+var (
+	rsaCertificate       CertificateChain
+	rsaChainCertificate  CertificateChain
+	rsa1024Certificate   CertificateChain
+	ecdsaP224Certificate CertificateChain
+	ecdsaP256Certificate CertificateChain
+	ecdsaP384Certificate CertificateChain
+	ecdsaP521Certificate CertificateChain
+	ed25519Certificate   CertificateChain
+	garbageCertificate   CertificateChain
+)
 
-	channelIDPEMBlock, err := os.ReadFile(path.Join(*resourceDir, channelIDKeyFile))
-	if err != nil {
-		panic(err)
-	}
-	channelIDDERBlock, _ := pem.Decode(channelIDPEMBlock)
-	if channelIDDERBlock.Type != "EC PRIVATE KEY" {
-		panic("bad key type")
-	}
-	channelIDKey, err = x509.ParseECPrivateKey(channelIDDERBlock.Bytes)
-	if err != nil {
-		panic(err)
-	}
-	if channelIDKey.Curve != elliptic.P256() {
-		panic("bad curve")
+var testCerts []*CertificateChain
+
+func initCertificates() {
+	for _, def := range []struct {
+		key crypto.Signer
+		out *CertificateChain
+	}{
+		{&rsa1024Key, &rsa1024Certificate},
+		{&rsa2048Key, &rsaCertificate},
+		{&ecdsaP224Key, &ecdsaP224Certificate},
+		{&ecdsaP256Key, &ecdsaP256Certificate},
+		{&ecdsaP384Key, &ecdsaP384Certificate},
+		{&ecdsaP521Key, &ecdsaP521Certificate},
+		{ed25519Key, &ed25519Certificate},
+	} {
+		*def.out = generateSingleCertChain(nil, def.key, testOCSPResponse, testSCTList)
+		testCerts = append(testCerts, def.out)
 	}
 
 	channelIDBytes = make([]byte, 64)
@@ -257,6 +231,31 @@
 
 	garbageCertificate.Certificate = [][]byte{[]byte("GARBAGE")}
 	garbageCertificate.PrivateKey = rsaCertificate.PrivateKey
+
+	// Build a basic three cert chain for testing chain specific things.
+	rootTmpl := *baseCertTemplate
+	rootTmpl.Subject.CommonName = "test root"
+	rootCert := generateTestCert(&rootTmpl, nil, &rsa2048Key, testOCSPResponse, testSCTList)
+	intermediateTmpl := *baseCertTemplate
+	intermediateTmpl.Subject.CommonName = "test inter"
+	intermediateCert := generateTestCert(&intermediateTmpl, rootCert, &rsa2048Key, testOCSPResponse, testSCTList)
+	leafTmpl := *baseCertTemplate
+	leafTmpl.IsCA, leafTmpl.BasicConstraintsValid = false, false
+	leafCert := generateTestCert(nil, intermediateCert, &rsa2048Key, testOCSPResponse, testSCTList)
+
+	keyPath := writeTempKeyFile(&rsa2048Key)
+	rootCertPath, chainPath := writeTempCertFile([]*x509.Certificate{rootCert}), writeTempCertFile([]*x509.Certificate{leafCert, intermediateCert})
+
+	rsaChainCertificate = CertificateChain{
+		Certificate:                    [][]byte{leafCert.Raw, intermediateCert.Raw},
+		PrivateKey:                     &rsa2048Key,
+		OCSPStaple:                     testOCSPResponse,
+		SignedCertificateTimestampList: testSCTList,
+		Leaf:                           leafCert,
+		ChainPath:                      chainPath,
+		KeyPath:                        keyPath,
+		RootPath:                       rootCertPath,
+	}
 }
 
 func flagInts(flagName string, vals []int) []string {
@@ -293,30 +292,28 @@
 	algo signatureAlgorithm
 }
 
-func loadRSAPrivateKey(filename string) (priv *rsa.PrivateKey, privPKCS8 []byte, err error) {
+func loadPEMKey(filename string) (crypto.PrivateKey, error) {
 	pemPath := path.Join(*resourceDir, filename)
 	pemBytes, err := os.ReadFile(pemPath)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	block, _ := pem.Decode(pemBytes)
 	if block == nil {
-		return nil, nil, fmt.Errorf("no PEM block found in %q", pemPath)
+		return nil, fmt.Errorf("no PEM block found in %q", pemPath)
 	}
-	privPKCS8 = block.Bytes
 
-	parsed, err := x509.ParsePKCS8PrivateKey(privPKCS8)
+	if block.Type != "PRIVATE KEY" {
+		return nil, fmt.Errorf("unexpected PEM type in %q (expected \"PRIVATE KEY\"): %s", pemPath, block.Type)
+	}
+
+	k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
 	if err != nil {
-		return nil, nil, fmt.Errorf("failed to parse PKCS#8 key from %q", pemPath)
+		return nil, fmt.Errorf("failed to parse PKCS#8 key from %q: %s", pemPath, err)
 	}
 
-	priv, ok := parsed.(*rsa.PrivateKey)
-	if !ok {
-		return nil, nil, fmt.Errorf("found %T in %q rather than an RSA private key", parsed, pemPath)
-	}
-
-	return priv, privPKCS8, nil
+	return k, nil
 }
 
 func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byte, parentPriv crypto.PrivateKey) (dc, privPKCS8 []uint8, err error) {
@@ -329,14 +326,12 @@
 
 	switch dcAlgo {
 	case signatureRSAPKCS1WithMD5, signatureRSAPKCS1WithSHA1, signatureRSAPKCS1WithSHA256, signatureRSAPKCS1WithSHA384, signatureRSAPKCS1WithSHA512, signatureRSAPSSWithSHA256, signatureRSAPSSWithSHA384, signatureRSAPSSWithSHA512:
-		// RSA keys are expensive to generate so load from disk instead.
-		var priv *rsa.PrivateKey
-		if priv, privPKCS8, err = loadRSAPrivateKey(rsaKeyFile); err != nil {
+		pub = &rsa2048Key.PublicKey
+		privPKCS8, err = x509.MarshalPKCS8PrivateKey(rsa2048Key)
+		if err != nil {
 			return nil, nil, err
 		}
 
-		pub = &priv.PublicKey
-
 	case signatureECDSAWithSHA1, signatureECDSAWithP256AndSHA256, signatureECDSAWithP384AndSHA384, signatureECDSAWithP521AndSHA512:
 		var curve elliptic.Curve
 		switch dcAlgo {
@@ -412,33 +407,6 @@
 	return dc, privPKCS8, nil
 }
 
-func getRunnerCertificate(t testCert) Certificate {
-	for _, cert := range testCerts {
-		if cert.id == t {
-			return *cert.cert
-		}
-	}
-	panic("Unknown test certificate")
-}
-
-func getShimCertificate(t testCert) string {
-	for _, cert := range testCerts {
-		if cert.id == t {
-			return cert.certFile
-		}
-	}
-	panic("Unknown test certificate")
-}
-
-func getShimKey(t testCert) string {
-	for _, cert := range testCerts {
-		if cert.id == t {
-			return cert.keyFile
-		}
-	}
-	panic("Unknown test certificate")
-}
-
 // recordVersionToWire maps a record-layer protocol version to its wire
 // representation.
 func recordVersionToWire(vers uint16, protocol protocol) uint16 {
@@ -544,7 +512,7 @@
 	curveID CurveID
 	// peerCertificate, if not nil, is the certificate chain the peer is
 	// expected to send.
-	peerCertificate *Certificate
+	peerCertificate *CertificateChain
 	// quicTransportParams contains the QUIC transport parameters that are to be
 	// sent by the peer using codepoint 57.
 	quicTransportParams []byte
@@ -583,10 +551,6 @@
 	messageLen int
 	// messageCount is the number of test messages that will be sent.
 	messageCount int
-	// certFile is the path to the certificate to use for the server.
-	certFile string
-	// keyFile is the path to the private key to use for the server.
-	keyFile string
 	// resumeSession controls whether a second connection should be tested
 	// which attempts to resume the first session.
 	resumeSession bool
@@ -708,6 +672,9 @@
 	// skipVersionNameCheck, if true, will skip the consistency check between
 	// test name and the versions.
 	skipVersionNameCheck bool
+	// shimCertificate, if populated, is the certificate/chain which should be sent
+	// by the server/client (this populates the -cert-file and -key-file flags).
+	shimCertificate *CertificateChain
 }
 
 var testCases []testCase
@@ -764,17 +731,14 @@
 			config.ServerSessionCache = NewLRUServerSessionCache(1)
 		}
 	}
-	if test.testType == clientTest {
-		if len(config.Certificates) == 0 {
-			config.Certificates = []Certificate{rsaCertificate}
-		}
-	} else {
+	if test.testType != clientTest {
 		// Supply a ServerName to ensure a constant session cache key,
 		// rather than falling back to net.Conn.RemoteAddr.
 		if len(config.ServerName) == 0 {
 			config.ServerName = "test"
 		}
 	}
+
 	if *fuzzer {
 		config.Bugs.NullAllCiphers = true
 	}
@@ -1378,6 +1342,9 @@
 		if test.resumeConfig != nil {
 			resumeConfig = *test.resumeConfig
 			resumeConfig.Rand = config.Rand
+			if resumeConfig.Chains == nil {
+				resumeConfig.Chains = config.Chains
+			}
 		} else {
 			resumeConfig = config
 		}
@@ -1447,20 +1414,14 @@
 	}
 	if test.testType == serverTest {
 		flags = append(flags, "-server")
+	}
 
-		flags = append(flags, "-key-file")
-		if test.keyFile == "" {
-			flags = append(flags, path.Join(*resourceDir, rsaKeyFile))
-		} else {
-			flags = append(flags, path.Join(*resourceDir, test.keyFile))
-		}
-
-		flags = append(flags, "-cert-file")
-		if test.certFile == "" {
-			flags = append(flags, path.Join(*resourceDir, rsaCertificateFile))
-		} else {
-			flags = append(flags, path.Join(*resourceDir, test.certFile))
-		}
+	if test.shimCertificate != nil {
+		flags = append(flags, "-key-file", test.shimCertificate.KeyPath)
+		flags = append(flags, "-cert-file", test.shimCertificate.ChainPath)
+	} else if test.testType == serverTest {
+		flags = append(flags, "-key-file", rsaCertificate.KeyPath)
+		flags = append(flags, "-cert-file", rsaCertificate.ChainPath)
 	}
 
 	if test.protocol == dtls {
@@ -1631,6 +1592,19 @@
 		flags = append(flags, "-write-settings", transcriptPrefix)
 	}
 
+	if test.testType == clientTest {
+		if len(test.config.Chains) == 0 {
+			test.config.Chains = []CertificateChain{rsaCertificate}
+		}
+
+		rootFiles := make([]string, 0, len(test.config.Chains))
+		for _, c := range test.config.Chains {
+			rootFiles = append(rootFiles, c.RootPath)
+		}
+
+		flags = append(flags, "-trust-cert", strings.Join(rootFiles, ","))
+	}
+
 	flags = append(flags, test.flags...)
 
 	var env []string
@@ -2099,14 +2073,14 @@
 			testType: serverTest,
 			name:     "ServerSkipCertificateVerify",
 			config: Config{
-				MaxVersion:   VersionTLS12,
-				Certificates: []Certificate{rsaChainCertificate},
+				MaxVersion: VersionTLS12,
+				Chains:     []CertificateChain{rsaCertificate},
 				Bugs: ProtocolBugs{
 					SkipCertificateVerify: true,
 				},
 			},
 			expectations: connectionExpectations{
-				peerCertificate: &rsaChainCertificate,
+				peerCertificate: &rsaCertificate,
 			},
 			flags: []string{
 				"-require-any-client-certificate",
@@ -3598,14 +3572,14 @@
 	testCases = append(testCases, testCase{
 		name: "LargeMessage",
 		config: Config{
-			Certificates: []Certificate{cert},
+			Chains: []CertificateChain{cert},
 		},
 	})
 	testCases = append(testCases, testCase{
 		protocol: dtls,
 		name:     "LargeMessage-DTLS",
 		config: Config{
-			Certificates: []Certificate{cert},
+			Chains: []CertificateChain{cert},
 		},
 	})
 
@@ -3613,7 +3587,7 @@
 	testCases = append(testCases, testCase{
 		name: "LargeMessage-Reject",
 		config: Config{
-			Certificates: []Certificate{cert},
+			Chains: []CertificateChain{cert},
 		},
 		flags:         []string{"-max-cert-list", "16384"},
 		shouldFail:    true,
@@ -3623,7 +3597,7 @@
 		protocol: dtls,
 		name:     "LargeMessage-Reject-DTLS",
 		config: Config{
-			Certificates: []Certificate{cert},
+			Chains: []CertificateChain{cert},
 		},
 		flags:         []string{"-max-cert-list", "16384"},
 		shouldFail:    true,
@@ -3671,17 +3645,11 @@
 	}
 	prefix := protocol.String() + "-"
 
-	var cert Certificate
-	var certFile string
-	var keyFile string
+	var cert CertificateChain
 	if hasComponent(suite.name, "ECDSA") {
 		cert = ecdsaP256Certificate
-		certFile = ecdsaP256CertificateFile
-		keyFile = ecdsaP256KeyFile
 	} else {
 		cert = rsaCertificate
-		certFile = rsaCertificateFile
-		keyFile = rsaKeyFile
 	}
 
 	var flags []string
@@ -3730,15 +3698,14 @@
 			MinVersion:           ver.version,
 			MaxVersion:           ver.version,
 			CipherSuites:         []uint16{suite.id},
-			Certificates:         []Certificate{cert},
+			Chains:               []CertificateChain{cert},
 			PreSharedKey:         []byte(psk),
 			PreSharedKeyIdentity: pskIdentity,
 			Bugs: ProtocolBugs{
 				AdvertiseAllConfiguredCiphers: true,
 			},
 		},
-		certFile:             certFile,
-		keyFile:              keyFile,
+		shimCertificate:      &cert,
 		flags:                flags,
 		resumeSession:        true,
 		shouldFail:           shouldFail,
@@ -3754,7 +3721,7 @@
 			MinVersion:           ver.version,
 			MaxVersion:           ver.version,
 			CipherSuites:         serverCipherSuites,
-			Certificates:         []Certificate{cert},
+			Chains:               []CertificateChain{cert},
 			PreSharedKey:         []byte(psk),
 			PreSharedKeyIdentity: pskIdentity,
 			Bugs: ProtocolBugs{
@@ -3781,7 +3748,7 @@
 			MinVersion:           ver.version,
 			MaxVersion:           ver.version,
 			CipherSuites:         []uint16{suite.id},
-			Certificates:         []Certificate{cert},
+			Chains:               []CertificateChain{cert},
 			PreSharedKey:         []byte(psk),
 			PreSharedKeyIdentity: pskIdentity,
 		},
@@ -3808,7 +3775,7 @@
 				MinVersion:           ver.version,
 				MaxVersion:           ver.version,
 				CipherSuites:         []uint16{suite.id},
-				Certificates:         []Certificate{cert},
+				Chains:               []CertificateChain{cert},
 				PreSharedKey:         []byte(psk),
 				PreSharedKeyIdentity: pskIdentity,
 			},
@@ -3948,7 +3915,7 @@
 		config: Config{
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-			Certificates: []Certificate{rsaCertificate},
+			Chains:       []CertificateChain{rsaCertificate},
 			Bugs: ProtocolBugs{
 				SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
 			},
@@ -3961,7 +3928,7 @@
 		config: Config{
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-			Certificates: []Certificate{ecdsaP256Certificate},
+			Chains:       []CertificateChain{ecdsaP256Certificate},
 			Bugs: ProtocolBugs{
 				SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
 			},
@@ -3974,7 +3941,7 @@
 		config: Config{
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-			Certificates: []Certificate{ed25519Certificate},
+			Chains:       []CertificateChain{ed25519Certificate},
 			Bugs: ProtocolBugs{
 				SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
 			},
@@ -3992,12 +3959,9 @@
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":NO_SHARED_CIPHER:",
+		shimCertificate: &rsaCertificate,
+		shouldFail:      true,
+		expectedError:   ":NO_SHARED_CIPHER:",
 	})
 	testCases = append(testCases, testCase{
 		testType: serverTest,
@@ -4006,12 +3970,9 @@
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-			"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":NO_SHARED_CIPHER:",
+		shimCertificate: &ecdsaP256Certificate,
+		shouldFail:      true,
+		expectedError:   ":NO_SHARED_CIPHER:",
 	})
 	testCases = append(testCases, testCase{
 		testType: serverTest,
@@ -4020,12 +3981,9 @@
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
-			"-key-file", path.Join(*resourceDir, ed25519KeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":NO_SHARED_CIPHER:",
+		shimCertificate: &ed25519Certificate,
+		shouldFail:      true,
+		expectedError:   ":NO_SHARED_CIPHER:",
 	})
 
 	// Test cipher suite negotiation works as expected. Configure a
@@ -4149,7 +4107,7 @@
 				config: Config{
 					MaxVersion:   VersionTLS12,
 					CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-					Certificates: []Certificate{ecdsaP256Certificate},
+					Chains:       []CertificateChain{ecdsaP256Certificate},
 					Bugs: ProtocolBugs{
 						BadECDSAR: badR,
 						BadECDSAS: badS,
@@ -4161,8 +4119,8 @@
 			testCases = append(testCases, testCase{
 				name: fmt.Sprintf("BadECDSA-%d-%d-TLS13", badR, badS),
 				config: Config{
-					MaxVersion:   VersionTLS13,
-					Certificates: []Certificate{ecdsaP256Certificate},
+					MaxVersion: VersionTLS13,
+					Chains:     []CertificateChain{ecdsaP256Certificate},
 					Bugs: ProtocolBugs{
 						BadECDSAR: badR,
 						BadECDSAS: badS,
@@ -4273,7 +4231,7 @@
 func addClientAuthTests() {
 	// Add a dummy cert pool to stress certificate authority parsing.
 	certPool := x509.NewCertPool()
-	for _, cert := range []Certificate{rsaCertificate, rsa1024Certificate} {
+	for _, cert := range []CertificateChain{rsaCertificate, rsa1024Certificate} {
 		cert, err := x509.ParseCertificate(cert.Certificate[0])
 		if err != nil {
 			panic(err)
@@ -4292,18 +4250,15 @@
 				ClientAuth: RequireAnyClientCert,
 				ClientCAs:  certPool,
 			},
-			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
-			},
+			shimCertificate: &rsaCertificate,
 		})
 		testCases = append(testCases, testCase{
 			testType: serverTest,
 			name:     ver.name + "-Server-ClientAuth-RSA",
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{"-require-any-client-certificate"},
 		})
@@ -4311,9 +4266,9 @@
 			testType: serverTest,
 			name:     ver.name + "-Server-ClientAuth-ECDSA",
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{ecdsaP256Certificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{ecdsaP256Certificate},
 			},
 			flags: []string{"-require-any-client-certificate"},
 		})
@@ -4326,10 +4281,7 @@
 				ClientAuth: RequireAnyClientCert,
 				ClientCAs:  certPool,
 			},
-			flags: []string{
-				"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-				"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-			},
+			shimCertificate: &ecdsaP256Certificate,
 		})
 
 		testCases = append(testCases, testCase{
@@ -4431,7 +4383,7 @@
 			config: Config{
 				MinVersion: ver.version,
 				MaxVersion: ver.version,
-				ChannelID:  channelIDKey,
+				ChannelID:  &channelIDKey,
 			},
 			expectations: connectionExpectations{
 				channelID: true,
@@ -4446,9 +4398,9 @@
 			testType: serverTest,
 			name:     ver.name + "-Server-CertReq-CA-List",
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaCertificate},
 				Bugs: ProtocolBugs{
 					ExpectCertificateReqNames: caNames,
 				},
@@ -4463,15 +4415,14 @@
 			testType: clientTest,
 			name:     ver.name + "-Client-CertReq-CA-List",
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaCertificate},
-				ClientAuth:   RequireAnyClientCert,
-				ClientCAs:    certPool,
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaCertificate},
+				ClientAuth: RequireAnyClientCert,
+				ClientCAs:  certPool,
 			},
+			shimCertificate: &rsaCertificate,
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
 				"-expect-client-ca-list", encodeDERValues(caNames),
 			},
 		})
@@ -4487,9 +4438,8 @@
 			PreSharedKey: []byte("secret"),
 			ClientAuth:   RequireAnyClientCert,
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-psk", "secret",
 		},
 		shouldFail:    true,
@@ -4504,9 +4454,8 @@
 			PreSharedKey: []byte("secret"),
 			ClientAuth:   RequireAnyClientCert,
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-psk", "secret",
 		},
 		shouldFail:    true,
@@ -4519,8 +4468,8 @@
 		testType: serverTest,
 		name:     "Null-Client-CA-List",
 		config: Config{
-			MaxVersion:   VersionTLS12,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS12,
+			Chains:     []CertificateChain{rsaCertificate},
 			Bugs: ProtocolBugs{
 				ExpectCertificateReqNames: [][]byte{},
 			},
@@ -4536,8 +4485,8 @@
 		testType: serverTest,
 		name:     "TLS13-Empty-Client-CA-List",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaCertificate},
 			Bugs: ProtocolBugs{
 				ExpectNoCertificateAuthoritiesExtension: true,
 			},
@@ -5051,10 +5000,7 @@
 				MaxVersion: VersionTLS12,
 				ClientAuth: RequireAnyClientCert,
 			},
-			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
-			},
+			shimCertificate: &rsaCertificate,
 		})
 	}
 	tests = append(tests, testCase{
@@ -5064,10 +5010,7 @@
 			MaxVersion: VersionTLS13,
 			ClientAuth: RequireAnyClientCert,
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate: &rsaCertificate,
 	})
 	if config.protocol != quic {
 		tests = append(tests, testCase{
@@ -5077,10 +5020,7 @@
 				MaxVersion: VersionTLS12,
 				ClientAuth: RequireAnyClientCert,
 			},
-			flags: []string{
-				"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-				"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-			},
+			shimCertificate: &ecdsaP256Certificate,
 		})
 	}
 	tests = append(tests, testCase{
@@ -5090,10 +5030,7 @@
 			MaxVersion: VersionTLS13,
 			ClientAuth: RequireAnyClientCert,
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-			"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-		},
+		shimCertificate: &ecdsaP256Certificate,
 	})
 	if config.protocol != quic {
 		tests = append(tests, testCase{
@@ -5123,9 +5060,8 @@
 				MaxVersion: VersionTLS12,
 				ClientAuth: RequireAnyClientCert,
 			},
+			shimCertificate: &rsaCertificate,
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
 				"-use-old-client-cert-callback",
 			},
 		})
@@ -5137,9 +5073,8 @@
 			MaxVersion: VersionTLS13,
 			ClientAuth: RequireAnyClientCert,
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-use-old-client-cert-callback",
 		},
 	})
@@ -5148,8 +5083,8 @@
 			testType: serverTest,
 			name:     "ClientAuth-Server",
 			config: Config{
-				MaxVersion:   VersionTLS12,
-				Certificates: []Certificate{rsaCertificate},
+				MaxVersion: VersionTLS12,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{"-require-any-client-certificate"},
 		})
@@ -5158,8 +5093,8 @@
 		testType: serverTest,
 		name:     "ClientAuth-Server-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaCertificate},
 		},
 		flags: []string{"-require-any-client-certificate"},
 	})
@@ -5173,10 +5108,7 @@
 				MaxVersion:   VersionTLS12,
 				CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
 			},
-			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
-			},
+			shimCertificate: &rsaCertificate,
 		})
 		tests = append(tests, testCase{
 			testType: serverTest,
@@ -5185,10 +5117,7 @@
 				MaxVersion:   VersionTLS12,
 				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 			},
-			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
-			},
+			shimCertificate: &rsaCertificate,
 		})
 		tests = append(tests, testCase{
 			testType: serverTest,
@@ -5197,10 +5126,7 @@
 				MaxVersion:   VersionTLS12,
 				CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
 			},
-			flags: []string{
-				"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-				"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-			},
+			shimCertificate: &ecdsaP256Certificate,
 		})
 		tests = append(tests, testCase{
 			testType: serverTest,
@@ -5209,9 +5135,8 @@
 				MaxVersion:   VersionTLS12,
 				CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
 			},
+			shimCertificate: &ed25519Certificate,
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
-				"-key-file", path.Join(*resourceDir, ed25519KeyFile),
 				"-verify-prefs", strconv.Itoa(int(signatureEd25519)),
 			},
 		})
@@ -5294,8 +5219,8 @@
 			testType: clientTest,
 			name:     "ClientOCSPCallback-Pass-" + vers.name,
 			config: Config{
-				MaxVersion:   vers.version,
-				Certificates: []Certificate{rsaCertificate},
+				MaxVersion: vers.version,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{
 				"-enable-ocsp-stapling",
@@ -5312,8 +5237,8 @@
 			testType: clientTest,
 			name:     "ClientOCSPCallback-Fail-" + vers.name,
 			config: Config{
-				MaxVersion:   vers.version,
-				Certificates: []Certificate{rsaCertificate},
+				MaxVersion: vers.version,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{
 				"-enable-ocsp-stapling",
@@ -5332,8 +5257,8 @@
 			testType: clientTest,
 			name:     "ClientOCSPCallback-FailNoStaple-" + vers.name,
 			config: Config{
-				MaxVersion:   vers.version,
-				Certificates: []Certificate{certNoStaple},
+				MaxVersion: vers.version,
+				Chains:     []CertificateChain{certNoStaple},
 			},
 			flags: []string{
 				"-enable-ocsp-stapling",
@@ -5442,8 +5367,8 @@
 					testType: testType,
 					name:     "CertificateVerificationSucceed" + suffix,
 					config: Config{
-						MaxVersion:   vers.version,
-						Certificates: []Certificate{rsaCertificate},
+						MaxVersion: vers.version,
+						Chains:     []CertificateChain{rsaCertificate},
 					},
 					flags:         append([]string{"-expect-verify-result"}, flags...),
 					resumeSession: true,
@@ -5452,8 +5377,8 @@
 					testType: testType,
 					name:     "CertificateVerificationFail" + suffix,
 					config: Config{
-						MaxVersion:   vers.version,
-						Certificates: []Certificate{rsaCertificate},
+						MaxVersion: vers.version,
+						Chains:     []CertificateChain{rsaCertificate},
 					},
 					flags:              append([]string{"-verify-fail"}, flags...),
 					shouldFail:         true,
@@ -5465,8 +5390,8 @@
 					testType: testType,
 					name:     "CertificateVerificationDoesNotFailOnResume" + suffix,
 					config: Config{
-						MaxVersion:   vers.version,
-						Certificates: []Certificate{rsaCertificate},
+						MaxVersion: vers.version,
+						Chains:     []CertificateChain{rsaCertificate},
 					},
 					flags:         append([]string{"-on-resume-verify-fail"}, flags...),
 					resumeSession: true,
@@ -5476,8 +5401,8 @@
 						testType: testType,
 						name:     "CertificateVerificationFailsOnResume" + suffix,
 						config: Config{
-							MaxVersion:   vers.version,
-							Certificates: []Certificate{rsaCertificate},
+							MaxVersion: vers.version,
+							Chains:     []CertificateChain{rsaCertificate},
 						},
 						flags: append([]string{
 							"-on-resume-verify-fail",
@@ -5492,8 +5417,8 @@
 						testType: testType,
 						name:     "CertificateVerificationPassesOnResume" + suffix,
 						config: Config{
-							MaxVersion:   vers.version,
-							Certificates: []Certificate{rsaCertificate},
+							MaxVersion: vers.version,
+							Chains:     []CertificateChain{rsaCertificate},
 						},
 						flags: append([]string{
 							"-reverify-on-resume",
@@ -5623,8 +5548,8 @@
 			testType: clientTest,
 			name:     "CertificateVerificationSoftFail-" + vers.name,
 			config: Config{
-				MaxVersion:   vers.version,
-				Certificates: []Certificate{rsaCertificate},
+				MaxVersion: vers.version,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{
 				"-verify-fail",
@@ -5836,7 +5761,7 @@
 			name:     "ChannelID-NPN-Server",
 			config: Config{
 				MaxVersion: VersionTLS12,
-				ChannelID:  channelIDKey,
+				ChannelID:  &channelIDKey,
 				NextProtos: []string{"bar"},
 			},
 			flags: []string{
@@ -5890,7 +5815,7 @@
 				name:     "ChannelID-Server-" + ver.name,
 				config: Config{
 					MaxVersion: ver.version,
-					ChannelID:  channelIDKey,
+					ChannelID:  &channelIDKey,
 				},
 				flags: []string{
 					"-expect-channel-id",
@@ -5907,7 +5832,7 @@
 				name:     "InvalidChannelIDSignature-" + ver.name,
 				config: Config{
 					MaxVersion: ver.version,
-					ChannelID:  channelIDKey,
+					ChannelID:  &channelIDKey,
 					Bugs: ProtocolBugs{
 						InvalidChannelIDSignature: true,
 					},
@@ -5925,7 +5850,7 @@
 					config: Config{
 						MaxVersion:   ver.version,
 						CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
-						ChannelID:    channelIDKey,
+						ChannelID:    &channelIDKey,
 					},
 					expectations: connectionExpectations{
 						channelID: false,
@@ -5940,7 +5865,7 @@
 					config: Config{
 						MaxVersion:   ver.version,
 						CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
-						ChannelID:    channelIDKey,
+						ChannelID:    &channelIDKey,
 					},
 					expectations: connectionExpectations{
 						channelID: false,
@@ -8474,7 +8399,7 @@
 				resumeSession: true,
 			})
 
-			emptySCTListCert := *testCerts[0].cert
+			emptySCTListCert := rsaCertificate
 			emptySCTListCert.SignedCertificateTimestampList = []byte{0, 0}
 
 			// Test empty SCT list.
@@ -8483,8 +8408,8 @@
 				name:     "SignedCertificateTimestampListEmpty-Client-" + suffix,
 				testType: clientTest,
 				config: Config{
-					MaxVersion:   ver.version,
-					Certificates: []Certificate{emptySCTListCert},
+					MaxVersion: ver.version,
+					Chains:     []CertificateChain{emptySCTListCert},
 				},
 				flags: []string{
 					"-enable-signed-cert-timestamps",
@@ -8493,7 +8418,7 @@
 				expectedError: ":ERROR_PARSING_EXTENSION:",
 			})
 
-			emptySCTCert := *testCerts[0].cert
+			emptySCTCert := rsaCertificate
 			emptySCTCert.SignedCertificateTimestampList = []byte{0, 6, 0, 2, 1, 2, 0, 0}
 
 			// Test empty SCT in non-empty list.
@@ -8502,8 +8427,8 @@
 				name:     "SignedCertificateTimestampListEmptySCT-Client-" + suffix,
 				testType: clientTest,
 				config: Config{
-					MaxVersion:   ver.version,
-					Certificates: []Certificate{emptySCTCert},
+					MaxVersion: ver.version,
+					Chains:     []CertificateChain{emptySCTCert},
 				},
 				flags: []string{
 					"-enable-signed-cert-timestamps",
@@ -8735,8 +8660,8 @@
 		name:     "SendExtensionOnClientCertificate-TLS13",
 		testType: serverTest,
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaCertificate},
 			Bugs: ProtocolBugs{
 				SendExtensionOnCertificate: testOCSPExtension,
 			},
@@ -8765,8 +8690,8 @@
 	testCases = append(testCases, testCase{
 		name: "IgnoreExtensionsOnIntermediates-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaChainCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaChainCertificate},
 			Bugs: ProtocolBugs{
 				// Send different values on the intermediate. This tests
 				// the intermediate's extensions do not override the
@@ -8797,9 +8722,8 @@
 				ExpectNoExtensionsOnIntermediate: true,
 			},
 		},
+		shimCertificate: &rsaChainCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
 			"-ocsp-response",
 			base64FlagValue(testOCSPResponse),
 			"-signed-cert-timestamps",
@@ -8814,9 +8738,8 @@
 			MaxVersion: VersionTLS13,
 			ClientAuth: RequireAnyClientCert,
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-ocsp-response",
 			base64FlagValue(testOCSPResponse),
 			"-signed-cert-timestamps",
@@ -9829,8 +9752,8 @@
 	testCases = append(testCases, testCase{
 		name: "Renegotiation-CertificateChange",
 		config: Config{
-			MaxVersion:   VersionTLS12,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS12,
+			Chains:     []CertificateChain{rsaCertificate},
 			Bugs: ProtocolBugs{
 				RenegotiationCertificate: &rsaChainCertificate,
 			},
@@ -9843,8 +9766,8 @@
 	testCases = append(testCases, testCase{
 		name: "Renegotiation-CertificateChange-2",
 		config: Config{
-			MaxVersion:   VersionTLS12,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS12,
+			Chains:     []CertificateChain{rsaCertificate},
 			Bugs: ProtocolBugs{
 				RenegotiationCertificate: &rsa1024Certificate,
 			},
@@ -9953,30 +9876,30 @@
 var testSignatureAlgorithms = []struct {
 	name string
 	id   signatureAlgorithm
-	cert testCert
+	cert *CertificateChain
 	// If non-zero, the curve that must be supported in TLS 1.2 for cert to be
 	// accepted.
 	curve CurveID
 }{
-	{"RSA_PKCS1_SHA1", signatureRSAPKCS1WithSHA1, testCertRSA, 0},
-	{"RSA_PKCS1_SHA256", signatureRSAPKCS1WithSHA256, testCertRSA, 0},
-	{"RSA_PKCS1_SHA384", signatureRSAPKCS1WithSHA384, testCertRSA, 0},
-	{"RSA_PKCS1_SHA512", signatureRSAPKCS1WithSHA512, testCertRSA, 0},
-	{"ECDSA_SHA1", signatureECDSAWithSHA1, testCertECDSAP256, CurveP256},
+	{"RSA_PKCS1_SHA1", signatureRSAPKCS1WithSHA1, &rsaCertificate, 0},
+	{"RSA_PKCS1_SHA256", signatureRSAPKCS1WithSHA256, &rsaCertificate, 0},
+	{"RSA_PKCS1_SHA384", signatureRSAPKCS1WithSHA384, &rsaCertificate, 0},
+	{"RSA_PKCS1_SHA512", signatureRSAPKCS1WithSHA512, &rsaCertificate, 0},
+	{"ECDSA_SHA1", signatureECDSAWithSHA1, &ecdsaP256Certificate, CurveP256},
 	// The “P256” in the following line is not a mistake. In TLS 1.2 the
 	// hash function doesn't have to match the curve and so the same
 	// signature algorithm works with P-224.
-	{"ECDSA_P224_SHA256", signatureECDSAWithP256AndSHA256, testCertECDSAP224, CurveP224},
-	{"ECDSA_P256_SHA256", signatureECDSAWithP256AndSHA256, testCertECDSAP256, CurveP256},
-	{"ECDSA_P384_SHA384", signatureECDSAWithP384AndSHA384, testCertECDSAP384, CurveP384},
-	{"ECDSA_P521_SHA512", signatureECDSAWithP521AndSHA512, testCertECDSAP521, CurveP521},
-	{"RSA_PSS_SHA256", signatureRSAPSSWithSHA256, testCertRSA, 0},
-	{"RSA_PSS_SHA384", signatureRSAPSSWithSHA384, testCertRSA, 0},
-	{"RSA_PSS_SHA512", signatureRSAPSSWithSHA512, testCertRSA, 0},
-	{"Ed25519", signatureEd25519, testCertEd25519, 0},
+	{"ECDSA_P224_SHA256", signatureECDSAWithP256AndSHA256, &ecdsaP224Certificate, CurveP224},
+	{"ECDSA_P256_SHA256", signatureECDSAWithP256AndSHA256, &ecdsaP256Certificate, CurveP256},
+	{"ECDSA_P384_SHA384", signatureECDSAWithP384AndSHA384, &ecdsaP384Certificate, CurveP384},
+	{"ECDSA_P521_SHA512", signatureECDSAWithP521AndSHA512, &ecdsaP521Certificate, CurveP521},
+	{"RSA_PSS_SHA256", signatureRSAPSSWithSHA256, &rsaCertificate, 0},
+	{"RSA_PSS_SHA384", signatureRSAPSSWithSHA384, &rsaCertificate, 0},
+	{"RSA_PSS_SHA512", signatureRSAPSSWithSHA512, &rsaCertificate, 0},
+	{"Ed25519", signatureEd25519, &ed25519Certificate, 0},
 	// Tests for key types prior to TLS 1.2.
-	{"RSA", 0, testCertRSA, 0},
-	{"ECDSA", 0, testCertECDSAP256, CurveP256},
+	{"RSA", 0, &rsaCertificate, 0},
+	{"ECDSA", 0, &ecdsaP256Certificate, CurveP256},
 }
 
 const fakeSigAlg1 signatureAlgorithm = 0x2a01
@@ -10019,7 +9942,7 @@
 			}
 			// SHA-224 has been removed from TLS 1.3 and, in 1.3,
 			// the curve has to match the hash size.
-			if ver.version >= VersionTLS13 && alg.cert == testCertECDSAP224 {
+			if ver.version >= VersionTLS13 && alg.curve == CurveP224 {
 				shouldFail = true
 			}
 
@@ -10069,13 +9992,8 @@
 							fakeSigAlg2,
 						},
 					},
-					flags: append(
-						[]string{
-							"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
-							"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
-						},
-						curveFlags...,
-					),
+					shimCertificate:    alg.cert,
+					flags:              curveFlags,
 					shouldFail:         shouldFail,
 					expectedError:      signError,
 					expectedLocalError: signLocalError,
@@ -10093,13 +10011,8 @@
 						MaxVersion:                ver.version,
 						VerifySignatureAlgorithms: allAlgorithms,
 					},
-					flags: append(
-						[]string{
-							"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
-							"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
-						},
-						curveFlags...,
-					),
+					shimCertificate: alg.cert,
+					flags:           curveFlags,
 					expectations: connectionExpectations{
 						peerSignatureAlgorithm: alg.id,
 					},
@@ -10127,8 +10040,8 @@
 					testType: testType,
 					name:     prefix + "Verify" + suffix,
 					config: Config{
-						MaxVersion:   ver.version,
-						Certificates: []Certificate{getRunnerCertificate(alg.cert)},
+						MaxVersion: ver.version,
+						Chains:     []CertificateChain{*alg.cert},
 						SignSignatureAlgorithms: []signatureAlgorithm{
 							alg.id,
 						},
@@ -10158,8 +10071,8 @@
 					testType: testType,
 					name:     prefix + "VerifyDefault" + suffix,
 					config: Config{
-						MaxVersion:   ver.version,
-						Certificates: []Certificate{getRunnerCertificate(alg.cert)},
+						MaxVersion: ver.version,
+						Chains:     []CertificateChain{*alg.cert},
 						SignSignatureAlgorithms: []signatureAlgorithm{
 							alg.id,
 						},
@@ -10187,8 +10100,8 @@
 					testType: testType,
 					name:     prefix + "InvalidSignature" + suffix,
 					config: Config{
-						MaxVersion:   ver.version,
-						Certificates: []Certificate{getRunnerCertificate(alg.cert)},
+						MaxVersion: ver.version,
+						Chains:     []CertificateChain{*alg.cert},
 						SignSignatureAlgorithms: []signatureAlgorithm{
 							alg.id,
 						},
@@ -10240,9 +10153,8 @@
 					signatureECDSAWithP256AndSHA256,
 				},
 			},
+			shimCertificate: &rsaCertificate,
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
 				"-expect-peer-verify-pref", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
 				"-expect-peer-verify-pref", strconv.Itoa(int(signatureEd25519)),
 				"-expect-peer-verify-pref", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
@@ -10260,9 +10172,8 @@
 					signatureECDSAWithP256AndSHA256,
 				},
 			},
+			shimCertificate: &rsaCertificate,
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
 				"-expect-peer-verify-pref", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
 				"-expect-peer-verify-pref", strconv.Itoa(int(signatureEd25519)),
 				"-expect-peer-verify-pref", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
@@ -10283,10 +10194,7 @@
 				signatureECDSAWithSHA1,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate: &rsaCertificate,
 		expectations: connectionExpectations{
 			peerSignatureAlgorithm: signatureRSAPKCS1WithSHA384,
 		},
@@ -10304,10 +10212,7 @@
 				signatureECDSAWithSHA1,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate: &rsaCertificate,
 		expectations: connectionExpectations{
 			peerSignatureAlgorithm: signatureRSAPSSWithSHA384,
 		},
@@ -10352,8 +10257,8 @@
 		testType: serverTest,
 		name:     "Verify-ClientAuth-SignatureType",
 		config: Config{
-			MaxVersion:   VersionTLS12,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS12,
+			Chains:     []CertificateChain{rsaCertificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureRSAPKCS1WithSHA256,
 			},
@@ -10372,8 +10277,8 @@
 		testType: serverTest,
 		name:     "Verify-ClientAuth-SignatureType-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaCertificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureRSAPSSWithSHA256,
 			},
@@ -10433,10 +10338,7 @@
 				NoSignatureAlgorithms: true,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate: &rsaCertificate,
 	})
 
 	testCases = append(testCases, testCase{
@@ -10451,10 +10353,7 @@
 				NoSignatureAlgorithms: true,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-			"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-		},
+		shimCertificate: &ecdsaP256Certificate,
 	})
 
 	testCases = append(testCases, testCase{
@@ -10488,10 +10387,7 @@
 				NoSignatureAlgorithms: true,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate:    &rsaCertificate,
 		shouldFail:         true,
 		expectedError:      ":DECODE_ERROR:",
 		expectedLocalError: "remote error: error decoding message",
@@ -10509,10 +10405,7 @@
 				NoSignatureAlgorithms: true,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-			"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-		},
+		shimCertificate:    &ecdsaP256Certificate,
 		shouldFail:         true,
 		expectedError:      ":DECODE_ERROR:",
 		expectedLocalError: "remote error: error decoding message",
@@ -10530,10 +10423,7 @@
 				NoSignatureAlgorithms: true,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate:    &rsaCertificate,
 		shouldFail:         true,
 		expectedError:      ":DECODE_ERROR:",
 		expectedLocalError: "remote error: error decoding message",
@@ -10545,8 +10435,8 @@
 		testType: serverTest,
 		name:     "ClientAuth-Enforced",
 		config: Config{
-			MaxVersion:   VersionTLS12,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS12,
+			Chains:     []CertificateChain{rsaCertificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureRSAPKCS1WithMD5,
 			},
@@ -10578,8 +10468,8 @@
 		testType: serverTest,
 		name:     "ClientAuth-Enforced-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaCertificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureRSAPKCS1WithMD5,
 			},
@@ -10621,9 +10511,8 @@
 				signatureRSAPKCS1WithSHA1,
 			},
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
 		},
 		shouldFail:    true,
@@ -10639,9 +10528,8 @@
 				signatureRSAPSSWithSHA384,
 			},
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
 		},
 		shouldFail:    true,
@@ -10657,9 +10545,8 @@
 				signatureRSAPKCS1WithSHA256,
 			},
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA1)),
 		},
@@ -10676,9 +10563,8 @@
 				signatureRSAPKCS1WithSHA1,
 			},
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA512)),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA1)),
@@ -10699,10 +10585,7 @@
 				signatureECDSAWithSHA1,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate: &rsaCertificate,
 		expectations: connectionExpectations{
 			peerSignatureAlgorithm: signatureRSAPKCS1WithSHA256,
 		},
@@ -10719,9 +10602,8 @@
 				signatureRSAPKCS1WithSHA256,
 			},
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 			"-signing-prefs", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
 			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
 		},
@@ -10737,7 +10619,7 @@
 		config: Config{
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-			Certificates: []Certificate{ecdsaP256Certificate},
+			Chains:       []CertificateChain{ecdsaP256Certificate},
 		},
 		flags:         []string{"-curves", strconv.Itoa(int(CurveP384))},
 		shouldFail:    true,
@@ -10748,8 +10630,8 @@
 	testCases = append(testCases, testCase{
 		name: "CheckLeafCurve-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{ecdsaP256Certificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{ecdsaP256Certificate},
 		},
 		flags: []string{"-curves", strconv.Itoa(int(CurveP384))},
 	})
@@ -10760,7 +10642,7 @@
 		config: Config{
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-			Certificates: []Certificate{ecdsaP256Certificate},
+			Chains:       []CertificateChain{ecdsaP256Certificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureECDSAWithP384AndSHA384,
 			},
@@ -10771,8 +10653,8 @@
 	testCases = append(testCases, testCase{
 		name: "ECDSACurveMismatch-Verify-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{ecdsaP256Certificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{ecdsaP256Certificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureECDSAWithP384AndSHA384,
 			},
@@ -10796,10 +10678,7 @@
 				signatureECDSAWithP256AndSHA256,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-			"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
-		},
+		shimCertificate: &ecdsaP256Certificate,
 		expectations: connectionExpectations{
 			peerSignatureAlgorithm: signatureECDSAWithP256AndSHA256,
 		},
@@ -10816,12 +10695,9 @@
 				signatureRSAPSSWithSHA512,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsa1024CertificateFile),
-			"-key-file", path.Join(*resourceDir, rsa1024KeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
+		shimCertificate: &rsa1024Certificate,
+		shouldFail:      true,
+		expectedError:   ":NO_COMMON_SIGNATURE_ALGORITHMS:",
 	})
 
 	// Test that RSA-PSS is enabled by default for TLS 1.2.
@@ -10855,8 +10731,8 @@
 		testType: clientTest,
 		name:     "NoEd25519-TLS11-ServerAuth-Verify",
 		config: Config{
-			MaxVersion:   VersionTLS11,
-			Certificates: []Certificate{ed25519Certificate},
+			MaxVersion: VersionTLS11,
+			Chains:     []CertificateChain{ed25519Certificate},
 			Bugs: ProtocolBugs{
 				// Sign with Ed25519 even though it is TLS 1.1.
 				SigningAlgorithmForLegacyVersions: signatureEd25519,
@@ -10872,19 +10748,16 @@
 		config: Config{
 			MaxVersion: VersionTLS11,
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
-			"-key-file", path.Join(*resourceDir, ed25519KeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":NO_SHARED_CIPHER:",
+		shimCertificate: &ed25519Certificate,
+		shouldFail:      true,
+		expectedError:   ":NO_SHARED_CIPHER:",
 	})
 	testCases = append(testCases, testCase{
 		testType: serverTest,
 		name:     "NoEd25519-TLS11-ClientAuth-Verify",
 		config: Config{
-			MaxVersion:   VersionTLS11,
-			Certificates: []Certificate{ed25519Certificate},
+			MaxVersion: VersionTLS11,
+			Chains:     []CertificateChain{ed25519Certificate},
 			Bugs: ProtocolBugs{
 				// Sign with Ed25519 even though it is TLS 1.1.
 				SigningAlgorithmForLegacyVersions: signatureEd25519,
@@ -10904,12 +10777,9 @@
 			MaxVersion: VersionTLS11,
 			ClientAuth: RequireAnyClientCert,
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
-			"-key-file", path.Join(*resourceDir, ed25519KeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
+		shimCertificate: &ed25519Certificate,
+		shouldFail:      true,
+		expectedError:   ":NO_COMMON_SIGNATURE_ALGORITHMS:",
 	})
 
 	// Test Ed25519 is not advertised by default.
@@ -10917,7 +10787,7 @@
 		testType: clientTest,
 		name:     "Ed25519DefaultDisable-NoAdvertise",
 		config: Config{
-			Certificates: []Certificate{ed25519Certificate},
+			Chains: []CertificateChain{ed25519Certificate},
 		},
 		shouldFail:         true,
 		expectedLocalError: "tls: no common signature algorithms",
@@ -10929,7 +10799,7 @@
 		testType: clientTest,
 		name:     "Ed25519DefaultDisable-NoAccept",
 		config: Config{
-			Certificates: []Certificate{ed25519Certificate},
+			Chains: []CertificateChain{ed25519Certificate},
 			Bugs: ProtocolBugs{
 				IgnorePeerSignatureAlgorithmPreferences: true,
 			},
@@ -10944,7 +10814,7 @@
 	testCases = append(testCases, testCase{
 		name: "VerifyPreferences-Advertised",
 		config: Config{
-			Certificates: []Certificate{rsaCertificate},
+			Chains: []CertificateChain{rsaCertificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureRSAPSSWithSHA256,
 				signatureRSAPSSWithSHA384,
@@ -10962,7 +10832,7 @@
 	testCases = append(testCases, testCase{
 		name: "VerifyPreferences-NoCommonAlgorithms",
 		config: Config{
-			Certificates: []Certificate{rsaCertificate},
+			Chains: []CertificateChain{rsaCertificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureRSAPSSWithSHA256,
 				signatureRSAPSSWithSHA512,
@@ -10979,7 +10849,7 @@
 	testCases = append(testCases, testCase{
 		name: "VerifyPreferences-Enforced",
 		config: Config{
-			Certificates: []Certificate{rsaCertificate},
+			Chains: []CertificateChain{rsaCertificate},
 			SignSignatureAlgorithms: []signatureAlgorithm{
 				signatureRSAPSSWithSHA256,
 				signatureRSAPSSWithSHA512,
@@ -11001,7 +10871,7 @@
 	testCases = append(testCases, testCase{
 		name: "VerifyPreferences-Ed25519",
 		config: Config{
-			Certificates: []Certificate{ed25519Certificate},
+			Chains: []CertificateChain{ed25519Certificate},
 		},
 		flags: []string{
 			"-verify-prefs", strconv.Itoa(int(signatureEd25519)),
@@ -11036,9 +10906,8 @@
 					ClientAuth:                RequireAnyClientCert,
 					VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPKCS1WithMD5AndSHA1},
 				},
+				shimCertificate: &rsaCertificate,
 				flags: []string{
-					"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-					"-key-file", path.Join(*resourceDir, rsaKeyFile),
 					"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithMD5AndSHA1)),
 					// Include a valid algorithm as well, to avoid an empty list
 					// if filtered out.
@@ -11054,17 +10923,16 @@
 				testType: testType,
 				name:     prefix + "NoVerify-RSA_PKCS1_MD5_SHA1",
 				config: Config{
-					MaxVersion:   ver.version,
-					Certificates: []Certificate{rsaCertificate},
+					MaxVersion: ver.version,
+					Chains:     []CertificateChain{rsaCertificate},
 					Bugs: ProtocolBugs{
 						IgnorePeerSignatureAlgorithmPreferences: true,
 						AlwaysSignAsLegacyVersion:               true,
 						SendSignatureAlgorithm:                  signatureRSAPKCS1WithMD5AndSHA1,
 					},
 				},
+				shimCertificate: &rsaCertificate,
 				flags: []string{
-					"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-					"-key-file", path.Join(*resourceDir, rsaKeyFile),
 					"-verify-prefs", strconv.Itoa(int(signatureRSAPKCS1WithMD5AndSHA1)),
 					// Include a valid algorithm as well, to avoid an empty list
 					// if filtered out.
@@ -13233,8 +13101,8 @@
 				protocol: protocol,
 				name:     "ClientCertificate" + suffix,
 				config: Config{
-					Certificates: []Certificate{rsaCertificate},
-					MaxVersion:   VersionTLS12,
+					Chains:     []CertificateChain{rsaCertificate},
+					MaxVersion: VersionTLS12,
 				},
 				flags: []string{"-require-any-client-certificate"},
 			},
@@ -13247,8 +13115,8 @@
 				protocol: protocol,
 				name:     "CertificateVerify" + suffix,
 				config: Config{
-					Certificates: []Certificate{rsaCertificate},
-					MaxVersion:   VersionTLS12,
+					Chains:     []CertificateChain{rsaCertificate},
+					MaxVersion: VersionTLS12,
 				},
 				flags: []string{"-require-any-client-certificate"},
 			},
@@ -13289,7 +13157,7 @@
 					name:     "ChannelID" + suffix,
 					config: Config{
 						MaxVersion: VersionTLS12,
-						ChannelID:  channelIDKey,
+						ChannelID:  &channelIDKey,
 					},
 					flags: []string{
 						"-expect-channel-id",
@@ -13423,8 +13291,8 @@
 				protocol: protocol,
 				name:     "TLS13-ClientCertificate" + suffix,
 				config: Config{
-					Certificates: []Certificate{rsaCertificate},
-					MaxVersion:   VersionTLS13,
+					Chains:     []CertificateChain{rsaCertificate},
+					MaxVersion: VersionTLS13,
 				},
 				flags: []string{"-require-any-client-certificate"},
 			},
@@ -13437,8 +13305,8 @@
 				protocol: protocol,
 				name:     "TLS13-ClientCertificateVerify" + suffix,
 				config: Config{
-					Certificates: []Certificate{rsaCertificate},
-					MaxVersion:   VersionTLS13,
+					Chains:     []CertificateChain{rsaCertificate},
+					MaxVersion: VersionTLS13,
 				},
 				flags: []string{"-require-any-client-certificate"},
 			},
@@ -14333,12 +14201,9 @@
 				SendRequestContext: []byte("request context"),
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
+		shimCertificate: &rsaCertificate,
+		shouldFail:      true,
+		expectedError:   ":DECODE_ERROR:",
 	})
 
 	testCases = append(testCases, testCase{
@@ -14351,10 +14216,7 @@
 				SendCustomCertificateRequest: 0x1212,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
+		shimCertificate: &rsaCertificate,
 	})
 
 	testCases = append(testCases, testCase{
@@ -14367,12 +14229,9 @@
 				OmitCertificateRequestAlgorithms: true,
 			},
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
+		shimCertificate: &rsaCertificate,
+		shouldFail:      true,
+		expectedError:   ":DECODE_ERROR:",
 	})
 
 	testCases = append(testCases, testCase{
@@ -14443,12 +14302,12 @@
 		testType: clientTest,
 		name:     "EarlyData-RejectTicket-Client-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaCertificate},
 		},
 		resumeConfig: &Config{
 			MaxVersion:             VersionTLS13,
-			Certificates:           []Certificate{ecdsaP256Certificate},
+			Chains:                 []CertificateChain{ecdsaP256Certificate},
 			SessionTicketsDisabled: true,
 		},
 		resumeSession:           true,
@@ -14459,9 +14318,9 @@
 			"-on-retry-expect-early-data-reason", "session_not_resumed",
 			// Test the peer certificate is reported correctly in each of the
 			// three logical connections.
-			"-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+			"-on-initial-expect-peer-cert-file", rsaCertificate.ChainPath,
+			"-on-resume-expect-peer-cert-file", rsaCertificate.ChainPath,
+			"-on-retry-expect-peer-cert-file", ecdsaP256Certificate.ChainPath,
 			// Session tickets are disabled, so the runner will not send a ticket.
 			"-on-retry-expect-no-session",
 		},
@@ -14538,12 +14397,12 @@
 		testType: clientTest,
 		name:     "EarlyData-HRR-RejectTicket-Client-TLS13",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaCertificate},
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaCertificate},
 		},
 		resumeConfig: &Config{
 			MaxVersion:             VersionTLS13,
-			Certificates:           []Certificate{ecdsaP256Certificate},
+			Chains:                 []CertificateChain{ecdsaP256Certificate},
 			SessionTicketsDisabled: true,
 			Bugs: ProtocolBugs{
 				SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
@@ -14559,9 +14418,9 @@
 			"-on-retry-expect-early-data-reason", "hello_retry_request",
 			// Test the peer certificate is reported correctly in each of the
 			// three logical connections.
-			"-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+			"-on-initial-expect-peer-cert-file", rsaCertificate.ChainPath,
+			"-on-resume-expect-peer-cert-file", rsaCertificate.ChainPath,
+			"-on-retry-expect-peer-cert-file", ecdsaP256Certificate.ChainPath,
 			// Session tickets are disabled, so the runner will not send a ticket.
 			"-on-retry-expect-no-session",
 		},
@@ -15044,7 +14903,7 @@
 		name:     "EarlyDataChannelID-OfferBoth-Server-TLS13",
 		config: Config{
 			MaxVersion: VersionTLS13,
-			ChannelID:  channelIDKey,
+			ChannelID:  &channelIDKey,
 		},
 		resumeSession:           true,
 		earlyData:               true,
@@ -15193,19 +15052,18 @@
 		testType: serverTest,
 		name:     "ServerSkipCertificateVerify-TLS13",
 		config: Config{
-			MinVersion:   VersionTLS13,
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaChainCertificate},
+			MinVersion: VersionTLS13,
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaChainCertificate},
 			Bugs: ProtocolBugs{
 				SkipCertificateVerify: true,
 			},
 		},
 		expectations: connectionExpectations{
-			peerCertificate: &rsaChainCertificate,
+			peerCertificate: &rsaCertificate,
 		},
+		shimCertificate: &rsaCertificate,
 		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
 			"-require-any-client-certificate",
 		},
 		shouldFail:         true,
@@ -15216,20 +15074,17 @@
 		testType: clientTest,
 		name:     "ClientSkipCertificateVerify-TLS13",
 		config: Config{
-			MinVersion:   VersionTLS13,
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaChainCertificate},
+			MinVersion: VersionTLS13,
+			MaxVersion: VersionTLS13,
+			Chains:     []CertificateChain{rsaChainCertificate},
 			Bugs: ProtocolBugs{
 				SkipCertificateVerify: true,
 			},
 		},
 		expectations: connectionExpectations{
-			peerCertificate: &rsaChainCertificate,
+			peerCertificate: &rsaCertificate,
 		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
-		},
+		shimCertificate:    &rsaCertificate,
 		shouldFail:         true,
 		expectedError:      ":UNEXPECTED_MESSAGE:",
 		expectedLocalError: "remote error: unexpected message",
@@ -15585,18 +15440,17 @@
 			testType: clientTest,
 			name:     "SendReceiveIntermediate-Client-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaChainCertificate},
-				ClientAuth:   RequireAnyClientCert,
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaChainCertificate},
+				ClientAuth: RequireAnyClientCert,
 			},
 			expectations: connectionExpectations{
 				peerCertificate: &rsaChainCertificate,
 			},
+			shimCertificate: &rsaChainCertificate,
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
-				"-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+				"-expect-peer-cert-file", rsaChainCertificate.ChainPath,
 			},
 		})
 
@@ -15604,18 +15458,17 @@
 			testType: serverTest,
 			name:     "SendReceiveIntermediate-Server-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaChainCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaChainCertificate},
 			},
 			expectations: connectionExpectations{
 				peerCertificate: &rsaChainCertificate,
 			},
+			shimCertificate: &rsaChainCertificate,
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
 				"-require-any-client-certificate",
-				"-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+				"-expect-peer-cert-file", rsaChainCertificate.ChainPath,
 			},
 		})
 
@@ -15624,9 +15477,9 @@
 			testType: clientTest,
 			name:     "GarbageCertificate-Client-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{garbageCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{garbageCertificate},
 			},
 			shouldFail:         true,
 			expectedError:      ":CANNOT_PARSE_LEAF_CERT:",
@@ -15637,9 +15490,9 @@
 			testType: serverTest,
 			name:     "GarbageCertificate-Server-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{garbageCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{garbageCertificate},
 			},
 			flags:              []string{"-require-any-client-certificate"},
 			shouldFail:         true,
@@ -15674,9 +15527,9 @@
 			testType: serverTest,
 			name:     "RetainOnlySHA256-Cert-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{
 				"-verify-peer",
@@ -15695,9 +15548,9 @@
 			testType: serverTest,
 			name:     "RetainOnlySHA256-OnOff-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{
 				"-verify-peer",
@@ -15715,9 +15568,9 @@
 			testType: serverTest,
 			name:     "RetainOnlySHA256-OffOn-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{rsaCertificate},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{
 				"-verify-peer",
@@ -15731,19 +15584,13 @@
 }
 
 func addECDSAKeyUsageTests() {
-	p256 := elliptic.P256()
-	priv, err := ecdsa.GenerateKey(p256, rand.Reader)
-	if err != nil {
-		panic(err)
-	}
-
 	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
 	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
 	if err != nil {
 		panic(err)
 	}
 
-	template := x509.Certificate{
+	template := &x509.Certificate{
 		SerialNumber: serialNumber,
 		Subject: pkix.Name{
 			Organization: []string{"Acme Co"},
@@ -15758,15 +15605,7 @@
 		BasicConstraintsValid: true,
 	}
 
-	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
-	if err != nil {
-		panic(err)
-	}
-
-	cert := Certificate{
-		Certificate: [][]byte{derBytes},
-		PrivateKey:  priv,
-	}
+	cert := generateSingleCertChain(template, &ecdsaP256Key, nil, nil)
 
 	for _, ver := range tlsVersions {
 		if ver.version < VersionTLS12 {
@@ -15777,9 +15616,9 @@
 			testType: clientTest,
 			name:     "ECDSAKeyUsage-Client-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{cert},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{cert},
 			},
 			shouldFail:    true,
 			expectedError: ":KEY_USAGE_BIT_INCORRECT:",
@@ -15789,9 +15628,9 @@
 			testType: serverTest,
 			name:     "ECDSAKeyUsage-Server-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{cert},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{cert},
 			},
 			flags:         []string{"-require-any-client-certificate"},
 			shouldFail:    true,
@@ -15833,25 +15672,9 @@
 		BasicConstraintsValid: true,
 	}
 
-	dsDerBytes, err := x509.CreateCertificate(rand.Reader, &dsTemplate, &dsTemplate, &priv.PublicKey, priv)
-	if err != nil {
-		panic(err)
-	}
+	dsCert := generateSingleCertChain(&dsTemplate, priv, nil, nil)
 
-	encDerBytes, err := x509.CreateCertificate(rand.Reader, &encTemplate, &encTemplate, &priv.PublicKey, priv)
-	if err != nil {
-		panic(err)
-	}
-
-	dsCert := Certificate{
-		Certificate: [][]byte{dsDerBytes},
-		PrivateKey:  priv,
-	}
-
-	encCert := Certificate{
-		Certificate: [][]byte{encDerBytes},
-		PrivateKey:  priv,
-	}
+	encCert := generateSingleCertChain(&encTemplate, priv, nil, nil)
 
 	dsSuites := []uint16{
 		TLS_AES_128_GCM_SHA256,
@@ -15870,7 +15693,7 @@
 			config: Config{
 				MinVersion:   ver.version,
 				MaxVersion:   ver.version,
-				Certificates: []Certificate{encCert},
+				Chains:       []CertificateChain{encCert},
 				CipherSuites: dsSuites,
 			},
 			shouldFail:    true,
@@ -15883,7 +15706,7 @@
 			config: Config{
 				MinVersion:   ver.version,
 				MaxVersion:   ver.version,
-				Certificates: []Certificate{dsCert},
+				Chains:       []CertificateChain{dsCert},
 				CipherSuites: dsSuites,
 			},
 		})
@@ -15896,7 +15719,7 @@
 				config: Config{
 					MinVersion:   ver.version,
 					MaxVersion:   ver.version,
-					Certificates: []Certificate{encCert},
+					Chains:       []CertificateChain{encCert},
 					CipherSuites: encSuites,
 				},
 			})
@@ -15907,7 +15730,7 @@
 				config: Config{
 					MinVersion:   ver.version,
 					MaxVersion:   ver.version,
-					Certificates: []Certificate{dsCert},
+					Chains:       []CertificateChain{dsCert},
 					CipherSuites: encSuites,
 				},
 				shouldFail:    true,
@@ -15921,7 +15744,7 @@
 				config: Config{
 					MinVersion:   ver.version,
 					MaxVersion:   ver.version,
-					Certificates: []Certificate{dsCert},
+					Chains:       []CertificateChain{dsCert},
 					CipherSuites: encSuites,
 				},
 				flags: []string{"-expect-key-usage-invalid", "-ignore-rsa-key-usage"},
@@ -15933,7 +15756,7 @@
 				config: Config{
 					MinVersion:   ver.version,
 					MaxVersion:   ver.version,
-					Certificates: []Certificate{encCert},
+					Chains:       []CertificateChain{encCert},
 					CipherSuites: dsSuites,
 				},
 				flags: []string{"-expect-key-usage-invalid", "-ignore-rsa-key-usage"},
@@ -15948,7 +15771,7 @@
 				config: Config{
 					MinVersion:   ver.version,
 					MaxVersion:   ver.version,
-					Certificates: []Certificate{encCert},
+					Chains:       []CertificateChain{encCert},
 					CipherSuites: dsSuites,
 				},
 				flags:         []string{"-ignore-rsa-key-usage"},
@@ -15962,9 +15785,9 @@
 			testType: serverTest,
 			name:     "RSAKeyUsage-Server-WantSignature-GotEncipherment-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{encCert},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{encCert},
 			},
 			shouldFail:    true,
 			expectedError: ":KEY_USAGE_BIT_INCORRECT:",
@@ -15975,9 +15798,9 @@
 			testType: serverTest,
 			name:     "RSAKeyUsage-Server-WantSignature-GotSignature-" + ver.name,
 			config: Config{
-				MinVersion:   ver.version,
-				MaxVersion:   ver.version,
-				Certificates: []Certificate{dsCert},
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Chains:     []CertificateChain{dsCert},
 			},
 			flags: []string{"-require-any-client-certificate"},
 		})
@@ -16641,26 +16464,9 @@
 }
 
 func addDelegatedCredentialTests() {
-	certPath := path.Join(*resourceDir, rsaCertificateFile)
-	pemBytes, err := os.ReadFile(certPath)
-	if err != nil {
-		panic(err)
-	}
-
-	block, _ := pem.Decode(pemBytes)
-	if block == nil {
-		panic(fmt.Sprintf("no PEM block found in %q", certPath))
-	}
-	parentDER := block.Bytes
-
-	rsaPriv, _, err := loadRSAPrivateKey(rsaKeyFile)
-	if err != nil {
-		panic(err)
-	}
-
 	ecdsaDC, ecdsaPKCS8, err := createDelegatedCredential(delegatedCredentialConfig{
 		algo: signatureRSAPSSWithSHA256,
-	}, parentDER, rsaPriv)
+	}, rsaCertificate.Leaf.Raw, rsaCertificate.PrivateKey)
 	if err != nil {
 		panic(err)
 	}
@@ -16721,7 +16527,7 @@
 	_, badTLSVersionPKCS8, err := createDelegatedCredential(delegatedCredentialConfig{
 		algo:       signatureRSAPSSWithSHA256,
 		tlsVersion: 0x1234,
-	}, parentDER, rsaPriv)
+	}, rsaCertificate.Leaf.Raw, rsaCertificate.PrivateKey)
 	if err != nil {
 		panic(err)
 	}
@@ -17529,7 +17335,7 @@
 			protocol: protocol,
 			name:     prefix + "ECH-Server-ClientAuth",
 			config: Config{
-				Certificates:    []Certificate{rsaCertificate},
+				Chains:          []CertificateChain{rsaCertificate},
 				ClientECHConfig: echConfig.ECHConfig,
 			},
 			flags: []string{
@@ -17548,7 +17354,7 @@
 			protocol: protocol,
 			name:     prefix + "ECH-Server-Decline-ClientAuth",
 			config: Config{
-				Certificates:    []Certificate{rsaCertificate},
+				Chains:          []CertificateChain{rsaCertificate},
 				ClientECHConfig: echConfig.ECHConfig,
 				Bugs: ProtocolBugs{
 					ExpectECHRetryConfigs: CreateECHConfigList(echConfig1.ECHConfig.Raw),
@@ -18670,9 +18476,8 @@
 					ServerECHConfigs: []ServerECHConfig{echConfig},
 					ClientAuth:       RequireAnyClientCert,
 				},
+				shimCertificate: &rsaCertificate,
 				flags: append([]string{
-					"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-					"-key-file", path.Join(*resourceDir, rsaKeyFile),
 					"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
 					"-expect-ech-accept",
 				}, flags...),
@@ -18690,9 +18495,8 @@
 					MaxVersion: VersionTLS13,
 					ClientAuth: RequireAnyClientCert,
 				},
+				shimCertificate: &rsaCertificate,
 				flags: append([]string{
-					"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-					"-key-file", path.Join(*resourceDir, rsaKeyFile),
 					"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
 				}, flags...),
 				shouldFail:         true,
@@ -18708,9 +18512,8 @@
 						MaxVersion: VersionTLS12,
 						ClientAuth: RequireAnyClientCert,
 					},
+					shimCertificate: &rsaCertificate,
 					flags: append([]string{
-						"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-						"-key-file", path.Join(*resourceDir, rsaKeyFile),
 						"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
 					}, flags...),
 					shouldFail:         true,
@@ -19129,10 +18932,9 @@
 					signatureRSAPSSWithSHA384,
 				},
 			},
+			shimCertificate: &rsaCertificate,
 			flags: []string{
 				"-allow-hint-mismatch",
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
 				"-on-shim-signing-prefs", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
 				"-on-handshaker-signing-prefs", strconv.Itoa(int(signatureRSAPSSWithSHA384)),
 			},
@@ -19154,10 +18956,9 @@
 						signatureRSAPSSWithSHA384,
 					},
 				},
+				shimCertificate: &rsaCertificate,
 				flags: []string{
 					"-allow-hint-mismatch",
-					"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-					"-key-file", path.Join(*resourceDir, rsaKeyFile),
 					"-on-shim-signing-prefs", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
 					"-on-handshaker-signing-prefs", strconv.Itoa(int(signatureRSAPSSWithSHA384)),
 				},
@@ -19243,9 +19044,9 @@
 			protocol:           protocol,
 			skipSplitHandshake: true,
 			config: Config{
-				MinVersion:   VersionTLS13,
-				MaxVersion:   VersionTLS13,
-				Certificates: []Certificate{rsaCertificate},
+				MinVersion: VersionTLS13,
+				MaxVersion: VersionTLS13,
+				Chains:     []CertificateChain{rsaCertificate},
 			},
 			flags: []string{
 				"-allow-hint-mismatch",
@@ -19457,17 +19258,11 @@
 				isWPACipherSuite = true
 			}
 
-			var certFile string
-			var keyFile string
-			var certs []Certificate
+			var cert CertificateChain
 			if hasComponent(suite.name, "ECDSA") {
-				certFile = ecdsaP384CertificateFile
-				keyFile = ecdsaP384KeyFile
-				certs = []Certificate{ecdsaP384Certificate}
+				cert = ecdsaP384Certificate
 			} else {
-				certFile = rsaCertificateFile
-				keyFile = rsaKeyFile
-				certs = []Certificate{rsaCertificate}
+				cert = rsaCertificate
 			}
 
 			maxVersion := uint16(VersionTLS13)
@@ -19496,8 +19291,7 @@
 						MaxVersion:   maxVersion,
 						CipherSuites: []uint16{suite.id},
 					},
-					certFile: certFile,
-					keyFile:  keyFile,
+					shimCertificate: &cert,
 					flags: []string{
 						policy.flag,
 					},
@@ -19512,7 +19306,7 @@
 						MinVersion:   VersionTLS12,
 						MaxVersion:   maxVersion,
 						CipherSuites: []uint16{suite.id},
-						Certificates: certs,
+						Chains:       []CertificateChain{cert},
 					},
 					flags: []string{
 						policy.flag,
@@ -19620,7 +19414,7 @@
 				isWPASigAlg = true
 			}
 
-			if sigalg.cert == testCertECDSAP224 {
+			if sigalg.curve == CurveP224 {
 				// This can work in TLS 1.2, but not with TLS 1.3.
 				// For consistency it's not permitted in FIPS mode.
 				isFIPSSigAlg = false
@@ -19652,12 +19446,9 @@
 						MaxVersion:                maxVersion,
 						VerifySignatureAlgorithms: []signatureAlgorithm{sigalg.id},
 					},
-					flags: []string{
-						policy.flag,
-						"-cert-file", path.Join(*resourceDir, getShimCertificate(sigalg.cert)),
-						"-key-file", path.Join(*resourceDir, getShimKey(sigalg.cert)),
-					},
-					shouldFail: !policy.sigAlgOk,
+					shimCertificate: sigalg.cert,
+					flags:           []string{policy.flag},
+					shouldFail:      !policy.sigAlgOk,
 				})
 
 				testCases = append(testCases, testCase{
@@ -19668,7 +19459,7 @@
 						MinVersion:              VersionTLS12,
 						MaxVersion:              maxVersion,
 						SignSignatureAlgorithms: []signatureAlgorithm{sigalg.id},
-						Certificates:            []Certificate{getRunnerCertificate(sigalg.cert)},
+						Chains:                  []CertificateChain{*sigalg.cert},
 					},
 					flags: []string{
 						policy.flag,
@@ -19857,6 +19648,13 @@
 func main() {
 	flag.Parse()
 	*resourceDir = path.Clean(*resourceDir)
+	var err error
+	if tmpDir, err = os.MkdirTemp("", "testing-certs"); err != nil {
+		fmt.Fprintf(os.Stderr, "failed to make temporary directory: %s", err)
+		os.Exit(1)
+	}
+	defer os.RemoveAll(tmpDir)
+	initKeys()
 	initCertificates()
 
 	if len(*shimConfigFile) != 0 {
diff --git a/ssl/test/runner/tls.go b/ssl/test/runner/tls.go
index 6e57d18..bd94275 100644
--- a/ssl/test/runner/tls.go
+++ b/ssl/test/runner/tls.go
@@ -73,7 +73,7 @@
 // The configuration config must be non-nil and must have
 // at least one certificate.
 func Listen(network, laddr string, config *Config) (net.Listener, error) {
-	if config == nil || len(config.Certificates) == 0 {
+	if config == nil || len(config.Chains) == 0 {
 		return nil, errors.New("tls.Listen: no certificates in configuration")
 	}
 	l, err := net.Listen(network, laddr)
@@ -173,7 +173,7 @@
 
 // LoadX509KeyPair reads and parses a public/private key pair from a pair of
 // files. The files must contain PEM encoded data.
-func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) {
+func LoadX509KeyPair(certFile, keyFile string) (cert CertificateChain, err error) {
 	certPEMBlock, err := os.ReadFile(certFile)
 	if err != nil {
 		return
@@ -187,7 +187,7 @@
 
 // X509KeyPair parses a public/private key pair from a pair of
 // PEM encoded data.
-func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) {
+func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert CertificateChain, err error) {
 	var certDERBlock *pem.Block
 	for {
 		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index c6cbb77..71812c5 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -218,6 +218,7 @@
       IntVectorFlag("-curves", &TestConfig::curves),
       StringFlag("-key-file", &TestConfig::key_file),
       StringFlag("-cert-file", &TestConfig::cert_file),
+      StringFlag("-trust-cert", &TestConfig::trust_cert),
       StringFlag("-expect-server-name", &TestConfig::expect_server_name),
       BoolFlag("-enable-ech-grease", &TestConfig::enable_ech_grease),
       Base64VectorFlag("-ech-server-config", &TestConfig::ech_server_configs),
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index f302ff2..4be2b60 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -40,6 +40,7 @@
   std::vector<uint16_t> curves;
   std::string key_file;
   std::string cert_file;
+  std::string trust_cert;
   std::string expect_server_name;
   bool enable_ech_grease = false;
   std::vector<std::string> ech_server_configs;
diff --git a/util/run_android_tests.go b/util/run_android_tests.go
index 6c793d2..acc5c6b 100644
--- a/util/run_android_tests.go
+++ b/util/run_android_tests.go
@@ -354,23 +354,14 @@
 		binaries = append(binaries, "ssl/test/bssl_shim")
 		files = append(files,
 			"BUILDING.md",
-			"ssl/test/runner/cert.pem",
 			"ssl/test/runner/channel_id_key.pem",
-			"ssl/test/runner/ecdsa_p224_cert.pem",
 			"ssl/test/runner/ecdsa_p224_key.pem",
-			"ssl/test/runner/ecdsa_p256_cert.pem",
 			"ssl/test/runner/ecdsa_p256_key.pem",
-			"ssl/test/runner/ecdsa_p384_cert.pem",
 			"ssl/test/runner/ecdsa_p384_key.pem",
-			"ssl/test/runner/ecdsa_p521_cert.pem",
 			"ssl/test/runner/ecdsa_p521_key.pem",
-			"ssl/test/runner/ed25519_cert.pem",
 			"ssl/test/runner/ed25519_key.pem",
-			"ssl/test/runner/key.pem",
-			"ssl/test/runner/rsa_1024_cert.pem",
+			"ssl/test/runner/rsa_2048_key.pem",
 			"ssl/test/runner/rsa_1024_key.pem",
-			"ssl/test/runner/rsa_chain_cert.pem",
-			"ssl/test/runner/rsa_chain_key.pem",
 			"util/all_tests.json",
 		)