diff --git a/Crypto/src/cms/KeyAgreeRecipientInformation.cs b/Crypto/src/cms/KeyAgreeRecipientInformation.cs
new file mode 100644
index 000000000..38a94b0a4
--- /dev/null
+++ b/Crypto/src/cms/KeyAgreeRecipientInformation.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Cms.Ecc;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * the RecipientInfo class for a recipient who has been sent a message
+ * encrypted using key agreement.
+ */
+ public class KeyAgreeRecipientInformation
+ : RecipientInformation
+ {
+ private KeyAgreeRecipientInfo info;
+ private Asn1OctetString encryptedKey;
+
+ internal static void ReadRecipientInfo(IList infos, KeyAgreeRecipientInfo info,
+ CmsSecureReadable secureReadable)
+ {
+ try
+ {
+ foreach (Asn1Encodable rek in info.RecipientEncryptedKeys)
+ {
+ RecipientEncryptedKey id = RecipientEncryptedKey.GetInstance(rek.ToAsn1Object());
+
+ RecipientID rid = new RecipientID();
+
+ Asn1.Cms.KeyAgreeRecipientIdentifier karid = id.Identifier;
+
+ Asn1.Cms.IssuerAndSerialNumber iAndSN = karid.IssuerAndSerialNumber;
+ if (iAndSN != null)
+ {
+ rid.Issuer = iAndSN.Name;
+ rid.SerialNumber = iAndSN.SerialNumber.Value;
+ }
+ else
+ {
+ Asn1.Cms.RecipientKeyIdentifier rKeyID = karid.RKeyID;
+
+ // Note: 'date' and 'other' fields of RecipientKeyIdentifier appear to be only informational
+
+ rid.SubjectKeyIdentifier = rKeyID.SubjectKeyIdentifier.GetOctets();
+ }
+
+ infos.Add(new KeyAgreeRecipientInformation(info, rid, id.EncryptedKey,
+ secureReadable));
+ }
+ }
+ catch (IOException e)
+ {
+ throw new ArgumentException("invalid rid in KeyAgreeRecipientInformation", e);
+ }
+ }
+
+ internal KeyAgreeRecipientInformation(
+ KeyAgreeRecipientInfo info,
+ RecipientID rid,
+ Asn1OctetString encryptedKey,
+ CmsSecureReadable secureReadable)
+ : base(info.KeyEncryptionAlgorithm, secureReadable)
+ {
+ this.info = info;
+ this.rid = rid;
+ this.encryptedKey = encryptedKey;
+ }
+
+ private AsymmetricKeyParameter GetSenderPublicKey(
+ AsymmetricKeyParameter receiverPrivateKey,
+ OriginatorIdentifierOrKey originator)
+ {
+ OriginatorPublicKey opk = originator.OriginatorPublicKey;
+ if (opk != null)
+ {
+ return GetPublicKeyFromOriginatorPublicKey(receiverPrivateKey, opk);
+ }
+
+ OriginatorID origID = new OriginatorID();
+
+ Asn1.Cms.IssuerAndSerialNumber iAndSN = originator.IssuerAndSerialNumber;
+ if (iAndSN != null)
+ {
+ origID.Issuer = iAndSN.Name;
+ origID.SerialNumber = iAndSN.SerialNumber.Value;
+ }
+ else
+ {
+ SubjectKeyIdentifier ski = originator.SubjectKeyIdentifier;
+
+ origID.SubjectKeyIdentifier = ski.GetKeyIdentifier();
+ }
+
+ return GetPublicKeyFromOriginatorID(origID);
+ }
+
+ private AsymmetricKeyParameter GetPublicKeyFromOriginatorPublicKey(
+ AsymmetricKeyParameter receiverPrivateKey,
+ OriginatorPublicKey originatorPublicKey)
+ {
+ PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(receiverPrivateKey);
+ SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(
+ privInfo.AlgorithmID,
+ originatorPublicKey.PublicKey.GetBytes());
+ return PublicKeyFactory.CreateKey(pubInfo);
+ }
+
+ private AsymmetricKeyParameter GetPublicKeyFromOriginatorID(
+ OriginatorID origID)
+ {
+ // TODO Support all alternatives for OriginatorIdentifierOrKey
+ // see RFC 3852 6.2.2
+ throw new CmsException("No support for 'originator' as IssuerAndSerialNumber or SubjectKeyIdentifier");
+ }
+
+ private KeyParameter CalculateAgreedWrapKey(
+ string wrapAlg,
+ AsymmetricKeyParameter senderPublicKey,
+ AsymmetricKeyParameter receiverPrivateKey)
+ {
+ DerObjectIdentifier agreeAlgID = keyEncAlg.ObjectID;
+
+ ICipherParameters senderPublicParams = senderPublicKey;
+ ICipherParameters receiverPrivateParams = receiverPrivateKey;
+
+ if (agreeAlgID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+ {
+ byte[] ukmEncoding = info.UserKeyingMaterial.GetOctets();
+ MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.GetInstance(
+ Asn1Object.FromByteArray(ukmEncoding));
+
+ AsymmetricKeyParameter ephemeralKey = GetPublicKeyFromOriginatorPublicKey(
+ receiverPrivateKey, ukm.EphemeralPublicKey);
+
+ senderPublicParams = new MqvPublicParameters(
+ (ECPublicKeyParameters)senderPublicParams,
+ (ECPublicKeyParameters)ephemeralKey);
+ receiverPrivateParams = new MqvPrivateParameters(
+ (ECPrivateKeyParameters)receiverPrivateParams,
+ (ECPrivateKeyParameters)receiverPrivateParams);
+ }
+
+ IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf(
+ agreeAlgID, wrapAlg);
+ agreement.Init(receiverPrivateParams);
+ BigInteger agreedValue = agreement.CalculateAgreement(senderPublicParams);
+
+ int wrapKeySize = GeneratorUtilities.GetDefaultKeySize(wrapAlg) / 8;
+ byte[] wrapKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, wrapKeySize);
+ return ParameterUtilities.CreateKeyParameter(wrapAlg, wrapKeyBytes);
+ }
+
+ private KeyParameter UnwrapSessionKey(
+ string wrapAlg,
+ KeyParameter agreedKey)
+ {
+ byte[] encKeyOctets = encryptedKey.GetOctets();
+
+ IWrapper keyCipher = WrapperUtilities.GetWrapper(wrapAlg);
+ keyCipher.Init(false, agreedKey);
+ byte[] sKeyBytes = keyCipher.Unwrap(encKeyOctets, 0, encKeyOctets.Length);
+ return ParameterUtilities.CreateKeyParameter(GetContentAlgorithmName(), sKeyBytes);
+ }
+
+ internal KeyParameter GetSessionKey(
+ AsymmetricKeyParameter receiverPrivateKey)
+ {
+ try
+ {
+ string wrapAlg = DerObjectIdentifier.GetInstance(
+ Asn1Sequence.GetInstance(keyEncAlg.Parameters)[0]).Id;
+
+ AsymmetricKeyParameter senderPublicKey = GetSenderPublicKey(
+ receiverPrivateKey, info.Originator);
+
+ KeyParameter agreedWrapKey = CalculateAgreedWrapKey(wrapAlg,
+ senderPublicKey, receiverPrivateKey);
+
+ return UnwrapSessionKey(wrapAlg, agreedWrapKey);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (Exception e)
+ {
+ throw new CmsException("originator key invalid.", e);
+ }
+ }
+
+ /**
+ * decrypt the content and return an input stream.
+ */
+ public override CmsTypedStream GetContentStream(
+ ICipherParameters key)
+ {
+ if (!(key is AsymmetricKeyParameter))
+ throw new ArgumentException("KeyAgreement requires asymmetric key", "key");
+
+ AsymmetricKeyParameter receiverPrivateKey = (AsymmetricKeyParameter) key;
+
+ if (!receiverPrivateKey.IsPrivate)
+ throw new ArgumentException("Expected private key", "key");
+
+ KeyParameter sKey = GetSessionKey(receiverPrivateKey);
+
+ return GetContentFromSessionKey(sKey);
+ }
+ }
+}
|