Ejemplo n.º 1
0
func (c AesContentCipher) decrypt(cek, iv, ciphertxt, tag, aad []byte) (plaintext []byte, err error) {
	aead, err := c.AeadFetch(cek)
	if err != nil {
		debug.Printf("AeadFetch failed for %v: %s", cek, err)
		return nil, err
	}

	// Open may panic (argh!), so protect ourselves from that
	defer func() {
		if e := recover(); e != nil {
			switch e.(type) {
			case error:
				err = e.(error)
			case string:
				err = errors.New(e.(string))
			default:
				err = fmt.Errorf("%s", e)
			}
			return
		}
	}()

	combined := make([]byte, len(ciphertxt)+len(tag))
	copy(combined, ciphertxt)
	copy(combined[len(ciphertxt):], tag)

	debug.Printf("AesContentCipher.decrypt: combined = %x (%d)", combined, len(combined))

	plaintext, err = aead.Open(nil, iv, combined, aad)
	return
}
Ejemplo n.º 2
0
// Seal fulfills the crypto.AEAD interface
func (c AesCbcHmac) Seal(dst, nonce, plaintext, data []byte) []byte {
	ctlen := len(plaintext)
	ciphertext := make([]byte, ctlen+c.Overhead())[:ctlen]
	copy(ciphertext, plaintext)
	ciphertext = padbuf.PadBuffer(ciphertext).Pad(c.blockCipher.BlockSize())

	cbc := cipher.NewCBCEncrypter(c.blockCipher, nonce)
	cbc.CryptBlocks(ciphertext, ciphertext)

	authtag := c.ComputeAuthTag(data, nonce, ciphertext)

	retlen := len(dst) + len(ciphertext) + len(authtag)

	ret := ensureSize(dst, retlen)
	out := ret[len(dst):]
	n := copy(out, ciphertext)
	n += copy(out[n:], authtag)

	if debug.Enabled {
		debug.Printf("Seal: ciphertext = %x (%d)\n", ciphertext, len(ciphertext))
		debug.Printf("Seal: authtag    = %x (%d)\n", authtag, len(authtag))
		debug.Printf("Seal: ret        = %x (%d)\n", ret, len(ret))
	}
	return ret
}
Ejemplo n.º 3
0
func New(key []byte, f BlockCipherFunc) (*AesCbcHmac, error) {
	keysize := len(key) / 2
	ikey := key[:keysize]
	ekey := key[keysize:]

	debug.Printf("New: cek (key)             = %x (%d)\n", key, len(key))
	debug.Printf("New: ikey                  = %x (%d)\n", ikey, len(ikey))
	debug.Printf("New: ekey                  = %x (%d)\n", ekey, len(ekey))

	bc, err := f(ekey)
	if err != nil {
		return nil, err
	}

	var hfunc func() hash.Hash
	switch keysize {
	case 16:
		hfunc = sha256.New
	case 24:
		hfunc = sha512.New384
	case 32:
		hfunc = sha512.New
	default:
		return nil, errors.New("unsupported key size")
	}

	return &AesCbcHmac{
		blockCipher:  bc,
		hash:         hfunc,
		integrityKey: ikey,
		keysize:      keysize,
		tagsize:      NonceSize,
	}, nil
}
Ejemplo n.º 4
0
func parseCompact(buf []byte) (*Message, error) {
	debug.Printf("Parse(Compact): buf = '%s'", buf)
	parts := bytes.Split(buf, []byte{'.'})
	if len(parts) != 5 {
		return nil, ErrInvalidCompactPartsCount
	}

	hdrbuf := buffer.Buffer{}
	if err := hdrbuf.Base64Decode(parts[0]); err != nil {
		return nil, err
	}
	debug.Printf("hdrbuf = %s", hdrbuf)

	hdr := NewHeader()
	if err := json.Unmarshal(hdrbuf, hdr); err != nil {
		return nil, err
	}

	// We need the protected header to contain the content encryption
	// algorithm. XXX probably other headers need to go there too
	protected := NewEncodedHeader()
	protected.ContentEncryption = hdr.ContentEncryption
	hdr.ContentEncryption = ""

	enckeybuf := buffer.Buffer{}
	if err := enckeybuf.Base64Decode(parts[1]); err != nil {
		return nil, err
	}

	ivbuf := buffer.Buffer{}
	if err := ivbuf.Base64Decode(parts[2]); err != nil {
		return nil, err
	}

	ctbuf := buffer.Buffer{}
	if err := ctbuf.Base64Decode(parts[3]); err != nil {
		return nil, err
	}

	tagbuf := buffer.Buffer{}
	if err := tagbuf.Base64Decode(parts[4]); err != nil {
		return nil, err
	}

	m := NewMessage()
	m.AuthenticatedData.SetBytes(hdrbuf.Bytes())
	m.ProtectedHeader = protected
	m.Tag = tagbuf
	m.CipherText = ctbuf
	m.InitializationVector = ivbuf
	m.Recipients = []Recipient{
		Recipient{
			Header:       hdr,
			EncryptedKey: enckeybuf,
		},
	}
	return m, nil
}
Ejemplo n.º 5
0
func (c GenericContentCrypt) Encrypt(cek, plaintext, aad []byte) ([]byte, []byte, []byte, error) {
	debug.Printf("ContentCrypt.Encrypt: cek        = %x", cek)
	debug.Printf("ContentCrypt.Encrypt: ciphertext = %x", plaintext)
	debug.Printf("ContentCrypt.Encrypt: aad        = %x", aad)
	iv, encrypted, tag, err := c.cipher.encrypt(cek, plaintext, aad)
	if err != nil {
		debug.Printf("cipher.encrypt failed")
		return nil, nil, nil, err
	}

	return iv, encrypted, tag, nil
}
Ejemplo n.º 6
0
func doMessageVerify(alg jwa.SignatureAlgorithm, v payloadVerifier, m *Message) error {
	var err error
	payload, err := m.Payload.Base64Encode()
	if err != nil {
		return err
	}
	for _, sig := range m.Signatures {
		if sig.ProtectedHeader.Algorithm != alg {
			continue
		}

		var phbuf []byte
		if sig.ProtectedHeader.Source.Len() > 0 {
			phbuf, err = sig.ProtectedHeader.Source.Base64Encode()
			if err != nil {
				continue
			}
		} else {
			phbuf, err = sig.ProtectedHeader.Base64Encode()
			if err != nil {
				continue
			}
		}
		siv := append(append(phbuf, '.'), payload...)

		if debug.Enabled {
			debug.Printf("siv = '%s'", siv)
		}
		if err := v.payloadVerify(siv, sig.Signature.Bytes()); err != nil {
			if debug.Enabled {
				debug.Printf("Payload verify failed: %s", err)
			}
			continue
		}

		return nil
	}

	return errors.New("none of the signatures could be verified")
}
Ejemplo n.º 7
0
func (d RSAOAEPKeyDecrypt) KeyDecrypt(enckey []byte) ([]byte, error) {
	debug.Printf("START OAEP.KeyDecrypt")
	var hash hash.Hash
	switch d.alg {
	case jwa.RSA_OAEP:
		hash = sha1.New()
	case jwa.RSA_OAEP_256:
		hash = sha256.New()
	default:
		return nil, errors.New("failed to generate key encrypter for RSA-OAEP: RSA_OAEP/RSA_OAEP_256 required")
	}
	return rsa.DecryptOAEP(hash, rand.Reader, d.privkey, enckey, []byte{})
}
Ejemplo n.º 8
0
// Open fulfills the crypto.AEAD interface
func (c AesCbcHmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
	if len(ciphertext) < c.keysize {
		return nil, errors.New("invalid ciphertext (too short)")
	}

	tagOffset := len(ciphertext) - c.tagsize
	if tagOffset%c.blockCipher.BlockSize() != 0 {
		return nil, fmt.Errorf(
			"invalid ciphertext (invalid length: %d %% %d != 0)",
			tagOffset,
			c.blockCipher.BlockSize(),
		)
	}
	tag := ciphertext[tagOffset:]
	ciphertext = ciphertext[:tagOffset]

	expectedTag := c.ComputeAuthTag(data, nonce, ciphertext)
	if subtle.ConstantTimeCompare(expectedTag, tag) != 1 {
		if debug.Enabled {
			debug.Printf("provided tag = %x\n", tag)
			debug.Printf("expected tag = %x\n", expectedTag)
		}
		return nil, errors.New("invalid ciphertext (tag mismatch)")
	}

	cbc := cipher.NewCBCDecrypter(c.blockCipher, nonce)
	buf := make([]byte, tagOffset)
	cbc.CryptBlocks(buf, ciphertext)

	plaintext, err := padbuf.PadBuffer(buf).Unpad(c.blockCipher.BlockSize())
	if err != nil {
		return nil, err
	}
	ret := ensureSize(dst, len(plaintext))
	out := ret[len(dst):]
	copy(out, plaintext)
	return ret, nil
}
Ejemplo n.º 9
0
// KeyDecrypt decryptes the encrypted key using RSA PKCS1v1.5
func (d RSAPKCS15KeyDecrypt) KeyDecrypt(enckey []byte) ([]byte, error) {
	if debug.Enabled {
		debug.Printf("START PKCS.KeyDecrypt")
	}
	// Hey, these notes and workarounds were stolen from go-jose
	defer func() {
		// DecryptPKCS1v15SessionKey sometimes panics on an invalid payload
		// because of an index out of bounds error, which we want to ignore.
		// This has been fixed in Go 1.3.1 (released 2014/08/13), the recover()
		// only exists for preventing crashes with unpatched versions.
		// See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k
		// See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33
		_ = recover()
	}()

	// Perform some input validation.
	expectedlen := d.privkey.PublicKey.N.BitLen() / 8
	if expectedlen != len(enckey) {
		// Input size is incorrect, the encrypted payload should always match
		// the size of the public modulus (e.g. using a 2048 bit key will
		// produce 256 bytes of output). Reject this since it's invalid input.
		return nil, fmt.Errorf(
			"input size for key decrypt is incorrect (expected %d, got %d)",
			expectedlen,
			len(enckey),
		)
	}

	var err error

	bk, err := d.generator.KeyGenerate()
	if err != nil {
		return nil, errors.New("failed to generate key")
	}
	cek := bk.Bytes()

	// When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to
	// prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing
	// the Million Message Attack on Cryptographic Message Syntax". We are
	// therefore deliberatly ignoring errors here.
	err = rsa.DecryptPKCS1v15SessionKey(rand.Reader, d.privkey, enckey, cek)
	if err != nil {
		return nil, err
	}

	return cek, nil
}
Ejemplo n.º 10
0
func (c AesContentCipher) encrypt(cek, plaintext, aad []byte) (iv, ciphertext, tag []byte, err error) {
	var aead cipher.AEAD
	aead, err = c.AeadFetch(cek)
	if err != nil {
		if debug.Enabled {
			debug.Printf("AeadFetch failed: %s", err)
		}
		err = fmt.Errorf("failed to fetch AEAD: %s", err)
		return
	}

	// Seal may panic (argh!), so protect ourselves from that
	defer func() {
		if e := recover(); e != nil {
			switch e.(type) {
			case error:
				err = e.(error)
			case string:
				err = errors.New(e.(string))
			default:
				err = fmt.Errorf("%s", e)
			}
			return
		}
	}()

	var bs ByteSource
	if c.NonceGenerator == nil {
		bs, err = NewRandomKeyGenerate(aead.NonceSize()).KeyGenerate()
	} else {
		bs, err = c.NonceGenerator.KeyGenerate()
	}
	if err != nil {
		return
	}
	iv = bs.Bytes()

	combined := aead.Seal(nil, iv, plaintext, aad)
	tagoffset := len(combined) - c.TagSize()
	if debug.Enabled {
		debug.Printf("tagsize = %d", c.TagSize())
	}
	tag = combined[tagoffset:]
	ciphertext = make([]byte, tagoffset)
	copy(ciphertext, combined[:tagoffset])

	if debug.Enabled {
		debug.Printf("encrypt: combined   = %x (%d)\n", combined, len(combined))
		debug.Printf("encrypt: ciphertext = %x (%d)\n", ciphertext, len(ciphertext))
		debug.Printf("encrypt: tag        = %x (%d)\n", tag, len(tag))
		debug.Printf("finally ciphertext = %x\n", ciphertext)
	}
	return
}
Ejemplo n.º 11
0
// parseCompact parses a JWS value serialized via compact serialization.
func parseCompact(buf []byte) (*Message, error) {
	parts := bytes.Split(buf, []byte{'.'})
	if len(parts) != 3 {
		return nil, ErrInvalidCompactPartsCount
	}

	enc := base64.RawURLEncoding

	hdrbuf, err := buffer.FromBase64(parts[0])
	if err != nil {
		return nil, err
	}

	debug.Printf("hdrbuf = %s", hdrbuf.Bytes())
	hdr := &EncodedHeader{Header: NewHeader()}
	if err := json.Unmarshal(hdrbuf.Bytes(), hdr.Header); err != nil {
		return nil, err
	}
	hdr.Source = hdrbuf

	payload, err := buffer.FromBase64(parts[1])
	if err != nil {
		return nil, err
	}

	signature := make([]byte, enc.DecodedLen(len(parts[2])))
	if _, err := enc.Decode(signature, parts[2]); err != nil {
		return nil, err
	}
	signature = bytes.TrimRight(signature, "\x00")

	s := NewSignature()
	s.Signature = signature
	s.ProtectedHeader = hdr
	m := &Message{
		Payload:    buffer.Buffer(payload),
		Signatures: []Signature{*s},
	}
	return m, nil
}
Ejemplo n.º 12
0
// PayloadSign generates a sign based on the Algorithm instance variable.
// This fulfills the `PayloadSigner` interface
func (s EcdsaSign) PayloadSign(payload []byte) ([]byte, error) {
	hash, err := ecdsaHashForAlg(s.SignatureAlgorithm())
	if err != nil {
		return nil, err
	}

	privkey := s.PrivateKey
	if privkey == nil {
		return nil, errors.New("cannot proceed with Sign(): no private key available")
	}

	keysiz := hash.Size()
	curveBits := privkey.Curve.Params().BitSize
	if curveBits != keysiz*8 {
		return nil, errors.New("key size does not match curve bit size")
	}

	h := hash.New()
	h.Write(payload)
	signed := h.Sum(nil)
	if debug.Enabled {
		debug.Printf("payload = %s, signed -> %x", payload, signed)
	}

	r, v, err := ecdsa.Sign(rand.Reader, privkey, signed)
	if err != nil {
		return nil, err
	}

	out := make([]byte, keysiz*2)
	keys := [][]byte{r.Bytes(), v.Bytes()}
	for i, data := range keys {
		start := i * keysiz
		padlen := keysiz - len(data)
		copy(out[start+padlen:], data)
	}

	return out, nil
}
Ejemplo n.º 13
0
func (v EcdsaVerify) PayloadVerify(payload, signature []byte) error {
	pubkey := v.pubkey
	hfunc := v.hash
	keysiz := hfunc.Size()
	if len(signature) != 2*keysiz {
		return ErrInvalidEcdsaSignatureSize
	}

	rv := (&big.Int{}).SetBytes(signature[:keysiz])
	sv := (&big.Int{}).SetBytes(signature[keysiz:])

	h := hfunc.New()
	h.Write(payload)
	signed := h.Sum(nil)

	debug.Printf("payload -> %s, signed -> %x", payload, signed)

	if !ecdsa.Verify(pubkey, signed, rv, sv) {
		return ErrInvalidSignature
	}
	return nil
}
Ejemplo n.º 14
0
func (c AesCbcHmac) ComputeAuthTag(aad, nonce, ciphertext []byte) []byte {
	debug.Printf("ComputeAuthTag: aad        = %x (%d)\n", aad, len(aad))
	debug.Printf("ComputeAuthTag: ciphertext = %x (%d)\n", ciphertext, len(ciphertext))
	debug.Printf("ComputeAuthTag: iv (nonce) = %x (%d)\n", nonce, len(nonce))
	debug.Printf("ComputeAuthTag: integrity  = %x (%d)\n", c.integrityKey, len(c.integrityKey))

	buf := make([]byte, len(aad)+len(nonce)+len(ciphertext)+8)
	n := 0
	n += copy(buf, aad)
	n += copy(buf[n:], nonce)
	n += copy(buf[n:], ciphertext)
	binary.BigEndian.PutUint64(buf[n:], uint64(len(aad)*8))

	h := hmac.New(c.hash, c.integrityKey)
	h.Write(buf)
	s := h.Sum(nil)
	debug.Printf("ComputeAuthTag: buf        = %x (%d)\n", buf, len(buf))
	debug.Printf("ComputeAuthTag: computed   = %x (%d)\n", s[:c.keysize], len(s[:c.keysize]))
	return s[:c.tagsize]
}
Ejemplo n.º 15
0
// Encrypt takes the plaintext payload and encrypts it in JWE compact format.
func Encrypt(payload []byte, keyalg jwa.KeyEncryptionAlgorithm, key interface{}, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm) ([]byte, error) {
	contentcrypt, err := NewAesCrypt(contentalg)
	if err != nil {
		return nil, err
	}

	var keyenc KeyEncrypter
	var keysize int
	switch keyalg {
	case jwa.RSA1_5:
		pubkey, ok := key.(*rsa.PublicKey)
		if !ok {
			return nil, errors.New("invalid key: *rsa.PublicKey required")
		}
		keyenc, err = NewRSAPKCSKeyEncrypt(keyalg, pubkey)
		if err != nil {
			return nil, err
		}
		keysize = contentcrypt.KeySize() / 2
	case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
		pubkey, ok := key.(*rsa.PublicKey)
		if !ok {
			return nil, errors.New("invalid key: *rsa.PublicKey required")
		}
		keyenc, err = NewRSAOAEPKeyEncrypt(keyalg, pubkey)
		if err != nil {
			return nil, err
		}
		keysize = contentcrypt.KeySize() / 2
	case jwa.A128KW, jwa.A192KW, jwa.A256KW:
		sharedkey, ok := key.([]byte)
		if !ok {
			return nil, errors.New("invalid key: []byte required")
		}
		keyenc, err = NewAesKeyWrap(keyalg, sharedkey)
		if err != nil {
			return nil, err
		}
		keysize = contentcrypt.KeySize()
	case jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
		pubkey, ok := key.(*ecdsa.PublicKey)
		if !ok {
			return nil, errors.New("invalid key: *ecdsa.PublicKey required")
		}
		keyenc, err = NewEcdhesKeyWrapEncrypt(keyalg, pubkey)
		if err != nil {
			return nil, err
		}
		keysize = contentcrypt.KeySize()
	case jwa.ECDH_ES:
		fallthrough
	case jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW:
		fallthrough
	case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
		fallthrough
	default:
		debug.Printf("Encrypt: unknown key encryption algorithm: %s", keyalg)
		return nil, ErrUnsupportedAlgorithm
	}

	enc := NewMultiEncrypt(contentcrypt, NewRandomKeyGenerate(keysize), keyenc)
	msg, err := enc.Encrypt(payload)
	if err != nil {
		debug.Printf("Encrypt: failed to encrypt: %s", err)
		return nil, err
	}

	return CompactSerialize{}.Serialize(msg)
}
Ejemplo n.º 16
0
// Decrypt decrypts the message using the specified algorithm and key
func (m *Message) Decrypt(alg jwa.KeyEncryptionAlgorithm, key interface{}) ([]byte, error) {
	var err error

	if len(m.Recipients) == 0 {
		return nil, errors.New("no recipients, can not proceed with decrypt")
	}

	enc := m.ProtectedHeader.ContentEncryption

	h := NewHeader()
	if err := h.Copy(m.ProtectedHeader.Header); err != nil {
		return nil, err
	}
	h, err = h.Merge(m.UnprotectedHeader)
	if err != nil {
		if debug.Enabled {
			debug.Printf("failed to merge unprotected header")
		}
		return nil, err
	}

	aad, err := m.AuthenticatedData.Base64Encode()
	if err != nil {
		return nil, err
	}
	ciphertext := m.CipherText.Bytes()
	iv := m.InitializationVector.Bytes()
	tag := m.Tag.Bytes()

	cipher, err := buildContentCipher(enc)
	if err != nil {
		return nil, fmt.Errorf("unsupported content cipher algorithm '%s'", enc)
	}
	keysize := cipher.KeySize()

	var plaintext []byte
	for _, recipient := range m.Recipients {
		if debug.Enabled {
			debug.Printf("Attempting to check if we can decode for recipient (alg = %s)", recipient.Header.Algorithm)
		}
		if recipient.Header.Algorithm != alg {
			continue
		}

		h2 := NewHeader()
		if err := h2.Copy(h); err != nil {
			if debug.Enabled {
				debug.Printf("failed to copy header: %s", err)
			}
			continue
		}

		h2, err := h2.Merge(recipient.Header)
		if err != nil {
			if debug.Enabled {
				debug.Printf("Failed to merge! %s", err)
			}
			continue
		}

		k, err := BuildKeyDecrypter(h2.Algorithm, h2, key, keysize)
		if err != nil {
			if debug.Enabled {
				debug.Printf("failed to create key decrypter: %s", err)
			}
			continue
		}

		cek, err := k.KeyDecrypt(recipient.EncryptedKey.Bytes())
		if err != nil {
			if debug.Enabled {
				debug.Printf("failed to decrypt key: %s", err)
			}
			continue
		}

		plaintext, err = cipher.decrypt(cek, iv, ciphertext, tag, aad)
		if err == nil {
			break
		}
		if debug.Enabled {
			debug.Printf("DecryptMessage: failed to decrypt using %s: %s", h2.Algorithm, err)
		}
		// Keep looping because there might be another key with the same algo
	}

	if plaintext == nil {
		return nil, errors.New("failed to find matching recipient to decrypt key")
	}

	if h.Compression == jwa.Deflate {
		output := bytes.Buffer{}
		w, _ := flate.NewWriter(&output, 1)
		in := plaintext
		for len(in) > 0 {
			n, err := w.Write(in)
			if err != nil {
				return nil, err
			}
			in = in[n:]
		}
		if err := w.Close(); err != nil {
			return nil, err
		}
		plaintext = output.Bytes()
	}

	return plaintext, nil
}
Ejemplo n.º 17
0
	"github.com/lestrrat/go-jwx/jwa"
	"github.com/lestrrat/go-jwx/jwe/aescbc"
)

