Repository: pion/dtls
Pull request #142 (2019-11-11 02:05:52)

V2 Release! First breaking change certificate chains.
Thank you so much @jkralik for this patch, sorry it took me so long :( but we are going to land this now and get in some other stuff!

-----

Breaking changes:
* certificate, privateKey was replaced
  by certificate (tls.Certificate)
* verifyPeerCertificates uses array of bytes for chain
  certificate instead of certificate(*x509.Certificate)

codecov-io: # [Codecov](https://codecov.io/gh/pion/dtls/pull/142?src=pr&el=h1) Report
> Merging [#142](https://codecov.io/gh/pion/dtls/pull/142?src=pr&el=desc) into [master](https://codecov.io/gh/pion/dtls/commit/1d51cfead39b3f858893953b073eb2146d57716d?src=pr&el=desc) will **decrease** coverage by `0.2%`.
> The diff coverage is `65.59%`.

[![Impacted file tree graph](https://codecov.io/gh/pion/dtls/pull/142/graphs/tree.svg?width=650&token=bcCB2jB88I&height=150&src=pr)](https://codecov.io/gh/pion/dtls/pull/142?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #142      +/-   ##
==========================================
- Coverage   73.61%   73.41%   -0.21%     
==========================================
  Files          54       54              
  Lines        3260     3303      +43     
==========================================
+ Hits         2400     2425      +25     
- Misses        600      610      +10     
- Partials      260      268       +8
```


| [Impacted Files](https://codecov.io/gh/pion/dtls/pull/142?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [state.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-c3RhdGUuZ28=) | `66.66% <ø> (ø)` | :arrow_up: |
| [server\_handlers.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-c2VydmVyX2hhbmRsZXJzLmdv) | `85.39% <100%> (ø)` | :arrow_up: |
| [config.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-Y29uZmlnLmdv) | `100% <100%> (ø)` | :arrow_up: |
| [client\_handlers.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-Y2xpZW50X2hhbmRsZXJzLmdv) | `82.96% <100%> (ø)` | :arrow_up: |
| [conn.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-Y29ubi5nbw==) | `80% <100%> (-0.05%)` | :arrow_down: |
| [crypto.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-Y3J5cHRvLmdv) | `58.2% <50%> (-6.5%)` | :arrow_down: |
| [util.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-dXRpbC5nbw==) | `89.21% <66.66%> (+3.5%)` | :arrow_up: |
| [handshake\_message\_certificate.go](https://codecov.io/gh/pion/dtls/pull/142/diff?src=pr&el=tree#diff-aGFuZHNoYWtlX21lc3NhZ2VfY2VydGlmaWNhdGUuZ28=) | `76.19% <74.19%> (+5.82%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/dtls/pull/142?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pion/dtls/pull/142?src=pr&el=footer). Last update [1d51cfe...b8ff2e3](https://codecov.io/gh/pion/dtls/pull/142?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

daenney: I'm a bit puzzled by something. In a few places I now see this show up:

```go
func xxx(... rawCertificates [][]byte ...) ... {
if len(rawCertificates) == 0 {
		return errLengthMismatch
	}
certificate, err := x509.ParseCertificate(rawCertificates[0])
```

Since it seems we only ever care/check for/validate the first one, what is the idea behind accepting a `[][]byte`? And if `len(rawCertificates) > 1`, what happens to those?
Sean-Der: @jkralik hey (no rush though)! any thoughts on @daenney question, I was just fixing merge conflicts haven't really dove in yet.

@hjames9 mind giving it another look over? I tried to use constants everywhere/simplify stuff as much as I possible.
jkralik: @Sean-Der Sorry for late response but I was very busy. I created PR https://github.com/pion/dtls/pull/152 for this branch to fix issues. And I updated Config  -> Now it is similar as is crypto/tls. But for now it supports only one certificate.

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index e9310e2..0251d3e 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -16,8 +16,6 @@ linters:
 
 issues:
   exclude-use-default: false
-  max-per-linter: 0
-  max-same-issues: 50
 
   exclude-rules:
     - path: internal/crypto/ccm
@@ -28,3 +26,6 @@ issues:
       text: "don't use ALL_CAPS in Go names; use CamelCase"
       linters:
         - golint
+
+run:
+  skip-dirs-use-default: false
diff --git a/README.md b/README.md
index 0d3f629..4603e80 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
 * [Ryan Gordon](https://github.com/ryangordon)
 * [Stefan Tatschner](https://rumpelsepp.org/contact.html)
 * [Hayden James](https://github.com/hjames9)
+* [Jozef Kralik](https://github.com/jkralik)
 
 ### License
 MIT License - see [LICENSE](LICENSE) for full text
diff --git a/client_handlers.go b/client_handlers.go
index 54cb0aa..b702aef 100644
--- a/client_handlers.go
+++ b/client_handlers.go
@@ -328,7 +328,7 @@ func clientFlightHandler(c *Conn) (bool, *alert, error) {
 							messageSequence: uint16(messageSequence),
 						},
 						handshakeMessage: &handshakeMessageCertificate{
-							certificate: c.localCertificate,
+							certificate: c.localCertificate.Certificate,
 						}},
 				},
 			})
@@ -392,7 +392,7 @@ func clientFlightHandler(c *Conn) (bool, *alert, error) {
 		// If the client has sent a certificate with signing ability, a digitally-signed
 		// CertificateVerify message is sent to explicitly verify possession of the
 		// private key in the certificate.
-		if c.remoteRequestedCertificate && c.localCertificate != nil {
+		if c.remoteRequestedCertificate && c.localCertificate.PrivateKey != nil {
 			if len(c.localCertificateVerify) == 0 {
 				plainText := c.handshakeCache.pullAndMerge(
 					handshakeCachePullRule{handshakeTypeClientHello, true},
@@ -405,7 +405,7 @@ func clientFlightHandler(c *Conn) (bool, *alert, error) {
 					handshakeCachePullRule{handshakeTypeClientKeyExchange, true},
 				)
 
-				certVerify, err := generateCertificateVerify(plainText, c.localPrivateKey)
+				certVerify, err := generateCertificateVerify(plainText, c.localCertificate.PrivateKey)
 				if err != nil {
 					return false, &alert{alertLevelFatal, alertInternalError}, err
 				}
diff --git a/config.go b/config.go
index ae6c788..f7792e7 100644
--- a/config.go
+++ b/config.go
@@ -1,9 +1,9 @@
 package dtls
 
 import (
-	"crypto"
 	"crypto/ecdsa"
 	"crypto/ed25519"
+	"crypto/tls"
 	"crypto/x509"
 	"time"
 
@@ -16,11 +16,7 @@ type Config struct {
 	// Certificates contains certificate chain to present to the other side of the connection.
 	// Server MUST set this if PSK is non-nil
 	// client SHOULD sets this so CertificateRequests can be handled if PSK is non-nil
-	Certificate *x509.Certificate
-
-	// PrivateKey contains matching private key for the certificate
-	// only ECDSA is supported
-	PrivateKey crypto.PrivateKey
+	Certificate tls.Certificate
 
 	// CipherSuites is a list of supported cipher suites.
 	// If CipherSuites is nil, a default list is used
@@ -67,7 +63,7 @@ type Config struct {
 	// setting InsecureSkipVerify, or (for a server) when ClientAuth is
 	// RequestClientCert or RequireAnyClientCert, then this callback will
 	// be considered but the verified flag will always be false.
-	VerifyPeerCertificate func(cer *x509.Certificate, verified bool) error
+	VerifyPeerCertificate func(rawCert [][]byte, verified bool) error
 
 	// RootCAs defines the set of root certificate authorities
 	// that one peer uses when verifying the other peer's certificates.
@@ -130,14 +126,14 @@ func validateConfig(config *Config) error {
 	switch {
 	case config == nil:
 		return errNoConfigProvided
-	case config.Certificate != nil && config.PSK != nil:
+	case config.Certificate.PrivateKey != nil && config.PSK != nil:
 		return errPSKAndCertificate
 	case config.PSKIdentityHint != nil && config.PSK == nil:
 		return errIdentityNoPSK
 	}
 
-	if config.PrivateKey != nil {
-		switch config.PrivateKey.(type) {
+	if config.Certificate.PrivateKey != nil {
+		switch config.Certificate.PrivateKey.(type) {
 		case ed25519.PrivateKey:
 		case *ecdsa.PrivateKey:
 		default:
diff --git a/conn.go b/conn.go
index 8bba1e1..97e33f7 100644
--- a/conn.go
+++ b/conn.go
@@ -1,8 +1,8 @@
 package dtls
 
 import (
-	"crypto"
 	"crypto/rand"
+	"crypto/tls"
 	"crypto/x509"
 	"fmt"
 	"math"
@@ -56,8 +56,7 @@ type Conn struct {
 
 	currFlight       *flight
 	namedCurve       namedCurve
-	localCertificate *x509.Certificate
-	localPrivateKey  crypto.PrivateKey
+	localCertificate tls.Certificate
 	localKeypair     *namedCurveKeypair
 	cookie           []byte
 
@@ -70,7 +69,7 @@ type Conn struct {
 	remoteCertificateVerified bool
 
 	insecureSkipVerify    bool
-	verifyPeerCertificate func(cer *x509.Certificate, verified bool) error
+	verifyPeerCertificate func(rawCert [][]byte, verified bool) error
 	rootCAs               *x509.CertPool
 	serverName            string
 
@@ -139,7 +138,6 @@ func createConn(nextConn net.Conn, flightHandler flightHandler, handshakeMessage
 		connectTimeout:              connectTimeout,
 		maximumTransmissionUnit:     mtu,
 		localCertificate:            config.Certificate,
-		localPrivateKey:             config.PrivateKey,
 		clientAuth:                  config.ClientAuth,
 		extendedMasterSecret:        config.ExtendedMasterSecret,
 		insecureSkipVerify:          config.InsecureSkipVerify,
@@ -234,7 +232,7 @@ func Server(conn net.Conn, config *Config) (*Conn, error) {
 	switch {
 	case config == nil:
 		return nil, errNoConfigProvided
-	case config.PSK == nil && config.Certificate == nil:
+	case config.PSK == nil && (config.Certificate.PrivateKey == nil || config.Certificate.Certificate == nil):
 		return nil, errServerMustHaveCertificate
 	}
 
@@ -294,7 +292,7 @@ func (c *Conn) Close() error {
 }
 
 // RemoteCertificate exposes the remote certificate
-func (c *Conn) RemoteCertificate() *x509.Certificate {
+func (c *Conn) RemoteCertificate() [][]byte {
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 	return c.state.remoteCertificate
diff --git a/crypto.go b/crypto.go
index f146d25..fc8ac49 100644
--- a/crypto.go
+++ b/crypto.go
@@ -52,7 +52,15 @@ func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCur
 	return nil, errKeySignatureGenerateUnimplemented
 }
 
-func verifyKeySignature(hash, remoteKeySignature []byte, hashAlgorithm HashAlgorithm, certificate *x509.Certificate) error {
+func verifyKeySignature(hash, remoteKeySignature []byte, hashAlgorithm HashAlgorithm, rawCertificates [][]byte) error {
+	if len(rawCertificates) == 0 {
+		return errLengthMismatch
+	}
+	certificate, err := x509.ParseCertificate(rawCertificates[0])
+	if err != nil {
+		return err
+	}
+
 	switch p := certificate.PublicKey.(type) {
 	case ed25519.PublicKey:
 		if ok := ed25519.Verify(p, hash, remoteKeySignature); !ok {
@@ -109,7 +117,15 @@ func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.Private
 	return nil, errInvalidSignatureAlgorithm
 }
 
-func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm HashAlgorithm, remoteKeySignature []byte, certificate *x509.Certificate) error {
+func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm HashAlgorithm, remoteKeySignature []byte, rawCertificates [][]byte) error {
+	if len(rawCertificates) == 0 {
+		return errLengthMismatch
+	}
+	certificate, err := x509.ParseCertificate(rawCertificates[0])
+	if err != nil {
+		return err
+	}
+
 	hash := hashAlgorithm.digest(handshakeBodies)
 	switch p := certificate.PublicKey.(type) {
 	case ed25519.PublicKey:
@@ -139,27 +155,59 @@ func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm HashAlgorithm
 	return errKeySignatureVerifyUnimplemented
 }
 
-func verifyClientCert(cert *x509.Certificate, roots *x509.CertPool) error {
+func loadCerts(rawCertificates [][]byte) ([]*x509.Certificate, error) {
+	if len(rawCertificates) == 0 {
+		return nil, errLengthMismatch
+	}
+
+	certs := make([]*x509.Certificate, 0, len(rawCertificates))
+	for _, rawCert := range rawCertificates {
+		cert, err := x509.ParseCertificate(rawCert)
+		if err != nil {
+			return nil, err
+		}
+		certs = append(certs, cert)
+	}
+	return certs, nil
+}
+
+func verifyClientCert(rawCertificates [][]byte, roots *x509.CertPool) error {
+	certificate, err := loadCerts(rawCertificates)
+	if err != nil {
+		return err
+	}
+	intermediateCAPool := x509.NewCertPool()
+	for _, cert := range certificate[1:] {
+		intermediateCAPool.AddCert(cert)
+	}
 	opts := x509.VerifyOptions{
 		Roots:         roots,
 		CurrentTime:   time.Now(),
-		Intermediates: x509.NewCertPool(),
+		Intermediates: intermediateCAPool,
 		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
 	}
-	if _, err := cert.Verify(opts); err != nil {
+	if _, err := certificate[0].Verify(opts); err != nil {
 		return err
 	}
 	return nil
 }
 
-func verifyServerCert(cert *x509.Certificate, roots *x509.CertPool, serverName string) error {
+func verifyServerCert(rawCertificates [][]byte, roots *x509.CertPool, serverName string) error {
+	certificate, err := loadCerts(rawCertificates)
+	if err != nil {
+		return err
+	}
+	intermediateCAPool := x509.NewCertPool()
+	for _, cert := range certificate[1:] {
+		intermediateCAPool.AddCert(cert)
+	}
 	opts := x509.VerifyOptions{
 		Roots:         roots,
 		CurrentTime:   time.Now(),
 		DNSName:       serverName,
-		Intermediates: x509.NewCertPool(),
+		Intermediates: intermediateCAPool,
 	}
-	if _, err := cert.Verify(opts); err != nil {
+	if _, err := certificate[0].Verify(opts); err != nil {
 		return err
 	}
 	return nil
diff --git a/crypto_ccm.go b/crypto_ccm.go
index 3facb24..57e3949 100644
--- a/crypto_ccm.go
+++ b/crypto_ccm.go
@@ -6,7 +6,7 @@ import (
 	"encoding/binary"
 	"fmt"
 
-	"github.com/pion/dtls/internal/crypto/ccm"
+	"github.com/pion/dtls/v2/internal/crypto/ccm"
 )
 
 const (
diff --git a/examples/dial-psk/main.go b/examples/dial-psk/main.go
index 9029c46..c194c8c 100644
--- a/examples/dial-psk/main.go
+++ b/examples/dial-psk/main.go
@@ -5,8 +5,8 @@ import (
 	"net"
 	"time"
 
-	"github.com/pion/dtls"
-	"github.com/pion/dtls/examples/util"
+	"github.com/pion/dtls/v2"
+	"github.com/pion/dtls/v2/examples/util"
 )
 
 func main() {
diff --git a/examples/dial/main.go b/examples/dial/main.go
index 3f2a252..80ef7aa 100644
--- a/examples/dial/main.go
+++ b/examples/dial/main.go
@@ -5,8 +5,8 @@ import (
 	"net"
 	"time"
 
-	"github.com/pion/dtls"
-	"github.com/pion/dtls/examples/util"
+	"github.com/pion/dtls/v2"
+	"github.com/pion/dtls/v2/examples/util"
 )
 
 func main() {
@@ -14,7 +14,7 @@ func main() {
 	addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444}
 
 	// Generate a certificate and private key to secure the connection
-	certificate, privateKey, genErr := dtls.GenerateSelfSigned()
+	certificate, genErr := dtls.GenerateSelfSigned()
 	util.Check(genErr)
 
 	//
@@ -24,7 +24,6 @@ func main() {
 	// Prepare the configuration of the DTLS connection
 	config := &dtls.Config{
 		Certificate:          certificate,
-		PrivateKey:           privateKey,
 		InsecureSkipVerify:   true,
 		ExtendedMasterSecret: dtls.RequireExtendedMasterSecret,
 		ConnectTimeout:       dtls.ConnectTimeoutOption(30 * time.Second),
diff --git a/examples/listen-psk/main.go b/examples/listen-psk/main.go
index e1955a8..f5ebc99 100644
--- a/examples/listen-psk/main.go
+++ b/examples/listen-psk/main.go
@@ -5,8 +5,8 @@ import (
 	"net"
 	"time"
 
-	"github.com/pion/dtls"
-	"github.com/pion/dtls/examples/util"
+	"github.com/pion/dtls/v2"
+	"github.com/pion/dtls/v2/examples/util"
 )
 
 func main() {
diff --git a/examples/listen/main.go b/examples/listen/main.go
index ebe2760..ef44bd4 100644
--- a/examples/listen/main.go
+++ b/examples/listen/main.go
@@ -5,8 +5,8 @@ import (
 	"net"
 	"time"
 
-	"github.com/pion/dtls"
-	"github.com/pion/dtls/examples/util"
+	"github.com/pion/dtls/v2"
+	"github.com/pion/dtls/v2/examples/util"
 )
 
 func main() {
@@ -14,7 +14,7 @@ func main() {
 	addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444}
 
 	// Generate a certificate and private key to secure the connection
-	certificate, privateKey, genErr := dtls.GenerateSelfSigned()
+	certificate, genErr := dtls.GenerateSelfSigned()
 	util.Check(genErr)
 
 	//
@@ -24,7 +24,6 @@ func main() {
 	// Prepare the configuration of the DTLS connection
 	config := &dtls.Config{
 		Certificate:          certificate,
-		PrivateKey:           privateKey,
 		ExtendedMasterSecret: dtls.RequireExtendedMasterSecret,
 		ConnectTimeout:       dtls.ConnectTimeoutOption(30 * time.Second),
 	}
diff --git a/go.mod b/go.mod
index 7aba479..9147424 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/pion/dtls
+module github.com/pion/dtls/v2
 
 require (
 	github.com/pion/logging v0.2.2
diff --git a/handshake_message_certificate.go b/handshake_message_certificate.go
index 5303d00..c968cfe 100644
--- a/handshake_message_certificate.go
+++ b/handshake_message_certificate.go
@@ -1,52 +1,55 @@
 package dtls
 
-import (
-	"crypto/x509"
-)
-
 type handshakeMessageCertificate struct {
-	certificate *x509.Certificate
+	certificate [][]byte
 }
 
 func (h handshakeMessageCertificate) handshakeType() handshakeType {
 	return handshakeTypeCertificate
 }
 
+const (
+	handshakeMessageCertificateLengthFieldSize = 3
+)
+
 func (h *handshakeMessageCertificate) Marshal() ([]byte, error) {
-	var raw []byte
-	if h.certificate != nil {
-		raw = h.certificate.Raw
-	}
+	out := make([]byte, handshakeMessageCertificateLengthFieldSize)
+
+	for _, r := range h.certificate {
+		// Certificate Length
+		out = append(out, make([]byte, handshakeMessageCertificateLengthFieldSize)...)
+		putBigEndianUint24(out[len(out)-handshakeMessageCertificateLengthFieldSize:], uint32(len(r)))
 
-	out := make([]byte, 6)
-	putBigEndianUint24(out, uint32(len(raw))+3)
-	putBigEndianUint24(out[3:], uint32(len(raw)))
+		// Certificate body
+		out = append(out, append([]byte{}, r...)...)
+	}
 
-	return append(out, raw...), nil
+	// Total Payload Size
+	putBigEndianUint24(out[0:], uint32(len(out[handshakeMessageCertificateLengthFieldSize:])))
+	return out, nil
 }
 
 func (h *handshakeMessageCertificate) Unmarshal(data []byte) error {
-	if len(data) < 3 {
+	if len(data) < handshakeMessageCertificateLengthFieldSize {
 		return errBufferTooSmall
 	}
 
-	certificateBodyLen := int(bigEndianUint24(data))
-	certificateLen := int(bigEndianUint24(data[3:]))
-	if certificateLen == 0 {
-		return nil
-	}
-	if certificateBodyLen+3 != len(data) {
-		return errLengthMismatch
-	} else if certificateLen+6 != len(data) {
+	if certificateBodyLen := int(bigEndianUint24(data)); certificateBodyLen+handshakeMessageCertificateLengthFieldSize != len(data) {
 		return errLengthMismatch
 	}
 
-	if len(data) > 6 {
-		cert, err := x509.ParseCertificate(data[6:])
-		if err != nil {
-			return err
+	offset := handshakeMessageCertificateLengthFieldSize
+	for offset < len(data) {
+		certificateLen := int(bigEndianUint24(data[offset:]))
+		offset += handshakeMessageCertificateLengthFieldSize
+
+		if offset+certificateLen > len(data) {
+			return errLengthMismatch
 		}
-		h.certificate = cert
+
+		h.certificate = append(h.certificate, append([]byte{}, data[offset:offset+certificateLen]...))
+		offset += certificateLen
 	}
+
 	return nil
 }
diff --git a/listener.go b/listener.go
index 365fc53..c64a66f 100644
--- a/listener.go
+++ b/listener.go
@@ -4,7 +4,7 @@ import (
 	"net"
 	"time"
 
-	"github.com/pion/dtls/internal/udp"
+	"github.com/pion/dtls/v2/internal/udp"
 )
 
 // Listen creates a DTLS listener
diff --git a/server_handlers.go b/server_handlers.go
index e638034..3043f72 100644
--- a/server_handlers.go
+++ b/server_handlers.go
@@ -349,7 +349,7 @@ func serverFlightHandler(c *Conn) (bool, *alert, error) {
 							messageSequence: uint16(messageSequence),
 						},
 						handshakeMessage: &handshakeMessageCertificate{
-							certificate: c.localCertificate,
+							certificate: c.localCertificate.Certificate,
 						}},
 				},
 			})
@@ -365,7 +365,7 @@ func serverFlightHandler(c *Conn) (bool, *alert, error) {
 					return false, &alert{alertLevelFatal, alertInternalError}, err
 				}
 
-				signature, err := generateKeySignature(clientRandom, serverRandom, c.localKeypair.publicKey, c.namedCurve, c.localPrivateKey, HashAlgorithmSHA256)
+				signature, err := generateKeySignature(clientRandom, serverRandom, c.localKeypair.publicKey, c.namedCurve, c.localCertificate.PrivateKey, HashAlgorithmSHA256)
 				if err != nil {
 					return false, &alert{alertLevelFatal, alertInternalError}, err
 				}
diff --git a/state.go b/state.go
index 822d078..4f1b586 100644
--- a/state.go
+++ b/state.go
@@ -2,7 +2,6 @@ package dtls
 
 import (
 	"bytes"
-	"crypto/x509"
 	"encoding/gob"
 	"sync/atomic"
 )
@@ -16,7 +15,7 @@ type State struct {
 	cipherSuite               cipherSuite // nil if a cipherSuite hasn't been chosen
 
 	srtpProtectionProfile SRTPProtectionProfile // Negotiated SRTPProtectionProfile
-	remoteCertificate     *x509.Certificate
+	remoteCertificate     [][]byte
 
 	isClient bool
 
diff --git a/util.go b/util.go
index 3d9d3cf..fb4b3d3 100644
--- a/util.go
+++ b/util.go
@@ -6,6 +6,7 @@ import (
 	"crypto/ed25519"
 	"crypto/elliptic"
 	"crypto/rand"
+	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"encoding/binary"
@@ -38,21 +39,17 @@ func putBigEndianUint48(out []byte, in uint64) {
 }
 
 // GenerateSelfSigned creates a self-signed certificate
-func GenerateSelfSigned() (*x509.Certificate, crypto.PrivateKey, error) {
-	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+func GenerateSelfSigned() (tls.Certificate, error) {
+	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 	if err != nil {
-		return nil, nil, err
+		return tls.Certificate{}, err
 	}
 
-	cert, err := SelfSign(key)
-	if err != nil {
-		return nil, nil, err
-	}
-	return cert, key, nil
+	return SelfSign(priv)
 }
 
 // SelfSign creates a self-signed certificate from a elliptic curve key
-func SelfSign(key crypto.PrivateKey) (*x509.Certificate, error) {
+func SelfSign(key crypto.PrivateKey) (tls.Certificate, error) {
 	var (
 		pubKey    crypto.PublicKey
 		origin    = make([]byte, 16)
@@ -65,7 +62,7 @@ func SelfSign(key crypto.PrivateKey) (*x509.Certificate, error) {
 	case *ecdsa.PrivateKey:
 		pubKey = k.Public()
 	default:
-		return nil, errInvalidPrivateKey
+		return tls.Certificate{}, errInvalidPrivateKey
 	}
 
 	/* #nosec */
@@ -73,7 +70,7 @@ func SelfSign(key crypto.PrivateKey) (*x509.Certificate, error) {
 	/* #nosec */
 	serialNumber, err := rand.Int(rand.Reader, maxBigInt)
 	if err != nil {
-		return nil, err
+		return tls.Certificate{}, err
 	}
 
 	template := x509.Certificate{
@@ -93,15 +90,13 @@ func SelfSign(key crypto.PrivateKey) (*x509.Certificate, error) {
 
 	raw, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey, key)
 	if err != nil {
-		return nil, err
-	}
-
-	cert, err := x509.ParseCertificate(raw)
-	if err != nil {
-		return nil, err
+		return tls.Certificate{}, err
 	}
 
-	return cert, nil
+	return tls.Certificate{
+		Certificate: [][]byte{raw},
+		PrivateKey:  key,
+	}, nil
 }
 
 func max(a, b int) int {


Test patch:
diff --git a/bench_test.go b/bench_test.go
index 878544f..abb671b 100644
--- a/bench_test.go
+++ b/bench_test.go
@@ -15,7 +15,7 @@ func TestSimpleReadWrite(t *testing.T) {
 	defer report()
 
 	ca, cb := net.Pipe()
-	certificate, privateKey, err := GenerateSelfSigned()
+	certificate, err := GenerateSelfSigned()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -24,7 +24,6 @@ func TestSimpleReadWrite(t *testing.T) {
 	go func() {
 		server, sErr := testServer(cb, &Config{
 			Certificate:   certificate,
-			PrivateKey:    privateKey,
 			LoggerFactory: logging.NewDefaultLoggerFactory(),
 		}, false)
 		if sErr != nil {
@@ -65,12 +64,11 @@ func TestSimpleReadWrite(t *testing.T) {
 func benchmarkConn(b *testing.B, n int64) {
 	b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
 		ca, cb := net.Pipe()
-		certificate, privateKey, err := GenerateSelfSigned()
+		certificate, err := GenerateSelfSigned()
 		server := make(chan *Conn)
 		go func() {
 			s, sErr := testServer(cb, &Config{
 				Certificate: certificate,
-				PrivateKey:  privateKey,
 			}, false)
 			if err != nil {
 				b.Error(sErr)
diff --git a/config_test.go b/config_test.go
index f23e579..757c836 100644
--- a/config_test.go
+++ b/config_test.go
@@ -1,6 +1,7 @@
 package dtls
 
 import (
+	"crypto/tls"
 	"crypto/x509"
 	"encoding/pem"
 	"testing"
@@ -13,7 +14,7 @@ func TestValidateConfig(t *testing.T) {
 	}
 
 	//PSK and Certificate
-	cert, key, err := GenerateSelfSigned()
+	cert, err := GenerateSelfSigned()
 	if err != nil {
 		t.Fatalf("TestValidateConfig: Config validation error(%v), self signed certificate not generated", err)
 		return
@@ -47,7 +48,7 @@ func TestValidateConfig(t *testing.T) {
 	}
 	config = &Config{
 		CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-		PrivateKey:   rsaKey,
+		Certificate:  tls.Certificate{PrivateKey: rsaKey},
 	}
 	if err = validateConfig(config); err != errInvalidPrivateKey {
 		t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", errInvalidPrivateKey, err)
@@ -63,7 +64,6 @@ func TestValidateConfig(t *testing.T) {
 	config = &Config{
 		CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
 		Certificate:  cert,
-		PrivateKey:   key,
 	}
 	if err = validateConfig(config); err != nil {
 		t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", nil, err)
diff --git a/conn_test.go b/conn_test.go
index 5f9795f..24efc28 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -106,11 +106,10 @@ func pipeMemory() (*Conn, *Conn, error) {
 
 func testClient(c net.Conn, cfg *Config, generateCertificate bool) (*Conn, error) {
 	if generateCertificate {
-		clientCert, clientKey, err := GenerateSelfSigned()
+		clientCert, err := GenerateSelfSigned()
 		if err != nil {
 			return nil, err
 		}
-		cfg.PrivateKey = clientKey
 		cfg.Certificate = clientCert
 	}
 	cfg.InsecureSkipVerify = true
@@ -119,11 +118,10 @@ func testClient(c net.Conn, cfg *Config, generateCertificate bool) (*Conn, error
 
 func testServer(c net.Conn, cfg *Config, generateCertificate bool) (*Conn, error) {
 	if generateCertificate {
-		serverCert, serverKey, err := GenerateSelfSigned()
+		serverCert, err := GenerateSelfSigned()
 		if err != nil {
 			return nil, err
 		}
-		cfg.PrivateKey = serverKey
 		cfg.Certificate = serverCert
 	}
 	return Server(c, cfg)
@@ -417,19 +415,27 @@ func TestSRTPConfiguration(t *testing.T) {
 }
 
 func TestClientCertificate(t *testing.T) {
-	srvCert, srvKey, err := GenerateSelfSigned()
+	srvCert, err := GenerateSelfSigned()
 	if err != nil {
 		t.Fatal(err)
 	}
 	srvCAPool := x509.NewCertPool()
-	srvCAPool.AddCert(srvCert)
+	srvCertificate, err := x509.ParseCertificate(srvCert.Certificate[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+	srvCAPool.AddCert(srvCertificate)
 
-	cert, key, err := GenerateSelfSigned()
+	cert, err := GenerateSelfSigned()
+	if err != nil {
+		t.Fatal(err)
+	}
+	certificate, err := x509.ParseCertificate(cert.Certificate[0])
 	if err != nil {
 		t.Fatal(err)
 	}
 	caPool := x509.NewCertPool()
-	caPool.AddCert(cert)
+	caPool.AddCert(certificate)
 
 	t.Parallel()
 	tests := map[string]struct {
@@ -441,24 +447,21 @@ func TestClientCertificate(t *testing.T) {
 			clientCfg: &Config{RootCAs: srvCAPool},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  NoClientCert,
 				RootCAs:     caPool,
 			},
 		},
 		"NoClientCert_cert": {
-			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert, PrivateKey: key},
+			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  RequireAnyClientCert,
 			},
 		},
 		"RequestClientCert_cert": {
-			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert, PrivateKey: key},
+			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  RequestClientCert,
 			},
 		},
@@ -466,16 +469,14 @@ func TestClientCertificate(t *testing.T) {
 			clientCfg: &Config{RootCAs: srvCAPool},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  RequestClientCert,
 				RootCAs:     caPool,
 			},
 		},
 		"RequireAnyClientCert": {
-			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert, PrivateKey: key},
+			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  RequireAnyClientCert,
 			},
 		},
@@ -483,7 +484,6 @@ func TestClientCertificate(t *testing.T) {
 			clientCfg: &Config{RootCAs: srvCAPool},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  RequireAnyClientCert,
 			},
 			wantErr: true,
@@ -492,34 +492,30 @@ func TestClientCertificate(t *testing.T) {
 			clientCfg: &Config{RootCAs: srvCAPool},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  VerifyClientCertIfGiven,
 				RootCAs:     caPool,
 			},
 		},
 		"VerifyClientCertIfGiven_cert": {
-			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert, PrivateKey: key},
+			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  VerifyClientCertIfGiven,
 				RootCAs:     caPool,
 			},
 		},
 		"VerifyClientCertIfGiven_error": {
-			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert, PrivateKey: key},
+			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  VerifyClientCertIfGiven,
 			},
 			wantErr: true,
 		},
 		"RequireAndVerifyClientCert": {
-			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert, PrivateKey: key},
+			clientCfg: &Config{RootCAs: srvCAPool, Certificate: cert},
 			serverCfg: &Config{
 				Certificate: srvCert,
-				PrivateKey:  srvKey,
 				ClientAuth:  RequireAndVerifyClientCert,
 				RootCAs:     caPool,
 			},
@@ -563,7 +559,7 @@ func TestClientCertificate(t *testing.T) {
 					t.Errorf("TestClientCertificate: Client did not provide a certificate")
 				}
 
-				if !actualClientCert.Equal(tt.clientCfg.Certificate) {
+				if len(actualClientCert) != len(tt.clientCfg.Certificate.Certificate) || !bytes.Equal(tt.clientCfg.Certificate.Certificate[0], actualClientCert[0]) {
 					t.Errorf("TestClientCertificate: Client certificate was not communicated correctly")
 				}
 			}
@@ -578,7 +574,7 @@ func TestClientCertificate(t *testing.T) {
 				t.Errorf("TestClientCertificate: Server did not provide a certificate")
 			}
 
-			if !actualServerCert.Equal(tt.serverCfg.Certificate) {
+			if len(actualServerCert) != len(tt.serverCfg.Certificate.Certificate) || !bytes.Equal(tt.serverCfg.Certificate.Certificate[0], actualServerCert[0]) {
 				t.Errorf("TestClientCertificate: Server certificate was not communicated correctly")
 			}
 		})
@@ -721,12 +717,16 @@ func TestExtendedMasterSecret(t *testing.T) {
 func TestServerCertificate(t *testing.T) {
 	t.Parallel()
 
-	cert, key, err := GenerateSelfSigned()
+	cert, err := GenerateSelfSigned()
+	if err != nil {
+		t.Fatal(err)
+	}
+	certificate, err := x509.ParseCertificate(cert.Certificate[0])
 	if err != nil {
 		t.Fatal(err)
 	}
 	caPool := x509.NewCertPool()
-	caPool.AddCert(cert)
+	caPool.AddCert(certificate)
 
 	tests := map[string]struct {
 		clientCfg *Config
@@ -735,34 +735,34 @@ func TestServerCertificate(t *testing.T) {
 	}{
 		"no_ca": {
 			clientCfg: &Config{},
-			serverCfg: &Config{Certificate: cert, PrivateKey: key, ClientAuth: NoClientCert},
+			serverCfg: &Config{Certificate: cert, ClientAuth: NoClientCert},
 			wantErr:   true,
 		},
 		"good_ca": {
 			clientCfg: &Config{RootCAs: caPool},
-			serverCfg: &Config{Certificate: cert, PrivateKey: key, ClientAuth: NoClientCert},
+			serverCfg: &Config{Certificate: cert, ClientAuth: NoClientCert},
 		},
 		"no_ca_skip_verify": {
 			clientCfg: &Config{InsecureSkipVerify: true},
-			serverCfg: &Config{Certificate: cert, PrivateKey: key, ClientAuth: NoClientCert},
+			serverCfg: &Config{Certificate: cert, ClientAuth: NoClientCert},
 		},
 		"good_ca_custom_verify_peer": {
 			clientCfg: &Config{
 				RootCAs: caPool,
-				VerifyPeerCertificate: func(*x509.Certificate, bool) error {
+				VerifyPeerCertificate: func([][]byte, bool) error {
 					return errors.New("wrong cert")
 				},
 			},
-			serverCfg: &Config{Certificate: cert, PrivateKey: key, ClientAuth: NoClientCert},
+			serverCfg: &Config{Certificate: cert, ClientAuth: NoClientCert},
 			wantErr:   true,
 		},
 		"server_name": {
-			clientCfg: &Config{RootCAs: caPool, ServerName: cert.Subject.CommonName},
-			serverCfg: &Config{Certificate: cert, PrivateKey: key, ClientAuth: NoClientCert},
+			clientCfg: &Config{RootCAs: caPool, ServerName: certificate.Subject.CommonName},
+			serverCfg: &Config{Certificate: cert, ClientAuth: NoClientCert},
 		},
 		"server_name_error": {
 			clientCfg: &Config{RootCAs: caPool, ServerName: "barfoo"},
-			serverCfg: &Config{Certificate: cert, PrivateKey: key, ClientAuth: NoClientCert},
+			serverCfg: &Config{Certificate: cert, ClientAuth: NoClientCert},
 			wantErr:   true,
 		},
 	}
diff --git a/e2e/e2e_lossy_test.go b/e2e/e2e_lossy_test.go
index e00a3af..e2992be 100644
--- a/e2e/e2e_lossy_test.go
+++ b/e2e/e2e_lossy_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/pion/dtls"
+	"github.com/pion/dtls/v2"
 	transportTest "github.com/pion/transport/test"
 )
 
@@ -23,12 +23,12 @@ func TestPionE2ELossy(t *testing.T) {
 		err      error
 	}
 
-	serverCert, serverKey, err := dtls.GenerateSelfSigned()
+	serverCert, err := dtls.GenerateSelfSigned()
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	clientCert, clientKey, err := dtls.GenerateSelfSigned()
+	clientCert, err := dtls.GenerateSelfSigned()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -119,7 +119,6 @@ func TestPionE2ELossy(t *testing.T) {
 
 			if test.DoClientAuth {
 				cfg.Certificate = clientCert
-				cfg.PrivateKey = clientKey
 			}
 
 			client, startupErr := dtls.Client(br.GetConn0(), cfg)
@@ -129,7 +128,6 @@ func TestPionE2ELossy(t *testing.T) {
 		go func() {
 			cfg := &dtls.Config{
 				Certificate:    serverCert,
-				PrivateKey:     serverKey,
 				FlightInterval: flightInterval,
 				MTU:            test.MTU,
 			}
diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go
index 6429107..5a4138b 100644
--- a/e2e/e2e_test.go
+++ b/e2e/e2e_test.go
@@ -11,7 +11,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/pion/dtls"
+	"github.com/pion/dtls/v2"
 	"github.com/pion/transport/test"
 )
 
@@ -194,14 +194,13 @@ func TestPionE2ESimple(t *testing.T) {
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
 	} {
-		cert, key, err := dtls.GenerateSelfSigned()
+		cert, err := dtls.GenerateSelfSigned()
 		if err != nil {
 			t.Fatal(err)
 		}
 
 		cfg := &dtls.Config{
 			Certificate:        cert,
-			PrivateKey:         key,
 			CipherSuites:       []dtls.CipherSuiteID{cipherSuite},
 			InsecureSkipVerify: true,
 			ConnectTimeout:     dtls.ConnectTimeoutOption(2 * time.Second),
@@ -235,7 +234,6 @@ func TestPionE2ESimpleED25519(t *testing.T) {
 
 		cfg := &dtls.Config{
 			Certificate:        cert,
-			PrivateKey:         key,
 			CipherSuites:       []dtls.CipherSuiteID{cipherSuite},
 			InsecureSkipVerify: true,
 			ConnectTimeout:     dtls.ConnectTimeoutOption(5 * time.Second),
@@ -283,14 +281,13 @@ func TestPionE2EMTUs(t *testing.T) {
 		1000,
 		100,
 	} {
-		cert, key, err := dtls.GenerateSelfSigned()
+		cert, err := dtls.GenerateSelfSigned()
 		if err != nil {
 			t.Fatal(err)
 		}
 
 		cfg := &dtls.Config{
 			Certificate:        cert,
-			PrivateKey:         key,
 			CipherSuites:       []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
 			InsecureSkipVerify: true,
 			MTU:                mtu,
diff --git a/handshake_message_certificate_test.go b/handshake_message_certificate_test.go
index c0017ae..6002103 100644
--- a/handshake_message_certificate_test.go
+++ b/handshake_message_certificate_test.go
@@ -43,26 +43,29 @@ func TestHandshakeMessageCertificate(t *testing.T) {
 		0x80, 0xa4, 0xd4, 0xb7, 0x7f, 0x7d, 0x78, 0xb3, 0xfb, 0xf3, 0x95, 0xfb, 0x02, 0x21, 0x00, 0xc0,
 		0x73, 0x30, 0xda, 0x2b, 0xc0, 0x0c, 0x9e, 0xb2, 0x25, 0x0d, 0x46, 0xb0, 0xbc, 0x66, 0x7f, 0x71,
 		0x66, 0xbf, 0x16, 0xb3, 0x80, 0x78, 0xd0, 0x0c, 0xef, 0xcc, 0xf5, 0xc1, 0x15, 0x0f, 0x58}
-	parsedCertificate := &handshakeMessageCertificate{
-		certificate: &x509.Certificate{
-			Raw:                     rawCertificate[6:],
-			RawTBSCertificate:       rawCertificate[10:313],
-			RawSubjectPublicKeyInfo: rawCertificate[222:313],
-			RawSubject:              rawCertificate[48:119],
-			RawIssuer:               rawCertificate[48:119],
-			Signature:               rawCertificate[328:],
-			SignatureAlgorithm:      x509.ECDSAWithSHA256,
-			PublicKeyAlgorithm:      x509.ECDSA,
-			Version:                 1,
-		},
+
+	parsedCertificate := &x509.Certificate{
+		Raw:                     rawCertificate[6:],
+		RawTBSCertificate:       rawCertificate[10:313],
+		RawSubjectPublicKeyInfo: rawCertificate[222:313],
+		RawSubject:              rawCertificate[48:119],
+		RawIssuer:               rawCertificate[48:119],
+		Signature:               rawCertificate[328:],
+		SignatureAlgorithm:      x509.ECDSAWithSHA256,
+		PublicKeyAlgorithm:      x509.ECDSA,
+		Version:                 1,
 	}
 
 	c := &handshakeMessageCertificate{}
 	if err := c.Unmarshal(rawCertificate); err != nil {
 		t.Error(err)
 	} else {
-		copyCertificatePrivateMembers(c.certificate, parsedCertificate.certificate)
-		if !reflect.DeepEqual(c, parsedCertificate) {
+		certificate, err := x509.ParseCertificate(c.certificate[0])
+		if err != nil {
+			t.Error(err)
+		}
+		copyCertificatePrivateMembers(certificate, parsedCertificate)
+		if !reflect.DeepEqual(certificate, parsedCertificate) {
 			t.Errorf("handshakeMessageCertificate unmarshal: got %#v, want %#v", c, parsedCertificate)
 		}
 	}
diff --git a/resume_test.go b/resume_test.go
index c9b2385..94e8a19 100644
--- a/resume_test.go
+++ b/resume_test.go
@@ -23,7 +23,7 @@ func fatal(t *testing.T, errChan chan error, err error) {
 }
 
 func DoTestResume(t *testing.T, newLocal, newRemote func(net.Conn, *Config) (*Conn, error)) {
-	certificate, privateKey, err := GenerateSelfSigned()
+	certificate, err := GenerateSelfSigned()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -43,7 +43,6 @@ func DoTestResume(t *testing.T, newLocal, newRemote func(net.Conn, *Config) (*Co
 	}()
 	config := &Config{
 		Certificate:          certificate,
-		PrivateKey:           privateKey,
 		InsecureSkipVerify:   true,
 		ExtendedMasterSecret: RequireExtendedMasterSecret,
 	}