summary refs log tree commit diff
path: root/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs')
-rw-r--r--crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs171
1 files changed, 171 insertions, 0 deletions
diff --git a/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs b/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
new file mode 100644
index 000000000..4fafb7c6e
--- /dev/null
+++ b/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
@@ -0,0 +1,171 @@
+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.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+	internal class KeyAgreeRecipientInfoGenerator : RecipientInfoGenerator
+	{
+		private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance;
+
+		private DerObjectIdentifier			keyAgreementOID;
+		private DerObjectIdentifier			keyEncryptionOID;
+		private IList					    recipientCerts;
+		private AsymmetricCipherKeyPair		senderKeyPair;
+
+		internal KeyAgreeRecipientInfoGenerator()
+		{
+		}
+
+		internal DerObjectIdentifier KeyAgreementOID
+		{
+			set { this.keyAgreementOID = value; }
+		}
+
+		internal DerObjectIdentifier KeyEncryptionOID
+		{
+			set { this.keyEncryptionOID = value; }
+		}
+
+		internal ICollection RecipientCerts
+		{
+			set { this.recipientCerts = Platform.CreateArrayList(value); }
+		}
+
+		internal AsymmetricCipherKeyPair SenderKeyPair
+		{
+			set { this.senderKeyPair = value; }
+		}
+
+		public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random)
+		{
+			byte[] keyBytes = contentEncryptionKey.GetKey();
+
+			AsymmetricKeyParameter senderPublicKey = senderKeyPair.Public;
+			ICipherParameters senderPrivateParams = senderKeyPair.Private;
+
+
+			OriginatorIdentifierOrKey originator;
+			try
+			{
+				originator = new OriginatorIdentifierOrKey(
+					CreateOriginatorPublicKey(senderPublicKey));
+			}
+			catch (IOException e)
+			{
+				throw new InvalidKeyException("cannot extract originator public key: " + e);
+			}
+
+
+			Asn1OctetString ukm = null;
+			if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+			{
+				try
+				{
+					IAsymmetricCipherKeyPairGenerator ephemKPG =
+						GeneratorUtilities.GetKeyPairGenerator(keyAgreementOID);
+					ephemKPG.Init(
+						((ECPublicKeyParameters)senderPublicKey).CreateKeyGenerationParameters(random));
+
+					AsymmetricCipherKeyPair ephemKP = ephemKPG.GenerateKeyPair();
+
+					ukm = new DerOctetString(
+						new MQVuserKeyingMaterial(
+							CreateOriginatorPublicKey(ephemKP.Public), null));
+
+					senderPrivateParams = new MqvPrivateParameters(
+						(ECPrivateKeyParameters)senderPrivateParams,
+						(ECPrivateKeyParameters)ephemKP.Private,
+						(ECPublicKeyParameters)ephemKP.Public);
+				}
+				catch (IOException e)
+				{
+					throw new InvalidKeyException("cannot extract MQV ephemeral public key: " + e);
+				}
+				catch (SecurityUtilityException e)
+				{
+					throw new InvalidKeyException("cannot determine MQV ephemeral key pair parameters from public key: " + e);
+				}
+			}
+
+
+			DerSequence paramSeq = new DerSequence(
+				keyEncryptionOID,
+				DerNull.Instance);
+			AlgorithmIdentifier keyEncAlg = new AlgorithmIdentifier(keyAgreementOID, paramSeq);
+
+
+			Asn1EncodableVector recipientEncryptedKeys = new Asn1EncodableVector();
+			foreach (X509Certificate recipientCert in recipientCerts)
+			{
+				TbsCertificateStructure tbsCert;
+				try
+				{
+					tbsCert = TbsCertificateStructure.GetInstance(
+						Asn1Object.FromByteArray(recipientCert.GetTbsCertificate()));
+				}
+				catch (Exception)
+				{
+					throw new ArgumentException("can't extract TBS structure from certificate");
+				}
+
+				// TODO Should there be a SubjectKeyIdentifier-based alternative?
+				IssuerAndSerialNumber issuerSerial = new IssuerAndSerialNumber(
+					tbsCert.Issuer, tbsCert.SerialNumber.Value);
+				KeyAgreeRecipientIdentifier karid = new KeyAgreeRecipientIdentifier(issuerSerial);
+
+				ICipherParameters recipientPublicParams = recipientCert.GetPublicKey();
+				if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+				{
+					recipientPublicParams = new MqvPublicParameters(
+						(ECPublicKeyParameters)recipientPublicParams,
+						(ECPublicKeyParameters)recipientPublicParams);
+				}
+
+				// Use key agreement to choose a wrap key for this recipient
+				IBasicAgreement keyAgreement = AgreementUtilities.GetBasicAgreementWithKdf(
+					keyAgreementOID, keyEncryptionOID.Id);
+				keyAgreement.Init(new ParametersWithRandom(senderPrivateParams, random));
+				BigInteger agreedValue = keyAgreement.CalculateAgreement(recipientPublicParams);
+
+				int keyEncryptionKeySize = GeneratorUtilities.GetDefaultKeySize(keyEncryptionOID) / 8;
+				byte[] keyEncryptionKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, keyEncryptionKeySize);
+				KeyParameter keyEncryptionKey = ParameterUtilities.CreateKeyParameter(
+					keyEncryptionOID, keyEncryptionKeyBytes);
+
+				// Wrap the content encryption key with the agreement key
+				IWrapper keyWrapper = Helper.CreateWrapper(keyEncryptionOID.Id);
+				keyWrapper.Init(true, new ParametersWithRandom(keyEncryptionKey, random));
+				byte[] encryptedKeyBytes = keyWrapper.Wrap(keyBytes, 0, keyBytes.Length);
+
+	        	Asn1OctetString encryptedKey = new DerOctetString(encryptedKeyBytes);
+
+				recipientEncryptedKeys.Add(new RecipientEncryptedKey(karid, encryptedKey));
+			}
+
+			return new RecipientInfo(new KeyAgreeRecipientInfo(originator, ukm, keyEncAlg,
+				new DerSequence(recipientEncryptedKeys)));
+		}
+
+		private static OriginatorPublicKey CreateOriginatorPublicKey(
+			AsymmetricKeyParameter publicKey)
+		{
+			SubjectPublicKeyInfo spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey);
+			return new OriginatorPublicKey(
+				new AlgorithmIdentifier(spki.AlgorithmID.ObjectID, DerNull.Instance),
+				spki.PublicKeyData.GetBytes());
+		}
+	}
+}