const (
	TagSize = 16
)

func (f AeadFetchFunc) AeadFetch(key []byte) (cipher.AEAD, error) {
	return f(key)
}

var GcmAeadFetch = AeadFetchFunc(func(key []byte) (cipher.AEAD, error) {
	aescipher, err := aes.NewCipher(key)
	if err != nil {
		debug.Printf("GcmAeadFetch: failed to create cipher")
		return nil, fmt.Errorf("cipher: failed to create AES cipher for GCM: %s", err)
	}

	return cipher.NewGCM(aescipher)
})
var CbcAeadFetch = AeadFetchFunc(func(key []byte) (cipher.AEAD, error) {
	aead, err := aescbc.New(key, aes.NewCipher)
	if err != nil {
		debug.Printf("CbcAeadFetch: failed to create aead fetcher (%v): %s", key, err)
		return nil, fmt.Errorf("cipher: failed to create AES cipher for CBC: %s", err)
	}
	return aead, nil
})

func (c AesContentCipher) KeySize() int {
Ejemplo n.º 18
0
// Encrypt takes the plaintext and encrypts into a JWE message.
func (e MultiEncrypt) Encrypt(plaintext []byte) (*Message, error) {
	bk, err := e.KeyGenerator.KeyGenerate()
	if err != nil {
		debug.Printf("Failed to generate key: %s", err)
		return nil, err
	}
	cek := bk.Bytes()

	debug.Printf("Encrypt: generated cek len = %d", len(cek))

	protected := NewEncodedHeader()
	protected.Set("enc", e.ContentEncrypter.Algorithm())

	// In JWE, multiple recipients may exist -- they receive an
	// encrypted version of the CEK, using their key encryption
	// algorithm of choice.
	recipients := make([]Recipient, len(e.KeyEncrypters))
	for i, enc := range e.KeyEncrypters {
		r := NewRecipient()
		r.Header.Set("alg", enc.Algorithm())
		if v := enc.Kid(); v != "" {
			r.Header.Set("kid", v)
		}
		enckey, err := enc.KeyEncrypt(cek)
		if err != nil {
			debug.Printf("Failed to encrypt key: %s", err)
			return nil, err
		}
		r.EncryptedKey = enckey.Bytes()
		if hp, ok := enckey.(HeaderPopulater); ok {
			hp.HeaderPopulate(r.Header)
		}
		debug.Printf("Encrypt: encrypted_key = %x (%d)", enckey.Bytes(), len(enckey.Bytes()))
		recipients[i] = *r
	}

	// If there's only one recipient, you want to include that in the
	// protected header
	if len(recipients) == 1 {
		protected.Header, err = protected.Header.Merge(recipients[0].Header)
		if err != nil {
			return nil, err
		}
	}

	aad, err := protected.Base64Encode()
	if err != nil {
		return nil, err
	}

	// ...on the other hand, there's only one content cipher.
	iv, ciphertext, tag, err := e.ContentEncrypter.Encrypt(cek, plaintext, aad)
	if err != nil {
		debug.Printf("Failed to encrypt: %s", err)
		return nil, err
	}

	debug.Printf("Encrypt.Encrypt: cek        = %x (%d)", cek, len(cek))
	debug.Printf("Encrypt.Encrypt: aad        = %x", aad)
	debug.Printf("Encrypt.Encrypt: ciphertext = %x", ciphertext)
	debug.Printf("Encrypt.Encrypt: iv         = %x", iv)
	debug.Printf("Encrypt.Encrypt: tag        = %x", tag)

	msg := NewMessage()
	msg.AuthenticatedData.Base64Decode(aad)
	msg.CipherText = ciphertext
	msg.InitializationVector = iv
	msg.ProtectedHeader = protected
	msg.Recipients = recipients
	msg.Tag = tag

	return msg, nil
}