diff --git a/Gopkg.lock b/Gopkg.lock
index dec803bf0e29fec0f83b721a0b972bbf4e5733ed..e4ef295e9605dd361c0ebd57dd8d2c7d13878742 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -253,6 +253,14 @@
   pruneopts = "N"
   revision = "bb3d318650d48840a39aa21a027c6630e198e626"
 
+[[projects]]
+  branch = "master"
+  digest = "1:e87690b366bc22563796a55d0f07f819d7d017a6f889543be3d6fc0ad591bab0"
+  name = "github.com/fullsailor/pkcs7"
+  packages = ["."]
+  pruneopts = "N"
+  revision = "d7302db945fa6ea264fb79d8e13e931ea514a602"
+
 [[projects]]
   digest = "1:8df1d0de814f90082a4127781dd89e01f00586f36104c3e922cb3721a2ce153d"
   name = "github.com/getsentry/raven-go"
@@ -659,6 +667,14 @@
   pruneopts = "N"
   revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1"
 
+[[projects]]
+  digest = "1:59e2776740eed3c71fdff7cf591c9401d69588607b0ab3fe2239005d8ca5ab33"
+  name = "github.com/zakjan/cert-chain-resolver"
+  packages = ["certUtil"]
+  pruneopts = "N"
+  revision = "6076e1ded27284d6df12668dba6bee76fc17f84e"
+  version = "1.0.2"
+
 [[projects]]
   branch = "master"
   digest = "1:9fd2853acdf61d31817ea8369efe98b5d28060e1e6a478576b3928ce4af4b801"
@@ -1086,6 +1102,7 @@
     "github.com/stretchr/testify/require",
     "github.com/tevino/abool",
     "github.com/urfave/cli",
+    "github.com/zakjan/cert-chain-resolver/certUtil",
     "gitlab.com/ayufan/golang-cli-helpers",
     "gitlab.com/gitlab-org/gitlab-terminal",
     "golang.org/x/crypto/ssh",
diff --git a/Gopkg.toml b/Gopkg.toml
index 0e8096b0fb0bff9b96ff5ad2bc90a10404a0fe82..f7bda671c04877e13fc74f3716ef75ee26906e02 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -170,6 +170,10 @@ ignored = ["test", "appengine"]
   name = "github.com/imdario/mergo"
   version = "v0.3.7"
 
+[[constraint]]
+  name = "github.com/zakjan/cert-chain-resolver"
+  version = "1.0.2"
+
 ##
 ## Refrain innovations ;)
 ##
diff --git a/network/client.go b/network/client.go
index c5c2d6c205bdb3eb8ee05398b5f5e8540e2989c2..e6d642e92909b647fb75c710f29645f986fe179d 100644
--- a/network/client.go
+++ b/network/client.go
@@ -23,6 +23,7 @@ import (
 
 	"github.com/jpillora/backoff"
 	"github.com/sirupsen/logrus"
+	"github.com/zakjan/cert-chain-resolver/certUtil"
 
 	"gitlab.com/gitlab-org/gitlab-runner/common"
 )
@@ -186,6 +187,9 @@ func (n *client) getCAChain(tls *tls.ConnectionState) string {
 	seenCertificates := make(map[string]bool, 0)
 
 	for _, verifiedChain := range tls.VerifiedChains {
+		verifiedChain, _ = certUtil.FetchCertificateChain(verifiedChain[0])
+		verifiedChain, _ = certUtil.AddRootCA(verifiedChain)
+
 		for _, certificate := range verifiedChain {
 			signature := hex.EncodeToString(certificate.Signature)
 			if seenCertificates[signature] {
diff --git a/vendor/github.com/fullsailor/pkcs7/LICENSE b/vendor/github.com/fullsailor/pkcs7/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..75f3209085b8e32d4335a959cec25e1e07da7b53
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Andrew Smith
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/fullsailor/pkcs7/ber.go b/vendor/github.com/fullsailor/pkcs7/ber.go
new file mode 100644
index 0000000000000000000000000000000000000000..89e96d30c791d74ae7d170d5cc77689f37f74d4e
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/ber.go
@@ -0,0 +1,248 @@
+package pkcs7
+
+import (
+	"bytes"
+	"errors"
+)
+
+// var encodeIndent = 0
+
+type asn1Object interface {
+	EncodeTo(writer *bytes.Buffer) error
+}
+
+type asn1Structured struct {
+	tagBytes []byte
+	content  []asn1Object
+}
+
+func (s asn1Structured) EncodeTo(out *bytes.Buffer) error {
+	//fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
+	//encodeIndent++
+	inner := new(bytes.Buffer)
+	for _, obj := range s.content {
+		err := obj.EncodeTo(inner)
+		if err != nil {
+			return err
+		}
+	}
+	//encodeIndent--
+	out.Write(s.tagBytes)
+	encodeLength(out, inner.Len())
+	out.Write(inner.Bytes())
+	return nil
+}
+
+type asn1Primitive struct {
+	tagBytes []byte
+	length   int
+	content  []byte
+}
+
+func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
+	_, err := out.Write(p.tagBytes)
+	if err != nil {
+		return err
+	}
+	if err = encodeLength(out, p.length); err != nil {
+		return err
+	}
+	//fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length)
+	//fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content))
+	out.Write(p.content)
+
+	return nil
+}
+
+func ber2der(ber []byte) ([]byte, error) {
+	if len(ber) == 0 {
+		return nil, errors.New("ber2der: input ber is empty")
+	}
+	//fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber))
+	out := new(bytes.Buffer)
+
+	obj, _, err := readObject(ber, 0)
+	if err != nil {
+		return nil, err
+	}
+	obj.EncodeTo(out)
+
+	// if offset < len(ber) {
+	//	return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber))
+	//}
+
+	return out.Bytes(), nil
+}
+
+// encodes lengths that are longer than 127 into string of bytes
+func marshalLongLength(out *bytes.Buffer, i int) (err error) {
+	n := lengthLength(i)
+
+	for ; n > 0; n-- {
+		err = out.WriteByte(byte(i >> uint((n-1)*8)))
+		if err != nil {
+			return
+		}
+	}
+
+	return nil
+}
+
+// computes the byte length of an encoded length value
+func lengthLength(i int) (numBytes int) {
+	numBytes = 1
+	for i > 255 {
+		numBytes++
+		i >>= 8
+	}
+	return
+}
+
+// encodes the length in DER format
+// If the length fits in 7 bits, the value is encoded directly.
+//
+// Otherwise, the number of bytes to encode the length is first determined.
+// This number is likely to be 4 or less for a 32bit length. This number is
+// added to 0x80. The length is encoded in big endian encoding follow after
+//
+// Examples:
+//  length | byte 1 | bytes n
+//  0      | 0x00   | -
+//  120    | 0x78   | -
+//  200    | 0x81   | 0xC8
+//  500    | 0x82   | 0x01 0xF4
+//
+func encodeLength(out *bytes.Buffer, length int) (err error) {
+	if length >= 128 {
+		l := lengthLength(length)
+		err = out.WriteByte(0x80 | byte(l))
+		if err != nil {
+			return
+		}
+		err = marshalLongLength(out, length)
+		if err != nil {
+			return
+		}
+	} else {
+		err = out.WriteByte(byte(length))
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func readObject(ber []byte, offset int) (asn1Object, int, error) {
+	//fmt.Printf("\n====> Starting readObject at offset: %d\n\n", offset)
+	tagStart := offset
+	b := ber[offset]
+	offset++
+	tag := b & 0x1F // last 5 bits
+	if tag == 0x1F {
+		tag = 0
+		for ber[offset] >= 0x80 {
+			tag = tag*128 + ber[offset] - 0x80
+			offset++
+		}
+		tag = tag*128 + ber[offset] - 0x80
+		offset++
+	}
+	tagEnd := offset
+
+	kind := b & 0x20
+	/*
+		if kind == 0 {
+			fmt.Print("--> Primitive\n")
+		} else {
+			fmt.Print("--> Constructed\n")
+		}
+	*/
+	// read length
+	var length int
+	l := ber[offset]
+	offset++
+	indefinite := false
+	if l > 0x80 {
+		numberOfBytes := (int)(l & 0x7F)
+		if numberOfBytes > 4 { // int is only guaranteed to be 32bit
+			return nil, 0, errors.New("ber2der: BER tag length too long")
+		}
+		if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F {
+			return nil, 0, errors.New("ber2der: BER tag length is negative")
+		}
+		if 0x0 == (int)(ber[offset]) {
+			return nil, 0, errors.New("ber2der: BER tag length has leading zero")
+		}
+		//fmt.Printf("--> (compute length) indicator byte: %x\n", l)
+		//fmt.Printf("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes])
+		for i := 0; i < numberOfBytes; i++ {
+			length = length*256 + (int)(ber[offset])
+			offset++
+		}
+	} else if l == 0x80 {
+		indefinite = true
+	} else {
+		length = (int)(l)
+	}
+
+	//fmt.Printf("--> length        : %d\n", length)
+	contentEnd := offset + length
+	if contentEnd > len(ber) {
+		return nil, 0, errors.New("ber2der: BER tag length is more than available data")
+	}
+	//fmt.Printf("--> content start : %d\n", offset)
+	//fmt.Printf("--> content end   : %d\n", contentEnd)
+	//fmt.Printf("--> content       : % X\n", ber[offset:contentEnd])
+	var obj asn1Object
+	if indefinite && kind == 0 {
+		return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding")
+	}
+	if kind == 0 {
+		obj = asn1Primitive{
+			tagBytes: ber[tagStart:tagEnd],
+			length:   length,
+			content:  ber[offset:contentEnd],
+		}
+	} else {
+		var subObjects []asn1Object
+		for (offset < contentEnd) || indefinite {
+			var subObj asn1Object
+			var err error
+			subObj, offset, err = readObject(ber, offset)
+			if err != nil {
+				return nil, 0, err
+			}
+			subObjects = append(subObjects, subObj)
+
+			if indefinite {
+				terminated, err := isIndefiniteTermination(ber, offset)
+				if err != nil {
+					return nil, 0, err
+				}
+
+				if terminated {
+					break
+				}
+			}
+		}
+		obj = asn1Structured{
+			tagBytes: ber[tagStart:tagEnd],
+			content:  subObjects,
+		}
+	}
+
+	// Apply indefinite form length with 0x0000 terminator.
+	if indefinite {
+		contentEnd = offset + 2
+	}
+
+	return obj, contentEnd, nil
+}
+
+func isIndefiniteTermination(ber []byte, offset int) (bool, error) {
+	if len(ber) - offset < 2 {
+		return false, errors.New("ber2der: Invalid BER format")
+	}
+
+	return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil
+}
diff --git a/vendor/github.com/fullsailor/pkcs7/ber_test.go b/vendor/github.com/fullsailor/pkcs7/ber_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..19a0f514c2e87bd25c6578e91ddf3279d929802d
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/ber_test.go
@@ -0,0 +1,97 @@
+package pkcs7
+
+import (
+	"bytes"
+	"encoding/asn1"
+	"strings"
+	"testing"
+)
+
+func TestBer2Der(t *testing.T) {
+	// indefinite length fixture
+	ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00}
+	expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01}
+	der, err := ber2der(ber)
+	if err != nil {
+		t.Fatalf("ber2der failed with error: %v", err)
+	}
+	if bytes.Compare(der, expected) != 0 {
+		t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der)
+	}
+
+	if der2, err := ber2der(der); err != nil {
+		t.Errorf("ber2der on DER bytes failed with error: %v", err)
+	} else {
+		if !bytes.Equal(der, der2) {
+			t.Error("ber2der is not idempotent")
+		}
+	}
+	var thing struct {
+		Number int
+	}
+	rest, err := asn1.Unmarshal(der, &thing)
+	if err != nil {
+		t.Errorf("Cannot parse resulting DER because: %v", err)
+	} else if len(rest) > 0 {
+		t.Errorf("Resulting DER has trailing data: % X", rest)
+	}
+}
+
+func TestBer2Der_Negatives(t *testing.T) {
+	fixtures := []struct {
+		Input         []byte
+		ErrorContains string
+	}{
+		{[]byte{0x30, 0x85}, "length too long"},
+		{[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"},
+		{[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"},
+		{[]byte{0x30, 0x80, 0x1, 0x2, 0x1, 0x2}, "Invalid BER format"},
+		{[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"},
+	}
+
+	for _, fixture := range fixtures {
+		_, err := ber2der(fixture.Input)
+		if err == nil {
+			t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains)
+		}
+		if !strings.Contains(err.Error(), fixture.ErrorContains) {
+			t.Errorf("Unexpected error thrown.\n\tExpected: /%s/\n\tActual: %s", fixture.ErrorContains, err.Error())
+		}
+	}
+}
+
+func TestBer2Der_NestedMultipleIndefinite(t *testing.T) {
+	// indefinite length fixture
+	ber := []byte{0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}
+	expected := []byte{0x30, 0x0A, 0x30, 0x03, 0x02, 0x01, 0x01, 0x30, 0x03, 0x02, 0x01, 0x02}
+
+	der, err := ber2der(ber)
+	if err != nil {
+		t.Fatalf("ber2der failed with error: %v", err)
+	}
+	if bytes.Compare(der, expected) != 0 {
+		t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der)
+	}
+
+	if der2, err := ber2der(der); err != nil {
+		t.Errorf("ber2der on DER bytes failed with error: %v", err)
+	} else {
+		if !bytes.Equal(der, der2) {
+			t.Error("ber2der is not idempotent")
+		}
+	}
+	var thing struct {
+		Nest1 struct {
+			Number int
+		}
+		Nest2 struct {
+			Number int
+		}
+	}
+	rest, err := asn1.Unmarshal(der, &thing)
+	if err != nil {
+		t.Errorf("Cannot parse resulting DER because: %v", err)
+	} else if len(rest) > 0 {
+		t.Errorf("Resulting DER has trailing data: % X", rest)
+	}
+}
diff --git a/vendor/github.com/fullsailor/pkcs7/pkcs7.go b/vendor/github.com/fullsailor/pkcs7/pkcs7.go
new file mode 100644
index 0000000000000000000000000000000000000000..0264466b467e2e5174df4de802faf7dd033a3b5f
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/pkcs7.go
@@ -0,0 +1,962 @@
+// Package pkcs7 implements parsing and generation of some PKCS#7 structures.
+package pkcs7
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/des"
+	"crypto/hmac"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"errors"
+	"fmt"
+	"math/big"
+	"sort"
+	"time"
+
+	_ "crypto/sha1" // for crypto.SHA1
+)
+
+// PKCS7 Represents a PKCS7 structure
+type PKCS7 struct {
+	Content      []byte
+	Certificates []*x509.Certificate
+	CRLs         []pkix.CertificateList
+	Signers      []signerInfo
+	raw          interface{}
+}
+
+type contentInfo struct {
+	ContentType asn1.ObjectIdentifier
+	Content     asn1.RawValue `asn1:"explicit,optional,tag:0"`
+}
+
+// ErrUnsupportedContentType is returned when a PKCS7 content is not supported.
+// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2),
+// and Enveloped Data are supported (1.2.840.113549.1.7.3)
+var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type")
+
+type unsignedData []byte
+
+var (
+	oidData                   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
+	oidSignedData             = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
+	oidEnvelopedData          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3}
+	oidSignedAndEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 4}
+	oidDigestedData           = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 5}
+	oidEncryptedData          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6}
+	oidAttributeContentType   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
+	oidAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
+	oidAttributeSigningTime   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
+)
+
+type signedData struct {
+	Version                    int                        `asn1:"default:1"`
+	DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
+	ContentInfo                contentInfo
+	Certificates               rawCertificates        `asn1:"optional,tag:0"`
+	CRLs                       []pkix.CertificateList `asn1:"optional,tag:1"`
+	SignerInfos                []signerInfo           `asn1:"set"`
+}
+
+type rawCertificates struct {
+	Raw asn1.RawContent
+}
+
+type envelopedData struct {
+	Version              int
+	RecipientInfos       []recipientInfo `asn1:"set"`
+	EncryptedContentInfo encryptedContentInfo
+}
+
+type recipientInfo struct {
+	Version                int
+	IssuerAndSerialNumber  issuerAndSerial
+	KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
+	EncryptedKey           []byte
+}
+
+type encryptedContentInfo struct {
+	ContentType                asn1.ObjectIdentifier
+	ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
+	EncryptedContent           asn1.RawValue `asn1:"tag:0,optional"`
+}
+
+type attribute struct {
+	Type  asn1.ObjectIdentifier
+	Value asn1.RawValue `asn1:"set"`
+}
+
+type issuerAndSerial struct {
+	IssuerName   asn1.RawValue
+	SerialNumber *big.Int
+}
+
+// MessageDigestMismatchError is returned when the signer data digest does not
+// match the computed digest for the contained content
+type MessageDigestMismatchError struct {
+	ExpectedDigest []byte
+	ActualDigest   []byte
+}
+
+func (err *MessageDigestMismatchError) Error() string {
+	return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual  : %X", err.ExpectedDigest, err.ActualDigest)
+}
+
+type signerInfo struct {
+	Version                   int `asn1:"default:1"`
+	IssuerAndSerialNumber     issuerAndSerial
+	DigestAlgorithm           pkix.AlgorithmIdentifier
+	AuthenticatedAttributes   []attribute `asn1:"optional,tag:0"`
+	DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
+	EncryptedDigest           []byte
+	UnauthenticatedAttributes []attribute `asn1:"optional,tag:1"`
+}
+
+// Parse decodes a DER encoded PKCS7 package
+func Parse(data []byte) (p7 *PKCS7, err error) {
+	if len(data) == 0 {
+		return nil, errors.New("pkcs7: input data is empty")
+	}
+	var info contentInfo
+	der, err := ber2der(data)
+	if err != nil {
+		return nil, err
+	}
+	rest, err := asn1.Unmarshal(der, &info)
+	if len(rest) > 0 {
+		err = asn1.SyntaxError{Msg: "trailing data"}
+		return
+	}
+	if err != nil {
+		return
+	}
+
+	// fmt.Printf("--> Content Type: %s", info.ContentType)
+	switch {
+	case info.ContentType.Equal(oidSignedData):
+		return parseSignedData(info.Content.Bytes)
+	case info.ContentType.Equal(oidEnvelopedData):
+		return parseEnvelopedData(info.Content.Bytes)
+	}
+	return nil, ErrUnsupportedContentType
+}
+
+func parseSignedData(data []byte) (*PKCS7, error) {
+	var sd signedData
+	asn1.Unmarshal(data, &sd)
+	certs, err := sd.Certificates.Parse()
+	if err != nil {
+		return nil, err
+	}
+	// fmt.Printf("--> Signed Data Version %d\n", sd.Version)
+
+	var compound asn1.RawValue
+	var content unsignedData
+
+	// The Content.Bytes maybe empty on PKI responses.
+	if len(sd.ContentInfo.Content.Bytes) > 0 {
+		if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil {
+			return nil, err
+		}
+	}
+	// Compound octet string
+	if compound.IsCompound {
+		if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil {
+			return nil, err
+		}
+	} else {
+		// assuming this is tag 04
+		content = compound.Bytes
+	}
+	return &PKCS7{
+		Content:      content,
+		Certificates: certs,
+		CRLs:         sd.CRLs,
+		Signers:      sd.SignerInfos,
+		raw:          sd}, nil
+}
+
+func (raw rawCertificates) Parse() ([]*x509.Certificate, error) {
+	if len(raw.Raw) == 0 {
+		return nil, nil
+	}
+
+	var val asn1.RawValue
+	if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil {
+		return nil, err
+	}
+
+	return x509.ParseCertificates(val.Bytes)
+}
+
+func parseEnvelopedData(data []byte) (*PKCS7, error) {
+	var ed envelopedData
+	if _, err := asn1.Unmarshal(data, &ed); err != nil {
+		return nil, err
+	}
+	return &PKCS7{
+		raw: ed,
+	}, nil
+}
+
+// Verify checks the signatures of a PKCS7 object
+// WARNING: Verify does not check signing time or verify certificate chains at
+// this time.
+func (p7 *PKCS7) Verify() (err error) {
+	if len(p7.Signers) == 0 {
+		return errors.New("pkcs7: Message has no signers")
+	}
+	for _, signer := range p7.Signers {
+		if err := verifySignature(p7, signer); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func verifySignature(p7 *PKCS7, signer signerInfo) error {
+	signedData := p7.Content
+	hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
+	if err != nil {
+		return err
+	}
+	if len(signer.AuthenticatedAttributes) > 0 {
+		// TODO(fullsailor): First check the content type match
+		var digest []byte
+		err := unmarshalAttribute(signer.AuthenticatedAttributes, oidAttributeMessageDigest, &digest)
+		if err != nil {
+			return err
+		}
+		h := hash.New()
+		h.Write(p7.Content)
+		computed := h.Sum(nil)
+		if !hmac.Equal(digest, computed) {
+			return &MessageDigestMismatchError{
+				ExpectedDigest: digest,
+				ActualDigest:   computed,
+			}
+		}
+		// TODO(fullsailor): Optionally verify certificate chain
+		// TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore
+		signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
+		if err != nil {
+			return err
+		}
+	}
+	cert := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
+	if cert == nil {
+		return errors.New("pkcs7: No certificate for signer")
+	}
+
+	algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm)
+	if algo == x509.UnknownSignatureAlgorithm {
+		// I'm not sure what the spec here is, and the openssl sources were not
+		// helpful. But, this is what App Store receipts appear to do.
+		// The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)"
+		// But we're expecting a digest + encryption algorithm. So... we're going
+		// to determine an algorithm based on the DigestAlgorithm and this
+		// encryption algorithm.
+		if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) {
+			algo = getRSASignatureAlgorithmForDigestAlgorithm(hash)
+		}
+	}
+	return cert.CheckSignature(algo, signedData, signer.EncryptedDigest)
+}
+
+func marshalAttributes(attrs []attribute) ([]byte, error) {
+	encodedAttributes, err := asn1.Marshal(struct {
+		A []attribute `asn1:"set"`
+	}{A: attrs})
+	if err != nil {
+		return nil, err
+	}
+
+	// Remove the leading sequence octets
+	var raw asn1.RawValue
+	asn1.Unmarshal(encodedAttributes, &raw)
+	return raw.Bytes, nil
+}
+
+var (
+	oidDigestAlgorithmSHA1    = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
+	oidEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
+)
+
+func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate {
+	for _, cert := range certs {
+		if isCertMatchForIssuerAndSerial(cert, ias) {
+			return cert
+		}
+	}
+	return nil
+}
+
+func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) {
+	switch {
+	case oid.Equal(oidDigestAlgorithmSHA1):
+		return crypto.SHA1, nil
+  case oid.Equal(oidSHA256):
+    return crypto.SHA256, nil
+	}
+	return crypto.Hash(0), ErrUnsupportedAlgorithm
+}
+
+func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm {
+	for _, details := range signatureAlgorithmDetails {
+		if details.pubKeyAlgo == x509.RSA && details.hash == hash {
+			return details.algo
+		}
+	}
+	return x509.UnknownSignatureAlgorithm
+}
+
+// GetOnlySigner returns an x509.Certificate for the first signer of the signed
+// data payload. If there are more or less than one signer, nil is returned
+func (p7 *PKCS7) GetOnlySigner() *x509.Certificate {
+	if len(p7.Signers) != 1 {
+		return nil
+	}
+	signer := p7.Signers[0]
+	return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
+}
+
+// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed
+var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported")
+
+// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data
+var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type")
+
+// Decrypt decrypts encrypted content info for recipient cert and private key
+func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pk crypto.PrivateKey) ([]byte, error) {
+	data, ok := p7.raw.(envelopedData)
+	if !ok {
+		return nil, ErrNotEncryptedContent
+	}
+	recipient := selectRecipientForCertificate(data.RecipientInfos, cert)
+	if recipient.EncryptedKey == nil {
+		return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
+	}
+	if priv := pk.(*rsa.PrivateKey); priv != nil {
+		var contentKey []byte
+		contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, priv, recipient.EncryptedKey)
+		if err != nil {
+			return nil, err
+		}
+		return data.EncryptedContentInfo.decrypt(contentKey)
+	}
+	fmt.Printf("Unsupported Private Key: %v\n", pk)
+	return nil, ErrUnsupportedAlgorithm
+}
+
+var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7}
+var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}
+var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
+var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6}
+var oidEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}
+
+func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) {
+	alg := eci.ContentEncryptionAlgorithm.Algorithm
+	if !alg.Equal(oidEncryptionAlgorithmDESCBC) &&
+		!alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) &&
+		!alg.Equal(oidEncryptionAlgorithmAES256CBC) &&
+		!alg.Equal(oidEncryptionAlgorithmAES128CBC) &&
+		!alg.Equal(oidEncryptionAlgorithmAES128GCM) {
+		fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg)
+		return nil, ErrUnsupportedAlgorithm
+	}
+
+	// EncryptedContent can either be constructed of multple OCTET STRINGs
+	// or _be_ a tagged OCTET STRING
+	var cyphertext []byte
+	if eci.EncryptedContent.IsCompound {
+		// Complex case to concat all of the children OCTET STRINGs
+		var buf bytes.Buffer
+		cypherbytes := eci.EncryptedContent.Bytes
+		for {
+			var part []byte
+			cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part)
+			buf.Write(part)
+			if cypherbytes == nil {
+				break
+			}
+		}
+		cyphertext = buf.Bytes()
+	} else {
+		// Simple case, the bytes _are_ the cyphertext
+		cyphertext = eci.EncryptedContent.Bytes
+	}
+
+	var block cipher.Block
+	var err error
+
+	switch {
+	case alg.Equal(oidEncryptionAlgorithmDESCBC):
+		block, err = des.NewCipher(key)
+	case alg.Equal(oidEncryptionAlgorithmDESEDE3CBC):
+		block, err = des.NewTripleDESCipher(key)
+	case alg.Equal(oidEncryptionAlgorithmAES256CBC):
+		fallthrough
+	case alg.Equal(oidEncryptionAlgorithmAES128GCM), alg.Equal(oidEncryptionAlgorithmAES128CBC):
+		block, err = aes.NewCipher(key)
+	}
+
+	if err != nil {
+		return nil, err
+	}
+
+	if alg.Equal(oidEncryptionAlgorithmAES128GCM) {
+		params := aesGCMParameters{}
+		paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes
+
+		_, err := asn1.Unmarshal(paramBytes, &params)
+		if err != nil {
+			return nil, err
+		}
+
+		gcm, err := cipher.NewGCM(block)
+		if err != nil {
+			return nil, err
+		}
+
+		if len(params.Nonce) != gcm.NonceSize() {
+			return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
+		}
+		if params.ICVLen != gcm.Overhead() {
+			return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
+		}
+
+		plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil)
+		if err != nil {
+			return nil, err
+		}
+
+		return plaintext, nil
+	}
+
+	iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes
+	if len(iv) != block.BlockSize() {
+		return nil, errors.New("pkcs7: encryption algorithm parameters are malformed")
+	}
+	mode := cipher.NewCBCDecrypter(block, iv)
+	plaintext := make([]byte, len(cyphertext))
+	mode.CryptBlocks(plaintext, cyphertext)
+	if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil {
+		return nil, err
+	}
+	return plaintext, nil
+}
+
+func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo {
+	for _, recp := range recipients {
+		if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
+			return recp
+		}
+	}
+	return recipientInfo{}
+}
+
+func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool {
+	return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Compare(cert.RawIssuer, ias.IssuerName.FullBytes) == 0
+}
+
+func pad(data []byte, blocklen int) ([]byte, error) {
+	if blocklen < 1 {
+		return nil, fmt.Errorf("invalid blocklen %d", blocklen)
+	}
+	padlen := blocklen - (len(data) % blocklen)
+	if padlen == 0 {
+		padlen = blocklen
+	}
+	pad := bytes.Repeat([]byte{byte(padlen)}, padlen)
+	return append(data, pad...), nil
+}
+
+func unpad(data []byte, blocklen int) ([]byte, error) {
+	if blocklen < 1 {
+		return nil, fmt.Errorf("invalid blocklen %d", blocklen)
+	}
+	if len(data)%blocklen != 0 || len(data) == 0 {
+		return nil, fmt.Errorf("invalid data len %d", len(data))
+	}
+
+	// the last byte is the length of padding
+	padlen := int(data[len(data)-1])
+
+	// check padding integrity, all bytes should be the same
+	pad := data[len(data)-padlen:]
+	for _, padbyte := range pad {
+		if padbyte != byte(padlen) {
+			return nil, errors.New("invalid padding")
+		}
+	}
+
+	return data[:len(data)-padlen], nil
+}
+
+func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error {
+	for _, attr := range attrs {
+		if attr.Type.Equal(attributeType) {
+			_, err := asn1.Unmarshal(attr.Value.Bytes, out)
+			return err
+		}
+	}
+	return errors.New("pkcs7: attribute type not in attributes")
+}
+
+// UnmarshalSignedAttribute decodes a single attribute from the signer info
+func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error {
+	sd, ok := p7.raw.(signedData)
+	if !ok {
+		return errors.New("pkcs7: payload is not signedData content")
+	}
+	if len(sd.SignerInfos) < 1 {
+		return errors.New("pkcs7: payload has no signers")
+	}
+	attributes := sd.SignerInfos[0].AuthenticatedAttributes
+	return unmarshalAttribute(attributes, attributeType, out)
+}
+
+// SignedData is an opaque data structure for creating signed data payloads
+type SignedData struct {
+	sd            signedData
+	certs         []*x509.Certificate
+	messageDigest []byte
+}
+
+// Attribute represents a key value pair attribute. Value must be marshalable byte
+// `encoding/asn1`
+type Attribute struct {
+	Type  asn1.ObjectIdentifier
+	Value interface{}
+}
+
+// SignerInfoConfig are optional values to include when adding a signer
+type SignerInfoConfig struct {
+	ExtraSignedAttributes []Attribute
+}
+
+// NewSignedData initializes a SignedData with content
+func NewSignedData(data []byte) (*SignedData, error) {
+	content, err := asn1.Marshal(data)
+	if err != nil {
+		return nil, err
+	}
+	ci := contentInfo{
+		ContentType: oidData,
+		Content:     asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
+	}
+	digAlg := pkix.AlgorithmIdentifier{
+		Algorithm: oidDigestAlgorithmSHA1,
+	}
+	h := crypto.SHA1.New()
+	h.Write(data)
+	md := h.Sum(nil)
+	sd := signedData{
+		ContentInfo:                ci,
+		Version:                    1,
+		DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digAlg},
+	}
+	return &SignedData{sd: sd, messageDigest: md}, nil
+}
+
+type attributes struct {
+	types  []asn1.ObjectIdentifier
+	values []interface{}
+}
+
+// Add adds the attribute, maintaining insertion order
+func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) {
+	attrs.types = append(attrs.types, attrType)
+	attrs.values = append(attrs.values, value)
+}
+
+type sortableAttribute struct {
+	SortKey   []byte
+	Attribute attribute
+}
+
+type attributeSet []sortableAttribute
+
+func (sa attributeSet) Len() int {
+	return len(sa)
+}
+
+func (sa attributeSet) Less(i, j int) bool {
+	return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0
+}
+
+func (sa attributeSet) Swap(i, j int) {
+	sa[i], sa[j] = sa[j], sa[i]
+}
+
+func (sa attributeSet) Attributes() []attribute {
+	attrs := make([]attribute, len(sa))
+	for i, attr := range sa {
+		attrs[i] = attr.Attribute
+	}
+	return attrs
+}
+
+func (attrs *attributes) ForMarshaling() ([]attribute, error) {
+	sortables := make(attributeSet, len(attrs.types))
+	for i := range sortables {
+		attrType := attrs.types[i]
+		attrValue := attrs.values[i]
+		asn1Value, err := asn1.Marshal(attrValue)
+		if err != nil {
+			return nil, err
+		}
+		attr := attribute{
+			Type:  attrType,
+			Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag
+		}
+		encoded, err := asn1.Marshal(attr)
+		if err != nil {
+			return nil, err
+		}
+		sortables[i] = sortableAttribute{
+			SortKey:   encoded,
+			Attribute: attr,
+		}
+	}
+	sort.Sort(sortables)
+	return sortables.Attributes(), nil
+}
+
+// AddSigner signs attributes about the content and adds certificate to payload
+func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
+	attrs := &attributes{}
+	attrs.Add(oidAttributeContentType, sd.sd.ContentInfo.ContentType)
+	attrs.Add(oidAttributeMessageDigest, sd.messageDigest)
+	attrs.Add(oidAttributeSigningTime, time.Now())
+	for _, attr := range config.ExtraSignedAttributes {
+		attrs.Add(attr.Type, attr.Value)
+	}
+	finalAttrs, err := attrs.ForMarshaling()
+	if err != nil {
+		return err
+	}
+	signature, err := signAttributes(finalAttrs, pkey, crypto.SHA1)
+	if err != nil {
+		return err
+	}
+
+	ias, err := cert2issuerAndSerial(cert)
+	if err != nil {
+		return err
+	}
+
+	signer := signerInfo{
+		AuthenticatedAttributes:   finalAttrs,
+		DigestAlgorithm:           pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1},
+		DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA},
+		IssuerAndSerialNumber:     ias,
+		EncryptedDigest:           signature,
+		Version:                   1,
+	}
+	// create signature of signed attributes
+	sd.certs = append(sd.certs, cert)
+	sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
+	return nil
+}
+
+// AddCertificate adds the certificate to the payload. Useful for parent certificates
+func (sd *SignedData) AddCertificate(cert *x509.Certificate) {
+	sd.certs = append(sd.certs, cert)
+}
+
+// Detach removes content from the signed data struct to make it a detached signature.
+// This must be called right before Finish()
+func (sd *SignedData) Detach() {
+	sd.sd.ContentInfo = contentInfo{ContentType: oidData}
+}
+
+// Finish marshals the content and its signers
+func (sd *SignedData) Finish() ([]byte, error) {
+	sd.sd.Certificates = marshalCertificates(sd.certs)
+	inner, err := asn1.Marshal(sd.sd)
+	if err != nil {
+		return nil, err
+	}
+	outer := contentInfo{
+		ContentType: oidSignedData,
+		Content:     asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
+	}
+	return asn1.Marshal(outer)
+}
+
+func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) {
+	var ias issuerAndSerial
+	// The issuer RDNSequence has to match exactly the sequence in the certificate
+	// We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence
+	ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer}
+	ias.SerialNumber = cert.SerialNumber
+
+	return ias, nil
+}
+
+// signs the DER encoded form of the attributes with the private key
+func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hash crypto.Hash) ([]byte, error) {
+	attrBytes, err := marshalAttributes(attrs)
+	if err != nil {
+		return nil, err
+	}
+	h := hash.New()
+	h.Write(attrBytes)
+	hashed := h.Sum(nil)
+	switch priv := pkey.(type) {
+	case *rsa.PrivateKey:
+		return rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hashed)
+	}
+	return nil, ErrUnsupportedAlgorithm
+}
+
+// concats and wraps the certificates in the RawValue structure
+func marshalCertificates(certs []*x509.Certificate) rawCertificates {
+	var buf bytes.Buffer
+	for _, cert := range certs {
+		buf.Write(cert.Raw)
+	}
+	rawCerts, _ := marshalCertificateBytes(buf.Bytes())
+	return rawCerts
+}
+
+// Even though, the tag & length are stripped out during marshalling the
+// RawContent, we have to encode it into the RawContent. If its missing,
+// then `asn1.Marshal()` will strip out the certificate wrapper instead.
+func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
+	var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
+	b, err := asn1.Marshal(val)
+	if err != nil {
+		return rawCertificates{}, err
+	}
+	return rawCertificates{Raw: b}, nil
+}
+
+// DegenerateCertificate creates a signed data structure containing only the
+// provided certificate or certificate chain.
+func DegenerateCertificate(cert []byte) ([]byte, error) {
+	rawCert, err := marshalCertificateBytes(cert)
+	if err != nil {
+		return nil, err
+	}
+	emptyContent := contentInfo{ContentType: oidData}
+	sd := signedData{
+		Version:      1,
+		ContentInfo:  emptyContent,
+		Certificates: rawCert,
+		CRLs:         []pkix.CertificateList{},
+	}
+	content, err := asn1.Marshal(sd)
+	if err != nil {
+		return nil, err
+	}
+	signedContent := contentInfo{
+		ContentType: oidSignedData,
+		Content:     asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
+	}
+	return asn1.Marshal(signedContent)
+}
+
+const (
+	EncryptionAlgorithmDESCBC = iota
+	EncryptionAlgorithmAES128GCM
+)
+
+// ContentEncryptionAlgorithm determines the algorithm used to encrypt the
+// plaintext message. Change the value of this variable to change which
+// algorithm is used in the Encrypt() function.
+var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC
+
+// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
+// content with an unsupported algorithm.
+var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC and AES-128-GCM supported")
+
+const nonceSize = 12
+
+type aesGCMParameters struct {
+	Nonce  []byte `asn1:"tag:4"`
+	ICVLen int
+}
+
+func encryptAES128GCM(content []byte) ([]byte, *encryptedContentInfo, error) {
+	// Create AES key and nonce
+	key := make([]byte, 16)
+	nonce := make([]byte, nonceSize)
+
+	_, err := rand.Read(key)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	_, err = rand.Read(nonce)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Encrypt content
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gcm, err := cipher.NewGCM(block)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ciphertext := gcm.Seal(nil, nonce, content, nil)
+
+	// Prepare ASN.1 Encrypted Content Info
+	paramSeq := aesGCMParameters{
+		Nonce:  nonce,
+		ICVLen: gcm.Overhead(),
+	}
+
+	paramBytes, err := asn1.Marshal(paramSeq)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	eci := encryptedContentInfo{
+		ContentType: oidData,
+		ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
+			Algorithm: oidEncryptionAlgorithmAES128GCM,
+			Parameters: asn1.RawValue{
+				Tag:   asn1.TagSequence,
+				Bytes: paramBytes,
+			},
+		},
+		EncryptedContent: marshalEncryptedContent(ciphertext),
+	}
+
+	return key, &eci, nil
+}
+
+func encryptDESCBC(content []byte) ([]byte, *encryptedContentInfo, error) {
+	// Create DES key & CBC IV
+	key := make([]byte, 8)
+	iv := make([]byte, des.BlockSize)
+	_, err := rand.Read(key)
+	if err != nil {
+		return nil, nil, err
+	}
+	_, err = rand.Read(iv)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Encrypt padded content
+	block, err := des.NewCipher(key)
+	if err != nil {
+		return nil, nil, err
+	}
+	mode := cipher.NewCBCEncrypter(block, iv)
+	plaintext, err := pad(content, mode.BlockSize())
+	cyphertext := make([]byte, len(plaintext))
+	mode.CryptBlocks(cyphertext, plaintext)
+
+	// Prepare ASN.1 Encrypted Content Info
+	eci := encryptedContentInfo{
+		ContentType: oidData,
+		ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
+			Algorithm:  oidEncryptionAlgorithmDESCBC,
+			Parameters: asn1.RawValue{Tag: 4, Bytes: iv},
+		},
+		EncryptedContent: marshalEncryptedContent(cyphertext),
+	}
+
+	return key, &eci, nil
+}
+
+// Encrypt creates and returns an envelope data PKCS7 structure with encrypted
+// recipient keys for each recipient public key.
+//
+// The algorithm used to perform encryption is determined by the current value
+// of the global ContentEncryptionAlgorithm package variable. By default, the
+// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the
+// value before calling Encrypt(). For example:
+//
+//     ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM
+//
+// TODO(fullsailor): Add support for encrypting content with other algorithms
+func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) {
+	var eci *encryptedContentInfo
+	var key []byte
+	var err error
+
+	// Apply chosen symmetric encryption method
+	switch ContentEncryptionAlgorithm {
+	case EncryptionAlgorithmDESCBC:
+		key, eci, err = encryptDESCBC(content)
+
+	case EncryptionAlgorithmAES128GCM:
+		key, eci, err = encryptAES128GCM(content)
+
+	default:
+		return nil, ErrUnsupportedEncryptionAlgorithm
+	}
+
+	if err != nil {
+		return nil, err
+	}
+
+	// Prepare each recipient's encrypted cipher key
+	recipientInfos := make([]recipientInfo, len(recipients))
+	for i, recipient := range recipients {
+		encrypted, err := encryptKey(key, recipient)
+		if err != nil {
+			return nil, err
+		}
+		ias, err := cert2issuerAndSerial(recipient)
+		if err != nil {
+			return nil, err
+		}
+		info := recipientInfo{
+			Version:               0,
+			IssuerAndSerialNumber: ias,
+			KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
+				Algorithm: oidEncryptionAlgorithmRSA,
+			},
+			EncryptedKey: encrypted,
+		}
+		recipientInfos[i] = info
+	}
+
+	// Prepare envelope content
+	envelope := envelopedData{
+		EncryptedContentInfo: *eci,
+		Version:              0,
+		RecipientInfos:       recipientInfos,
+	}
+	innerContent, err := asn1.Marshal(envelope)
+	if err != nil {
+		return nil, err
+	}
+
+	// Prepare outer payload structure
+	wrapper := contentInfo{
+		ContentType: oidEnvelopedData,
+		Content:     asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
+	}
+
+	return asn1.Marshal(wrapper)
+}
+
+func marshalEncryptedContent(content []byte) asn1.RawValue {
+	asn1Content, _ := asn1.Marshal(content)
+	return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
+}
+
+func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) {
+	if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
+		return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
+	}
+	return nil, ErrUnsupportedAlgorithm
+}
diff --git a/vendor/github.com/fullsailor/pkcs7/pkcs7_test.go b/vendor/github.com/fullsailor/pkcs7/pkcs7_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..55ede8b61a20a99ec283d3818e217f1ab8998db5
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/pkcs7_test.go
@@ -0,0 +1,679 @@
+package pkcs7
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"encoding/pem"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"math/big"
+	"os"
+	"os/exec"
+	"testing"
+	"time"
+)
+
+func TestVerify(t *testing.T) {
+	fixture := UnmarshalTestFixture(SignedTestFixture)
+	p7, err := Parse(fixture.Input)
+	if err != nil {
+		t.Errorf("Parse encountered unexpected error: %v", err)
+	}
+
+	if err := p7.Verify(); err != nil {
+		t.Errorf("Verify failed with error: %v", err)
+	}
+	expected := []byte("We the People")
+	if bytes.Compare(p7.Content, expected) != 0 {
+		t.Errorf("Signed content does not match.\n\tExpected:%s\n\tActual:%s", expected, p7.Content)
+
+	}
+}
+
+func TestVerifyEC2(t *testing.T) {
+	fixture := UnmarshalTestFixture(EC2IdentityDocumentFixture)
+	p7, err := Parse(fixture.Input)
+	if err != nil {
+		t.Errorf("Parse encountered unexpected error: %v", err)
+	}
+	p7.Certificates = []*x509.Certificate{fixture.Certificate}
+	if err := p7.Verify(); err != nil {
+		t.Errorf("Verify failed with error: %v", err)
+	}
+}
+
+func TestVerifyAppStore(t *testing.T) {
+	fixture := UnmarshalTestFixture(AppStoreRecieptFixture)
+	p7, err := Parse(fixture.Input)
+	if err != nil {
+		t.Errorf("Parse encountered unexpected error: %v", err)
+	}
+	if err := p7.Verify(); err != nil {
+		t.Errorf("Verify failed with error: %v", err)
+	}
+}
+
+func TestDecrypt(t *testing.T) {
+	fixture := UnmarshalTestFixture(EncryptedTestFixture)
+	p7, err := Parse(fixture.Input)
+	if err != nil {
+		t.Fatal(err)
+	}
+	content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey)
+	if err != nil {
+		t.Errorf("Cannot Decrypt with error: %v", err)
+	}
+	expected := []byte("This is a test")
+	if bytes.Compare(content, expected) != 0 {
+		t.Errorf("Decrypted result does not match.\n\tExpected:%s\n\tActual:%s", expected, content)
+	}
+}
+
+func TestDegenerateCertificate(t *testing.T) {
+	cert, err := createTestCertificate()
+	if err != nil {
+		t.Fatal(err)
+	}
+	deg, err := DegenerateCertificate(cert.Certificate.Raw)
+	if err != nil {
+		t.Fatal(err)
+	}
+	testOpenSSLParse(t, deg)
+	pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg})
+}
+
+// writes the cert to a temporary file and tests that openssl can read it.
+func testOpenSSLParse(t *testing.T, certBytes []byte) {
+	tmpCertFile, err := ioutil.TempFile("", "testCertificate")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(tmpCertFile.Name()) // clean up
+
+	if _, err := tmpCertFile.Write(certBytes); err != nil {
+		t.Fatal(err)
+	}
+
+	opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name())
+	_, err = opensslCMD.Output()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := tmpCertFile.Close(); err != nil {
+		t.Fatal(err)
+	}
+
+}
+
+func TestSign(t *testing.T) {
+	cert, err := createTestCertificate()
+	if err != nil {
+		t.Fatal(err)
+	}
+	content := []byte("Hello World")
+	for _, testDetach := range []bool{false, true} {
+		toBeSigned, err := NewSignedData(content)
+		if err != nil {
+			t.Fatalf("Cannot initialize signed data: %s", err)
+		}
+		if err := toBeSigned.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil {
+			t.Fatalf("Cannot add signer: %s", err)
+		}
+		if testDetach {
+			t.Log("Testing detached signature")
+			toBeSigned.Detach()
+		} else {
+			t.Log("Testing attached signature")
+		}
+		signed, err := toBeSigned.Finish()
+		if err != nil {
+			t.Fatalf("Cannot finish signing data: %s", err)
+		}
+		pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
+		p7, err := Parse(signed)
+		if err != nil {
+			t.Fatalf("Cannot parse our signed data: %s", err)
+		}
+		if testDetach {
+			p7.Content = content
+		}
+		if bytes.Compare(content, p7.Content) != 0 {
+			t.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content)
+		}
+		if err := p7.Verify(); err != nil {
+			t.Errorf("Cannot verify our signed data: %s", err)
+		}
+	}
+}
+
+func ExampleSignedData() {
+	// generate a signing cert or load a key pair
+	cert, err := createTestCertificate()
+	if err != nil {
+		fmt.Printf("Cannot create test certificates: %s", err)
+	}
+
+	// Initialize a SignedData struct with content to be signed
+	signedData, err := NewSignedData([]byte("Example data to be signed"))
+	if err != nil {
+		fmt.Printf("Cannot initialize signed data: %s", err)
+	}
+
+	// Add the signing cert and private key
+	if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil {
+		fmt.Printf("Cannot add signer: %s", err)
+	}
+
+	// Call Detach() is you want to remove content from the signature
+	// and generate an S/MIME detached signature
+	signedData.Detach()
+
+	// Finish() to obtain the signature bytes
+	detachedSignature, err := signedData.Finish()
+	if err != nil {
+		fmt.Printf("Cannot finish signing data: %s", err)
+	}
+	pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature})
+}
+
+func TestOpenSSLVerifyDetachedSignature(t *testing.T) {
+	rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil)
+	if err != nil {
+		t.Fatalf("Cannot generate root cert: %s", err)
+	}
+	signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", rootCert)
+	if err != nil {
+		t.Fatalf("Cannot generate signer cert: %s", err)
+	}
+	content := []byte("Hello World")
+	toBeSigned, err := NewSignedData(content)
+	if err != nil {
+		t.Fatalf("Cannot initialize signed data: %s", err)
+	}
+	if err := toBeSigned.AddSigner(signerCert.Certificate, signerCert.PrivateKey, SignerInfoConfig{}); err != nil {
+		t.Fatalf("Cannot add signer: %s", err)
+	}
+	toBeSigned.Detach()
+	signed, err := toBeSigned.Finish()
+	if err != nil {
+		t.Fatalf("Cannot finish signing data: %s", err)
+	}
+
+	// write the root cert to a temp file
+	tmpRootCertFile, err := ioutil.TempFile("", "pkcs7TestRootCA")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(tmpRootCertFile.Name()) // clean up
+	fd, err := os.OpenFile(tmpRootCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
+	if err != nil {
+		t.Fatal(err)
+	}
+	pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Certificate.Raw})
+	fd.Close()
+
+	// write the signature to a temp file
+	tmpSignatureFile, err := ioutil.TempFile("", "pkcs7Signature")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(tmpSignatureFile.Name()) // clean up
+	ioutil.WriteFile(tmpSignatureFile.Name(), signed, 0755)
+
+	// write the content to a temp file
+	tmpContentFile, err := ioutil.TempFile("", "pkcs7Content")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(tmpContentFile.Name()) // clean up
+	ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
+
+	// call openssl to verify the signature on the content using the root
+	opensslCMD := exec.Command("openssl", "smime", "-verify",
+		"-in", tmpSignatureFile.Name(), "-inform", "DER",
+		"-content", tmpContentFile.Name(),
+		"-CAfile", tmpRootCertFile.Name())
+	out, err := opensslCMD.Output()
+	t.Logf("%s", out)
+	if err != nil {
+		t.Fatalf("openssl command failed with %s", err)
+	}
+}
+
+func TestEncrypt(t *testing.T) {
+	modes := []int{
+		EncryptionAlgorithmDESCBC,
+		EncryptionAlgorithmAES128GCM,
+	}
+
+	for _, mode := range modes {
+		ContentEncryptionAlgorithm = mode
+
+		plaintext := []byte("Hello Secret World!")
+		cert, err := createTestCertificate()
+		if err != nil {
+			t.Fatal(err)
+		}
+		encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate})
+		if err != nil {
+			t.Fatal(err)
+		}
+		p7, err := Parse(encrypted)
+		if err != nil {
+			t.Fatalf("cannot Parse encrypted result: %s", err)
+		}
+		result, err := p7.Decrypt(cert.Certificate, cert.PrivateKey)
+		if err != nil {
+			t.Fatalf("cannot Decrypt encrypted result: %s", err)
+		}
+		if bytes.Compare(plaintext, result) != 0 {
+			t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
+		}
+	}
+}
+
+func TestUnmarshalSignedAttribute(t *testing.T) {
+	cert, err := createTestCertificate()
+	if err != nil {
+		t.Fatal(err)
+	}
+	content := []byte("Hello World")
+	toBeSigned, err := NewSignedData(content)
+	if err != nil {
+		t.Fatalf("Cannot initialize signed data: %s", err)
+	}
+	oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7}
+	testValue := "TestValue"
+	if err := toBeSigned.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{
+		ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}},
+	}); err != nil {
+		t.Fatalf("Cannot add signer: %s", err)
+	}
+	signed, err := toBeSigned.Finish()
+	if err != nil {
+		t.Fatalf("Cannot finish signing data: %s", err)
+	}
+	p7, err := Parse(signed)
+	var actual string
+	err = p7.UnmarshalSignedAttribute(oidTest, &actual)
+	if err != nil {
+		t.Fatalf("Cannot unmarshal test value: %s", err)
+	}
+	if testValue != actual {
+		t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual)
+	}
+}
+
+func TestPad(t *testing.T) {
+	tests := []struct {
+		Original  []byte
+		Expected  []byte
+		BlockSize int
+	}{
+		{[]byte{0x1, 0x2, 0x3, 0x10}, []byte{0x1, 0x2, 0x3, 0x10, 0x4, 0x4, 0x4, 0x4}, 8},
+		{[]byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0}, []byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8}, 8},
+	}
+	for _, test := range tests {
+		padded, err := pad(test.Original, test.BlockSize)
+		if err != nil {
+			t.Errorf("pad encountered error: %s", err)
+			continue
+		}
+		if bytes.Compare(test.Expected, padded) != 0 {
+			t.Errorf("pad results mismatch:\n\tExpected: %X\n\tActual: %X", test.Expected, padded)
+		}
+	}
+}
+
+type certKeyPair struct {
+	Certificate *x509.Certificate
+	PrivateKey  *rsa.PrivateKey
+}
+
+func createTestCertificate() (certKeyPair, error) {
+	signer, err := createTestCertificateByIssuer("Eddard Stark", nil)
+	if err != nil {
+		return certKeyPair{}, err
+	}
+	fmt.Println("Created root cert")
+	pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: signer.Certificate.Raw})
+	pair, err := createTestCertificateByIssuer("Jon Snow", signer)
+	if err != nil {
+		return certKeyPair{}, err
+	}
+	fmt.Println("Created signer cert")
+	pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: pair.Certificate.Raw})
+	return *pair, nil
+}
+
+func createTestCertificateByIssuer(name string, issuer *certKeyPair) (*certKeyPair, error) {
+	priv, err := rsa.GenerateKey(rand.Reader, 1024)
+	if err != nil {
+		return nil, err
+	}
+	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 32)
+	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
+	if err != nil {
+		return nil, err
+	}
+
+	template := x509.Certificate{
+		SerialNumber:       serialNumber,
+		SignatureAlgorithm: x509.SHA256WithRSA,
+		Subject: pkix.Name{
+			CommonName:   name,
+			Organization: []string{"Acme Co"},
+		},
+		NotBefore:   time.Now(),
+		NotAfter:    time.Now().AddDate(1, 0, 0),
+		KeyUsage:    x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection},
+	}
+	var issuerCert *x509.Certificate
+	var issuerKey crypto.PrivateKey
+	if issuer != nil {
+		issuerCert = issuer.Certificate
+		issuerKey = issuer.PrivateKey
+	} else {
+		template.IsCA = true
+		template.KeyUsage |= x509.KeyUsageCertSign
+		issuerCert = &template
+		issuerKey = priv
+	}
+	cert, err := x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.Public(), issuerKey)
+	if err != nil {
+		return nil, err
+	}
+	leaf, err := x509.ParseCertificate(cert)
+	if err != nil {
+		return nil, err
+	}
+	return &certKeyPair{
+		Certificate: leaf,
+		PrivateKey:  priv,
+	}, nil
+}
+
+type TestFixture struct {
+	Input       []byte
+	Certificate *x509.Certificate
+	PrivateKey  *rsa.PrivateKey
+}
+
+func UnmarshalTestFixture(testPEMBlock string) TestFixture {
+	var result TestFixture
+	var derBlock *pem.Block
+	var pemBlock = []byte(testPEMBlock)
+	for {
+		derBlock, pemBlock = pem.Decode(pemBlock)
+		if derBlock == nil {
+			break
+		}
+		switch derBlock.Type {
+		case "PKCS7":
+			result.Input = derBlock.Bytes
+		case "CERTIFICATE":
+			result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes)
+		case "PRIVATE KEY":
+			result.PrivateKey, _ = x509.ParsePKCS1PrivateKey(derBlock.Bytes)
+		}
+	}
+
+	return result
+}
+
+func MarshalTestFixture(t TestFixture, w io.Writer) {
+	if t.Input != nil {
+		pem.Encode(w, &pem.Block{Type: "PKCS7", Bytes: t.Input})
+	}
+	if t.Certificate != nil {
+		pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: t.Certificate.Raw})
+	}
+	if t.PrivateKey != nil {
+		pem.Encode(w, &pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(t.PrivateKey)})
+	}
+}
+
+var SignedTestFixture = `
+-----BEGIN PKCS7-----
+MIIDVgYJKoZIhvcNAQcCoIIDRzCCA0MCAQExCTAHBgUrDgMCGjAcBgkqhkiG9w0B
+BwGgDwQNV2UgdGhlIFBlb3BsZaCCAdkwggHVMIIBQKADAgECAgRpuDctMAsGCSqG
+SIb3DQEBCzApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3Rh
+cmswHhcNMTUwNTA2MDQyNDQ4WhcNMTYwNTA2MDQyNDQ4WjAlMRAwDgYDVQQKEwdB
+Y21lIENvMREwDwYDVQQDEwhKb24gU25vdzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAqr+tTF4mZP5rMwlXp1y+crRtFpuLXF1zvBZiYMfIvAHwo1ta8E1IcyEP
+J1jIiKMcwbzeo6kAmZzIJRCTezq9jwXUsKbQTvcfOH9HmjUmXBRWFXZYoQs/OaaF
+a45deHmwEeMQkuSWEtYiVKKZXtJOtflKIT3MryJEDiiItMkdybUCAwEAAaMSMBAw
+DgYDVR0PAQH/BAQDAgCgMAsGCSqGSIb3DQEBCwOBgQDK1EweZWRL+f7Z+J0kVzY8
+zXptcBaV4Lf5wGZJLJVUgp33bpLNpT3yadS++XQJ+cvtW3wADQzBSTMduyOF8Zf+
+L7TjjrQ2+F2HbNbKUhBQKudxTfv9dJHdKbD+ngCCdQJYkIy2YexsoNG0C8nQkggy
+axZd/J69xDVx6pui3Sj8sDGCATYwggEyAgEBMDEwKTEQMA4GA1UEChMHQWNtZSBD
+bzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrAgRpuDctMAcGBSsOAwIaoGEwGAYJKoZI
+hvcNAQkDMQsGCSqGSIb3DQEHATAgBgkqhkiG9w0BCQUxExcRMTUwNTA2MDAyNDQ4
+LTA0MDAwIwYJKoZIhvcNAQkEMRYEFG9D7gcTh9zfKiYNJ1lgB0yTh4sZMAsGCSqG
+SIb3DQEBAQSBgFF3sGDU9PtXty/QMtpcFa35vvIOqmWQAIZt93XAskQOnBq4OloX
+iL9Ct7t1m4pzjRm0o9nDkbaSLZe7HKASHdCqijroScGlI8M+alJ8drHSFv6ZIjnM
+FIwIf0B2Lko6nh9/6mUXq7tbbIHa3Gd1JUVire/QFFtmgRXMbXYk8SIS
+-----END PKCS7-----
+-----BEGIN CERTIFICATE-----
+MIIB1TCCAUCgAwIBAgIEabg3LTALBgkqhkiG9w0BAQswKTEQMA4GA1UEChMHQWNt
+ZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTE1MDUwNjA0MjQ0OFoXDTE2
+MDUwNjA0MjQ0OFowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNu
+b3cwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKq/rUxeJmT+azMJV6dcvnK0
+bRabi1xdc7wWYmDHyLwB8KNbWvBNSHMhDydYyIijHMG83qOpAJmcyCUQk3s6vY8F
+1LCm0E73Hzh/R5o1JlwUVhV2WKELPzmmhWuOXXh5sBHjEJLklhLWIlSimV7STrX5
+SiE9zK8iRA4oiLTJHcm1AgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIAoDALBgkqhkiG
+9w0BAQsDgYEAytRMHmVkS/n+2fidJFc2PM16bXAWleC3+cBmSSyVVIKd926SzaU9
+8mnUvvl0CfnL7Vt8AA0MwUkzHbsjhfGX/i+04460Nvhdh2zWylIQUCrncU37/XSR
+3Smw/p4AgnUCWJCMtmHsbKDRtAvJ0JIIMmsWXfyevcQ1ceqbot0o/LA=
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIICXgIBAAKBgQCqv61MXiZk/mszCVenXL5ytG0Wm4tcXXO8FmJgx8i8AfCjW1rw
+TUhzIQ8nWMiIoxzBvN6jqQCZnMglEJN7Or2PBdSwptBO9x84f0eaNSZcFFYVdlih
+Cz85poVrjl14ebAR4xCS5JYS1iJUople0k61+UohPcyvIkQOKIi0yR3JtQIDAQAB
+AoGBAIPLCR9N+IKxodq11lNXEaUFwMHXc1zqwP8no+2hpz3+nVfplqqubEJ4/PJY
+5AgbJoIfnxVhyBXJXu7E+aD/OPneKZrgp58YvHKgGvvPyJg2gpC/1Fh0vQB0HNpI
+1ZzIZUl8ZTUtVgtnCBUOh5JGI4bFokAqrT//Uvcfd+idgxqBAkEA1ZbP/Kseld14
+qbWmgmU5GCVxsZRxgR1j4lG3UVjH36KXMtRTm1atAam1uw3OEGa6Y3ANjpU52FaB
+Hep5rkk4FQJBAMynMo1L1uiN5GP+KYLEF5kKRxK+FLjXR0ywnMh+gpGcZDcOae+J
++t1gLoWBIESH/Xt639T7smuSfrZSA9V0EyECQA8cvZiWDvLxmaEAXkipmtGPjKzQ
+4PsOtkuEFqFl07aKDYKmLUg3aMROWrJidqsIabWxbvQgsNgSvs38EiH3wkUCQQCg
+ndxb7piVXb9RBwm3OoU2tE1BlXMX+sVXmAkEhd2dwDsaxrI3sHf1xGXem5AimQRF
+JBOFyaCnMotGNioSHY5hAkEAxyXcNixQ2RpLXJTQZtwnbk0XDcbgB+fBgXnv/4f3
+BCvcu85DqJeJyQv44Oe1qsXEX9BfcQIOVaoep35RPlKi9g==
+-----END PRIVATE KEY-----`
+
+// echo -n "This is a test" > test.txt
+// openssl cms -encrypt -in test.txt cert.pem
+var EncryptedTestFixture = `
+-----BEGIN PKCS7-----
+MIIBGgYJKoZIhvcNAQcDoIIBCzCCAQcCAQAxgcwwgckCAQAwMjApMRAwDgYDVQQK
+EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMA0GCSqGSIb3
+DQEBAQUABIGAyFz7bfI2noUs4FpmYfztm1pVjGyB00p9x0H3gGHEYNXdqlq8VG8d
+iq36poWtEkatnwsOlURWZYECSi0g5IAL0U9sj82EN0xssZNaK0S5FTGnB3DPvYgt
+HJvcKq7YvNLKMh4oqd17C6GB4oXyEBDj0vZnL7SUoCAOAWELPeC8CTUwMwYJKoZI
+hvcNAQcBMBQGCCqGSIb3DQMHBAhEowTkot3a7oAQFD//J/IhFnk+JbkH7HZQFA==
+-----END PKCS7-----
+-----BEGIN CERTIFICATE-----
+MIIB1jCCAUGgAwIBAgIFAMv4K9YwCwYJKoZIhvcNAQELMCkxEDAOBgNVBAoTB0Fj
+bWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0xNTA1MDYwMzU2NDBaFw0x
+NjA1MDYwMzU2NDBaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBT
+bm93MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK6NU0R0eiCYVquU4RcjKc
+LzGfx0aa1lMr2TnLQUSeLFZHFxsyyMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg
+8+Zg2r8xnnney7abxcuv0uATWSIeKlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP
++Zxp2ni5qHNraf3wE2VPIQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCAKAwCwYJKoZI
+hvcNAQELA4GBAIr2F7wsqmEU/J/kLyrCgEVXgaV/sKZq4pPNnzS0tBYk8fkV3V18
+sBJyHKRLL/wFZASvzDcVGCplXyMdAOCyfd8jO3F9Ac/xdlz10RrHJT75hNu3a7/n
+9KNwKhfN4A1CQv2x372oGjRhCW5bHNCWx4PIVeNzCyq/KZhyY9sxHE6f
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIICXgIBAAKBgQDK6NU0R0eiCYVquU4RcjKcLzGfx0aa1lMr2TnLQUSeLFZHFxsy
+yMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg8+Zg2r8xnnney7abxcuv0uATWSIe
+KlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP+Zxp2ni5qHNraf3wE2VPIQIDAQAB
+AoGBALyvnSt7KUquDen7nXQtvJBudnf9KFPt//OjkdHHxNZNpoF/JCSqfQeoYkeu
+MdAVYNLQGMiRifzZz4dDhA9xfUAuy7lcGQcMCxEQ1dwwuFaYkawbS0Tvy2PFlq2d
+H5/HeDXU4EDJ3BZg0eYj2Bnkt1sJI35UKQSxblQ0MY2q0uFBAkEA5MMOogkgUx1C
+67S1tFqMUSM8D0mZB0O5vOJZC5Gtt2Urju6vywge2ArExWRXlM2qGl8afFy2SgSv
+Xk5eybcEiQJBAOMRwwbEoW5NYHuFFbSJyWll4n71CYuWuQOCzehDPyTb80WFZGLV
+i91kFIjeERyq88eDE5xVB3ZuRiXqaShO/9kCQQCKOEkpInaDgZSjskZvuJ47kByD
+6CYsO4GIXQMMeHML8ncFH7bb6AYq5ybJVb2NTU7QLFJmfeYuhvIm+xdOreRxAkEA
+o5FC5Jg2FUfFzZSDmyZ6IONUsdF/i78KDV5nRv1R+hI6/oRlWNCtTNBv/lvBBd6b
+dseUE9QoaQZsn5lpILEvmQJAZ0B+Or1rAYjnbjnUhdVZoy9kC4Zov+4UH3N/BtSy
+KJRWUR0wTWfZBPZ5hAYZjTBEAFULaYCXlQKsODSp0M1aQA==
+-----END PRIVATE KEY-----`
+
+var EC2IdentityDocumentFixture = `
+-----BEGIN PKCS7-----
+MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA
+JIAEggGmewogICJwcml2YXRlSXAiIDogIjE3Mi4zMC4wLjI1MiIsCiAgImRldnBh
+eVByb2R1Y3RDb2RlcyIgOiBudWxsLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1
+cy1lYXN0LTFhIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3Rh
+bmNlSWQiIDogImktZjc5ZmU1NmMiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVs
+bCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIg
+OiAiMTIxNjU5MDE0MzM0IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwK
+ICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDhUMDM6MDE6MzhaIiwKICAiYXJj
+aGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJy
+YW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAA
+AAAxggEYMIIBFAIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5n
+dG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2Vi
+IFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0B
+CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDgwMzAxNDRaMCMG
+CSqGSIb3DQEJBDEWBBTuUc28eBXmImAautC+wOjqcFCBVjAJBgcqhkjOOAQDBC8w
+LQIVAKA54NxGHWWCz5InboDmY/GHs33nAhQ6O/ZI86NwjA9Vz3RNMUJrUPU5tAAA
+AAAAAA==
+-----END PKCS7-----
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw
+FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD
+VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z
+ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u
+IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl
+cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e
+ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3
+VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P
+hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j
+k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U
+hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF
+lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf
+MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW
+MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw
+vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw
+7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K
+-----END CERTIFICATE-----`
+
+var AppStoreRecieptFixture = `
+-----BEGIN PKCS7-----
+MIITtgYJKoZIhvcNAQcCoIITpzCCE6MCAQExCzAJBgUrDgMCGgUAMIIDVwYJKoZI
+hvcNAQcBoIIDSASCA0QxggNAMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC
+AQEEAwIBADALAgEDAgEBBAMMATEwCwIBCwIBAQQDAgEAMAsCAQ8CAQEEAwIBADAL
+AgEQAgEBBAMCAQAwCwIBGQIBAQQDAgEDMAwCAQoCAQEEBBYCNCswDAIBDgIBAQQE
+AgIAjTANAgENAgEBBAUCAwFgvTANAgETAgEBBAUMAzEuMDAOAgEJAgEBBAYCBFAy
+NDcwGAIBAgIBAQQQDA5jb20uemhpaHUudGVzdDAYAgEEAgECBBCS+ZODNMHwT1Nz
+gWYDXyWZMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQU4nRh
+YCEZx70Flzv7hvJRjJZckYIwHgIBDAIBAQQWFhQyMDE2LTA3LTIzVDA2OjIxOjEx
+WjAeAgESAgEBBBYWFDIwMTMtMDgtMDFUMDc6MDA6MDBaMD0CAQYCAQEENbR21I+a
+8+byMXo3NPRoDWQmSXQF2EcCeBoD4GaL//ZCRETp9rGFPSg1KekCP7Kr9HAqw09m
+MEICAQcCAQEEOlVJozYYBdugybShbiiMsejDMNeCbZq6CrzGBwW6GBy+DGWxJI91
+Y3ouXN4TZUhuVvLvN1b0m5T3ggQwggFaAgERAgEBBIIBUDGCAUwwCwICBqwCAQEE
+AhYAMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgaz
+AgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAM
+AgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQEwDAICBq4CAQEEAwIBADAMAgIGrwIB
+AQQDAgEAMAwCAgaxAgEBBAMCAQAwGwICBqcCAQEEEgwQMTAwMDAwMDIyNTMyNTkw
+MTAbAgIGqQIBAQQSDBAxMDAwMDAwMjI1MzI1OTAxMB8CAgaoAgEBBBYWFDIwMTYt
+MDctMjNUMDY6MjE6MTFaMB8CAgaqAgEBBBYWFDIwMTYtMDctMjNUMDY6MjE6MTFa
+MCACAgamAgEBBBcMFWNvbS56aGlodS50ZXN0LnRlc3RfMaCCDmUwggV8MIIEZKAD
+AgECAggO61eH554JjTANBgkqhkiG9w0BAQUFADCBljELMAkGA1UEBhMCVVMxEzAR
+BgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZl
+bG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv
+cGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNTExMTMw
+MjE1MDlaFw0yMzAyMDcyMTQ4NDdaMIGJMTcwNQYDVQQDDC5NYWMgQXBwIFN0b3Jl
+IGFuZCBpVHVuZXMgU3RvcmUgUmVjZWlwdCBTaWduaW5nMSwwKgYDVQQLDCNBcHBs
+ZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczETMBEGA1UECgwKQXBwbGUg
+SW5jLjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQClz4H9JaKBW9aH7SPaMxyO4iPApcQmyz3Gn+xKDVWG/6QC15fKOVRtfX+yVBid
+xCxScY5ke4LOibpJ1gjltIhxzz9bRi7GxB24A6lYogQ+IXjV27fQjhKNg0xbKmg3
+k8LyvR7E0qEMSlhSqxLj7d0fmBWQNS3CzBLKjUiB91h4VGvojDE2H0oGDEdU8zeQ
+uLKSiX1fpIVK4cCc4Lqku4KXY/Qrk8H9Pm/KwfU8qY9SGsAlCnYO3v6Z/v/Ca/Vb
+XqxzUUkIVonMQ5DMjoEC0KCXtlyxoWlph5AQaCYmObgdEHOwCl3Fc9DfdjvYLdmI
+HuPsB8/ijtDT+iZVge/iA0kjAgMBAAGjggHXMIIB0zA/BggrBgEFBQcBAQQzMDEw
+LwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcjA0
+MB0GA1UdDgQWBBSRpJz8xHa3n6CK9E31jzZd7SsEhTAMBgNVHRMBAf8EAjAAMB8G
+A1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHgYDVR0gBIIBFTCCAREw
+ggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9u
+IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j
+ZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25k
+aXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0
+aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3
+LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wDgYDVR0PAQH/BAQDAgeA
+MBAGCiqGSIb3Y2QGCwEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQANphvTLj3jWysH
+bkKWbNPojEMwgl/gXNGNvr0PvRr8JZLbjIXDgFnf4+LXLgUUrA3btrj+/DUufMut
+F2uOfx/kd7mxZ5W0E16mGYZ2+FogledjjA9z/Ojtxh+umfhlSFyg4Cg6wBA3Lbmg
+BDkfc7nIBf3y3n8aKipuKwH8oCBc2et9J6Yz+PWY4L5E27FMZ/xuCk/J4gao0pfz
+p45rUaJahHVl0RYEYuPBX/UIqc9o2ZIAycGMs/iNAGS6WGDAfK+PdcppuVsq1h1o
+bphC9UynNxmbzDscehlD86Ntv0hgBgw2kivs3hi1EdotI9CO/KBpnBcbnoB7OUdF
+MGEvxxOoMIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjEL
+MAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxl
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENB
+MB4XDTEzMDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVT
+MRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUg
+RGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERl
+dmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0
+U3rOfGOAYXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkV
+CBmsqtsqMu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8
+V25nNYB2NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHl
+d0WNUEi6Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1q
+arunFjVg0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGj
+gaYwgaMwHQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQF
+MAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcw
+JTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/
+BAQDAgGGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Z
+viz1smwvj+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/N
+w0Uwj6ODDc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJ
+TleMa1s8Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1V
+AKmuu0swruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur
++cmV6U/kTecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxR
+pVzscYqCtGwPDBUfMIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQsw
+CQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0Ew
+HhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzET
+MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne
++Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjcz
+y8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQ
+Z48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCS
+C7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINB
+hzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIB
+djAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9Bp
+R5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/
+CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcC
+ARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCB
+thqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFz
+c3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJk
+IHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5
+IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3
+DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizU
+sZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJ
+fBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr
+1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltk
+wGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIq
+xw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUhMYIByzCCAccCAQEwgaMwgZYxCzAJ
+BgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBX
+b3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29y
+bGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkCCA7rV4fnngmNMAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEggEAasPtnide
+NWyfUtewW9OSgcQA8pW+5tWMR0469cBPZR84uJa0gyfmPspySvbNOAwnrwzZHYLa
+ujOxZLip4DUw4F5s3QwUa3y4BXpF4J+NSn9XNvxNtnT/GcEQtCuFwgJ0o3F0ilhv
+MTHrwiwyx/vr+uNDqlORK8lfK+1qNp+A/kzh8eszMrn4JSeTh9ZYxLHE56WkTQGD
+VZXl0gKgxSOmDrcp1eQxdlymzrPv9U60wUJ0bkPfrU9qZj3mJrmrkQk61JTe3j6/
+QfjfFBG9JG2mUmYQP1KQ3SypGHzDW8vngvsGu//tNU0NFfOqQu4bYU4VpQl0nPtD
+4B85NkrgvQsWAQ==
+-----END PKCS7-----`
diff --git a/vendor/github.com/fullsailor/pkcs7/x509.go b/vendor/github.com/fullsailor/pkcs7/x509.go
new file mode 100644
index 0000000000000000000000000000000000000000..195fd0e4bb0ba69b7d8ad3052516041bc1c4eca8
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/x509.go
@@ -0,0 +1,133 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the go/golang LICENSE file.
+
+package pkcs7
+
+// These are private constants and functions from the crypto/x509 package that
+// are useful when dealing with signatures verified by x509 certificates
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+)
+
+var (
+	oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
+	oidSignatureMD5WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
+	oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
+	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
+	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
+	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
+	oidSignatureRSAPSS          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
+	oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
+	oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
+	oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
+	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
+	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
+	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
+
+	oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
+	oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
+	oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
+
+	oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
+
+	// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
+	// but it's specified by ISO. Microsoft's makecert.exe has been known
+	// to produce certificates with this OID.
+	oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
+)
+
+var signatureAlgorithmDetails = []struct {
+	algo       x509.SignatureAlgorithm
+	name       string
+	oid        asn1.ObjectIdentifier
+	pubKeyAlgo x509.PublicKeyAlgorithm
+	hash       crypto.Hash
+}{
+	{x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
+	{x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
+	{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
+	{x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
+	{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
+	{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
+	{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
+	{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
+	{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
+	{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
+	{x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
+	{x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
+	{x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
+	{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
+	{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
+	{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
+}
+
+// pssParameters reflects the parameters in an AlgorithmIdentifier that
+// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3
+type pssParameters struct {
+	// The following three fields are not marked as
+	// optional because the default values specify SHA-1,
+	// which is no longer suitable for use in signatures.
+	Hash         pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
+	MGF          pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
+	SaltLength   int                      `asn1:"explicit,tag:2"`
+	TrailerField int                      `asn1:"optional,explicit,tag:3,default:1"`
+}
+
+// asn1.NullBytes is not available prior to Go 1.9
+var nullBytes = []byte{5, 0}
+
+func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
+	if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
+		for _, details := range signatureAlgorithmDetails {
+			if ai.Algorithm.Equal(details.oid) {
+				return details.algo
+			}
+		}
+		return x509.UnknownSignatureAlgorithm
+	}
+
+	// RSA PSS is special because it encodes important parameters
+	// in the Parameters.
+
+	var params pssParameters
+	if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, &params); err != nil {
+		return x509.UnknownSignatureAlgorithm
+	}
+
+	var mgf1HashFunc pkix.AlgorithmIdentifier
+	if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
+		return x509.UnknownSignatureAlgorithm
+	}
+
+	// PSS is greatly overburdened with options. This code forces
+	// them into three buckets by requiring that the MGF1 hash
+	// function always match the message hash function (as
+	// recommended in
+	// https://tools.ietf.org/html/rfc3447#section-8.1), that the
+	// salt length matches the hash length, and that the trailer
+	// field has the default value.
+	if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) ||
+		!params.MGF.Algorithm.Equal(oidMGF1) ||
+		!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
+		!bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) ||
+		params.TrailerField != 1 {
+		return x509.UnknownSignatureAlgorithm
+	}
+
+	switch {
+	case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
+		return x509.SHA256WithRSAPSS
+	case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
+		return x509.SHA384WithRSAPSS
+	case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
+		return x509.SHA512WithRSAPSS
+	}
+
+	return x509.UnknownSignatureAlgorithm
+}
diff --git a/vendor/github.com/zakjan/cert-chain-resolver/LICENCE b/vendor/github.com/zakjan/cert-chain-resolver/LICENCE
new file mode 100644
index 0000000000000000000000000000000000000000..0b5b3cb96be6d5029aefdbde966df908a9979074
--- /dev/null
+++ b/vendor/github.com/zakjan/cert-chain-resolver/LICENCE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jan Žák (http://zakjan.cz)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/zakjan/cert-chain-resolver/certUtil/chain.go b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/chain.go
new file mode 100644
index 0000000000000000000000000000000000000000..59e24b7b64f8be5a4b82e2c5177e121462e64ca9
--- /dev/null
+++ b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/chain.go
@@ -0,0 +1,75 @@
+package certUtil
+
+import (
+	"crypto/x509"
+	"io/ioutil"
+	"net/http"
+)
+
+func isSelfSigned(cert *x509.Certificate) bool {
+	return cert.CheckSignatureFrom(cert) == nil
+}
+
+func isChainRootNode(cert *x509.Certificate) bool {
+	if isSelfSigned(cert) {
+		return true
+	}
+	return false
+}
+
+func FetchCertificateChain(cert *x509.Certificate) ([]*x509.Certificate, error) {
+	var certs []*x509.Certificate
+
+	certs = append(certs, cert)
+
+	for certs[len(certs)-1].IssuingCertificateURL != nil {
+		parentURL := certs[len(certs)-1].IssuingCertificateURL[0]
+
+		resp, err := http.Get(parentURL)
+		if resp != nil {
+			defer resp.Body.Close()
+		}
+		if err != nil {
+			return nil, err
+		}
+
+		data, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			return nil, err
+		}
+
+		cert, err := DecodeCertificate(data)
+		if err != nil {
+			return nil, err
+		}
+
+		if isChainRootNode(cert) {
+			break
+		}
+
+		certs = append(certs, cert)
+	}
+
+	return certs, nil
+}
+
+func AddRootCA(certs []*x509.Certificate) ([]*x509.Certificate, error) {
+	lastCert := certs[len(certs)-1]
+
+	chains, err := lastCert.Verify(x509.VerifyOptions{})
+	if err != nil {
+		if _, e := err.(x509.UnknownAuthorityError); e {
+			return certs, nil
+		}
+		return nil, err
+	}
+
+	for _, cert := range chains[0] {
+		if lastCert.Equal(cert) {
+			continue
+		}
+		certs = append(certs, cert)
+	}
+
+	return certs, nil
+}
diff --git a/vendor/github.com/zakjan/cert-chain-resolver/certUtil/chain_test.go b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/chain_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6dbe4178abe9c5604bba95b0ef357d35f05e73fb
--- /dev/null
+++ b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/chain_test.go
@@ -0,0 +1,33 @@
+package certUtil
+
+import (
+	"crypto/x509"
+	"github.com/stretchr/testify/assert"
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+func TestFetchCertificateChain(t *testing.T) {
+	file, err := os.Open("../tests/comodo.der.crt")
+	if err != nil {
+		t.Error(err)
+	}
+
+	data, err := ioutil.ReadAll(file)
+	if err != nil {
+		t.Error(err)
+	}
+
+	cert, err := x509.ParseCertificate(data)
+	if err != nil {
+		t.Error(err)
+	}
+
+	certs, err := FetchCertificateChain(cert)
+	if err != nil {
+		t.Error(err)
+	}
+
+	assert.Equal(t, 3, len(certs))
+}
diff --git a/vendor/github.com/zakjan/cert-chain-resolver/certUtil/io.go b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/io.go
new file mode 100644
index 0000000000000000000000000000000000000000..40818ac515e40c876b00a4315e820b35195d8b41
--- /dev/null
+++ b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/io.go
@@ -0,0 +1,110 @@
+package certUtil
+
+import (
+	"bytes"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+	"github.com/fullsailor/pkcs7"
+)
+
+var pemStart = []byte("-----BEGIN ")
+var certBlockType = "CERTIFICATE"
+
+func IsPEM(data []byte) bool {
+	return bytes.HasPrefix(data, pemStart)
+}
+
+func DecodeCertificates(data []byte) ([]*x509.Certificate, error) {
+	if IsPEM(data) {
+		var certs []*x509.Certificate
+
+		for len(data) > 0 {
+			var block *pem.Block
+
+			block, data = pem.Decode(data)
+			if block == nil {
+				return nil, errors.New("Invalid certificate.")
+			}
+			if block.Type != certBlockType {
+				return nil, errors.New("Invalid certificate.")
+			}
+
+			cert, err := x509.ParseCertificate(block.Bytes)
+			if err != nil {
+				return nil, errors.New("Invalid certificate.")
+			}
+
+			certs = append(certs, cert)
+		}
+
+		return certs, nil
+	} else {
+		certs, err := x509.ParseCertificates(data)
+		if err != nil {
+			return nil, errors.New("Invalid certificate.")
+		}
+
+		return certs, nil
+	}
+}
+
+func DecodeCertificate(data []byte) (*x509.Certificate, error) {
+	if IsPEM(data) {
+		block, _ := pem.Decode(data)
+		if block == nil {
+			return nil, errors.New("Invalid certificate.")
+		}
+		if block.Type != certBlockType {
+			return nil, errors.New("Invalid certificate.")
+		}
+
+		data = block.Bytes
+	}
+
+	cert, err := x509.ParseCertificate(data)
+	if err == nil {
+		return cert, nil
+	}
+
+	p, err := pkcs7.Parse(data)
+	if err == nil {
+		return p.Certificates[0], nil
+	}
+
+	return nil, errors.New("Invalid certificate.")
+}
+
+func EncodeCertificate(cert *x509.Certificate) []byte {
+	block := pem.Block{
+		Type:  certBlockType,
+		Bytes: cert.Raw,
+	}
+	return pem.EncodeToMemory(&block)
+}
+
+func EncodeCertificateDER(cert *x509.Certificate) []byte {
+	return cert.Raw
+}
+
+func EncodeCertificates(certs []*x509.Certificate) []byte {
+	var data []byte
+
+	for _, cert := range certs {
+		data2 := EncodeCertificate(cert)
+		data = append(data, data2...)
+	}
+
+	return data
+}
+
+func EncodeCertificatesDER(certs []*x509.Certificate) []byte {
+	var data []byte
+
+	for _, cert := range certs {
+		data2 := EncodeCertificateDER(cert)
+		data = append(data, data2...)
+	}
+
+	return data
+}
diff --git a/vendor/github.com/zakjan/cert-chain-resolver/certUtil/io_test.go b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/io_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c897f9ba08633443e57d53b1ca00931a10a7df4b
--- /dev/null
+++ b/vendor/github.com/zakjan/cert-chain-resolver/certUtil/io_test.go
@@ -0,0 +1,11 @@
+package certUtil
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestIsPEM(t *testing.T) {
+	assert.True(t, IsPEM([]byte("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----")))
+	assert.False(t, IsPEM([]byte("xxx")))
+}
diff --git a/vendor/github.com/zakjan/cert-chain-resolver/main.go b/vendor/github.com/zakjan/cert-chain-resolver/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ee9a9400fa4d8916e38eb3032dad1e25b3fb5f8
--- /dev/null
+++ b/vendor/github.com/zakjan/cert-chain-resolver/main.go
@@ -0,0 +1,146 @@
+package main
+
+import (
+	"fmt"
+	"github.com/urfave/cli"
+	"github.com/zakjan/cert-chain-resolver/certUtil"
+	"io/ioutil"
+	"os"
+)
+
+func openInputFile(filename string) (*os.File, error) {
+	if filename == "" {
+		return os.Stdin, nil
+	}
+
+	file, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	return file, nil
+}
+
+func openOutputFile(filename string) (*os.File, error) {
+	if filename == "" {
+		return os.Stdout, nil
+	}
+
+	file, err := os.Create(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	return file, nil
+}
+
+func run(inputFilename string, outputFilename string, outputIntermediateOnly bool,
+	outputDerFormat bool, includeSystem bool) error {
+	inputFile, err := openInputFile(inputFilename)
+	if err != nil {
+		return err
+	}
+
+	outputFile, err := openOutputFile(outputFilename)
+	if err != nil {
+		return err
+	}
+
+	data, err := ioutil.ReadAll(inputFile)
+	if err != nil {
+		return err
+	}
+
+	cert, err := certUtil.DecodeCertificate(data)
+	if err != nil {
+		return err
+	}
+
+	certs, err := certUtil.FetchCertificateChain(cert)
+	if err != nil {
+		return err
+	}
+
+	if includeSystem {
+		certs, err = certUtil.AddRootCA(certs)
+		if err != nil {
+			return err
+		}
+	}
+
+	if outputIntermediateOnly {
+		certs = certs[1:]
+	}
+
+	if !outputDerFormat {
+		data = certUtil.EncodeCertificates(certs)
+	} else {
+		data = certUtil.EncodeCertificatesDER(certs)
+	}
+
+	_, err = outputFile.Write(data)
+	if err != nil {
+		return err
+	}
+
+	for i, cert := range certs {
+		fmt.Fprintf(os.Stderr, "%d: %s\n", i+1, cert.Subject.CommonName)
+	}
+	fmt.Fprintf(os.Stderr, "Certificate chain complete.\n")
+	fmt.Fprintf(os.Stderr, "Total %d certificate(s) found.\n", len(certs))
+
+	return nil
+}
+
+func main() {
+	var (
+		inputFilename          string
+		outputFilename         string
+		outputIntermediateOnly bool
+		outputDerFormat        bool
+		includeSystem          bool
+	)
+
+	app := cli.NewApp()
+	app.Usage = "SSL certificate chain resolver"
+	app.ArgsUsage = "[INPUT_FILE]"
+	app.Version = "1.0.2"
+	app.HideHelp = true
+	app.Flags = []cli.Flag{
+		cli.StringFlag{
+			Name:        "output, o",
+			Usage:       "output to `OUTPUT_FILE` (default: stdout)",
+			Destination: &outputFilename,
+		},
+		cli.BoolFlag{
+			Name:        "intermediate-only, i",
+			Usage:       "output intermediate certificates only",
+			Destination: &outputIntermediateOnly,
+		},
+		cli.BoolFlag{
+			Name:        "der, d",
+			Usage:       "output DER format",
+			Destination: &outputDerFormat,
+		},
+		cli.BoolFlag{
+			Name:        "include-system, s",
+			Usage:       "include root CA from system in output",
+			Destination: &includeSystem,
+		},
+	}
+	app.Action = func(c *cli.Context) error {
+		args := c.Args()
+		if len(args) > 0 {
+			inputFilename = args[0]
+		}
+
+		err := run(inputFilename, outputFilename, outputIntermediateOnly, outputDerFormat, includeSystem)
+		return err
+	}
+
+	err := app.Run(os.Args)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+}
diff --git a/vendor/github.com/zakjan/cert-chain-resolver/tests/http-server.go b/vendor/github.com/zakjan/cert-chain-resolver/tests/http-server.go
new file mode 100644
index 0000000000000000000000000000000000000000..88da39d0714296cba61243157a3e60f525daf6a3
--- /dev/null
+++ b/vendor/github.com/zakjan/cert-chain-resolver/tests/http-server.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+	"fmt"
+	"net/http"
+	"os"
+)
+
+func main() {
+	fs := http.FileServer(http.Dir(os.Args[1]))
+	http.Handle("/", fs)
+
+	err := http.ListenAndServe(":8080", nil)
+
+	if err != nil {
+		fmt.Println(err)
+	}
+}