summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-02-16 16:48:49 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-02-16 16:48:49 +0700
commitecdb18e294b26b92aa92b9fed9e45c4463765298 (patch)
tree65f820a3e620947638146e3570f104c470312d1c
parentRemove ExtendedKeyUsage from criticalExtensions (diff)
downloadBouncyCastle.NET-ed25519-ecdb18e294b26b92aa92b9fed9e45c4463765298.tar.xz
CMS support for key ID + public key recipients in key agreement
- see https://github.com/bcgit/bc-csharp/issues/415
-rw-r--r--crypto/src/cms/CMSEnvelopedGenerator.cs47
-rw-r--r--crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs85
2 files changed, 76 insertions, 56 deletions
diff --git a/crypto/src/cms/CMSEnvelopedGenerator.cs b/crypto/src/cms/CMSEnvelopedGenerator.cs
index 22a999766..401f4d2e8 100644
--- a/crypto/src/cms/CMSEnvelopedGenerator.cs
+++ b/crypto/src/cms/CMSEnvelopedGenerator.cs
@@ -225,11 +225,10 @@ namespace Org.BouncyCastle.Cms
 			X509Certificate			recipientCert,
 			string					cekWrapAlgorithm)
 		{
-            var recipientCerts = new List<X509Certificate>(1);
-			recipientCerts.Add(recipientCert);
+            var recipientCerts = new List<X509Certificate>(1){ recipientCert };
 
-			AddKeyAgreementRecipients(agreementAlgorithm, senderPrivateKey, senderPublicKey,
-				recipientCerts, cekWrapAlgorithm);
+			AddKeyAgreementRecipients(agreementAlgorithm, senderPrivateKey, senderPublicKey, recipientCerts,
+				cekWrapAlgorithm);
 		}
 
 		/**
@@ -251,24 +250,46 @@ namespace Org.BouncyCastle.Cms
 			string					cekWrapAlgorithm)
 		{
 			if (!senderPrivateKey.IsPrivate)
-				throw new ArgumentException("Expected private key", "senderPrivateKey");
+				throw new ArgumentException("Expected private key", nameof(senderPrivateKey));
 			if (senderPublicKey.IsPrivate)
-				throw new ArgumentException("Expected public key", "senderPublicKey");
+				throw new ArgumentException("Expected public key", nameof(senderPublicKey));
 
 			/* TODO
 			 * "a recipient X.509 version 3 certificate that contains a key usage extension MUST
 			 * assert the keyAgreement bit."
 			 */
 
-			KeyAgreeRecipientInfoGenerator karig = new KeyAgreeRecipientInfoGenerator();
-			karig.KeyAgreementOID = new DerObjectIdentifier(agreementAlgorithm);
-			karig.KeyEncryptionOID = new DerObjectIdentifier(cekWrapAlgorithm);
-			karig.RecipientCerts = new List<X509Certificate>(recipientCerts);
-			karig.SenderKeyPair = new AsymmetricCipherKeyPair(senderPublicKey, senderPrivateKey);
-
-			recipientInfoGenerators.Add(karig);
+			recipientInfoGenerators.Add(new KeyAgreeRecipientInfoGenerator(recipientCerts)
+            {
+                KeyAgreementOid = new DerObjectIdentifier(agreementAlgorithm),
+                KeyEncryptionOid = new DerObjectIdentifier(cekWrapAlgorithm),
+                SenderKeyPair = new AsymmetricCipherKeyPair(senderPublicKey, senderPrivateKey),
+            });
 		}
 
+        public void AddKeyAgreementRecipient(
+			string agreementAlgorithm,
+            AsymmetricKeyParameter senderPrivateKey,
+            AsymmetricKeyParameter senderPublicKey,
+			byte[] recipientKeyID,
+            AsymmetricKeyParameter recipientPublicKey,
+            string cekWrapAlgorithm)
+        {
+            if (!senderPrivateKey.IsPrivate)
+                throw new ArgumentException("Expected private key", nameof(senderPrivateKey));
+            if (senderPublicKey.IsPrivate)
+                throw new ArgumentException("Expected public key", nameof(senderPublicKey));
+            if (recipientPublicKey.IsPrivate)
+                throw new ArgumentException("Expected public key", nameof(recipientPublicKey));
+
+            recipientInfoGenerators.Add(new KeyAgreeRecipientInfoGenerator(recipientKeyID, recipientPublicKey)
+            {
+                KeyAgreementOid = new DerObjectIdentifier(agreementAlgorithm),
+                KeyEncryptionOid = new DerObjectIdentifier(cekWrapAlgorithm),
+                SenderKeyPair = new AsymmetricCipherKeyPair(senderPublicKey, senderPrivateKey),
+            });
+        }
+
         /// <summary>
         /// Add a generator to produce the recipient info required.
         /// </summary>
diff --git a/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs b/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
index f74365571..8feb25b43 100644
--- a/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
+++ b/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
@@ -1,4 +1,3 @@
-using System;
 using System.Collections.Generic;
 using System.IO;
 
@@ -15,46 +14,54 @@ using Org.BouncyCastle.X509;
 
 namespace Org.BouncyCastle.Cms
 {
-	internal class KeyAgreeRecipientInfoGenerator : RecipientInfoGenerator
+	internal class KeyAgreeRecipientInfoGenerator
+		: RecipientInfoGenerator
 	{
 		private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance;
 
-		private DerObjectIdentifier			keyAgreementOID;
-		private DerObjectIdentifier			keyEncryptionOID;
-		private IList<X509Certificate>      recipientCerts;
-		private AsymmetricCipherKeyPair		senderKeyPair;
+        private readonly List<KeyAgreeRecipientIdentifier> m_recipientIDs = new List<KeyAgreeRecipientIdentifier>();
+        private readonly List<AsymmetricKeyParameter> m_recipientKeys = new List<AsymmetricKeyParameter>();
 
-		internal KeyAgreeRecipientInfoGenerator()
-		{
-		}
+        private DerObjectIdentifier m_keyAgreementOid;
+		private DerObjectIdentifier m_keyEncryptionOid;
+		private AsymmetricCipherKeyPair m_senderKeyPair;
 
-		internal DerObjectIdentifier KeyAgreementOID
+		internal KeyAgreeRecipientInfoGenerator(IEnumerable<X509Certificate> recipientCerts)
 		{
-			set { this.keyAgreementOID = value; }
-		}
-
-		internal DerObjectIdentifier KeyEncryptionOID
+			foreach (var recipientCert in recipientCerts)
+			{
+				m_recipientIDs.Add(new KeyAgreeRecipientIdentifier(CmsUtilities.GetIssuerAndSerialNumber(recipientCert)));
+				m_recipientKeys.Add(recipientCert.GetPublicKey());
+            }
+        }
+
+        internal KeyAgreeRecipientInfoGenerator(byte[] subjectKeyID, AsymmetricKeyParameter publicKey)
+        {
+            m_recipientIDs.Add(new KeyAgreeRecipientIdentifier(new RecipientKeyIdentifier(subjectKeyID)));
+            m_recipientKeys.Add(publicKey);
+        }
+
+        internal DerObjectIdentifier KeyAgreementOid
 		{
-			set { this.keyEncryptionOID = value; }
+			set { m_keyAgreementOid = value; }
 		}
 
-		internal IEnumerable<X509Certificate> RecipientCerts
+		internal DerObjectIdentifier KeyEncryptionOid
 		{
-			set { this.recipientCerts = new List<X509Certificate>(value); }
+			set { m_keyEncryptionOid = value; }
 		}
 
 		internal AsymmetricCipherKeyPair SenderKeyPair
 		{
-			set { this.senderKeyPair = value; }
+			set { m_senderKeyPair = value; }
 		}
 
 		public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random)
 		{
 			byte[] keyBytes = contentEncryptionKey.GetKey();
 
-			AsymmetricKeyParameter senderPublicKey = senderKeyPair.Public;
-			ICipherParameters senderPrivateParams = senderKeyPair.Private;
-
+			AsymmetricKeyParameter senderPublicKey = m_senderKeyPair.Public;
+			ICipherParameters senderPrivateParams = m_senderKeyPair.Private;
 
 			OriginatorIdentifierOrKey originator;
 			try
@@ -67,14 +74,13 @@ namespace Org.BouncyCastle.Cms
 				throw new InvalidKeyException("cannot extract originator public key: " + e);
 			}
 
-
 			Asn1OctetString ukm = null;
-			if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+			if (m_keyAgreementOid.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
 			{
 				try
 				{
 					IAsymmetricCipherKeyPairGenerator ephemKPG =
-						GeneratorUtilities.GetKeyPairGenerator(keyAgreementOID);
+						GeneratorUtilities.GetKeyPairGenerator(m_keyAgreementOid);
 					ephemKPG.Init(
 						((ECPublicKeyParameters)senderPublicKey).CreateKeyGenerationParameters(random));
 
@@ -99,22 +105,16 @@ namespace Org.BouncyCastle.Cms
 				}
 			}
 
-
-			DerSequence paramSeq = new DerSequence(
-				keyEncryptionOID,
-				DerNull.Instance);
-			AlgorithmIdentifier keyEncAlg = new AlgorithmIdentifier(keyAgreementOID, paramSeq);
-
+			DerSequence paramSeq = new DerSequence(m_keyEncryptionOid, DerNull.Instance);
+			AlgorithmIdentifier keyEncAlg = new AlgorithmIdentifier(m_keyAgreementOid, paramSeq);
 
 			Asn1EncodableVector recipientEncryptedKeys = new Asn1EncodableVector();
-			foreach (X509Certificate recipientCert in recipientCerts)
+            for (int i = 0; i < m_recipientIDs.Count; ++i)
 			{
-				// TODO Should there be a SubjectKeyIdentifier-based alternative?
-                KeyAgreeRecipientIdentifier karid = new KeyAgreeRecipientIdentifier(
-					CmsUtilities.GetIssuerAndSerialNumber(recipientCert));
+				var recipientID = m_recipientIDs[i];
+				ICipherParameters recipientPublicParams = m_recipientKeys[i];
 
-				ICipherParameters recipientPublicParams = recipientCert.GetPublicKey();
-				if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+				if (m_keyAgreementOid.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
 				{
 					recipientPublicParams = new MqvPublicParameters(
 						(ECPublicKeyParameters)recipientPublicParams,
@@ -123,31 +123,30 @@ namespace Org.BouncyCastle.Cms
 
 				// Use key agreement to choose a wrap key for this recipient
 				IBasicAgreement keyAgreement = AgreementUtilities.GetBasicAgreementWithKdf(
-					keyAgreementOID, keyEncryptionOID.Id);
+					m_keyAgreementOid, m_keyEncryptionOid.Id);
 				keyAgreement.Init(new ParametersWithRandom(senderPrivateParams, random));
 				BigInteger agreedValue = keyAgreement.CalculateAgreement(recipientPublicParams);
 
-				int keyEncryptionKeySize = GeneratorUtilities.GetDefaultKeySize(keyEncryptionOID) / 8;
+				int keyEncryptionKeySize = GeneratorUtilities.GetDefaultKeySize(m_keyEncryptionOid) / 8;
 				byte[] keyEncryptionKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, keyEncryptionKeySize);
 				KeyParameter keyEncryptionKey = ParameterUtilities.CreateKeyParameter(
-					keyEncryptionOID, keyEncryptionKeyBytes);
+					m_keyEncryptionOid, keyEncryptionKeyBytes);
 
 				// Wrap the content encryption key with the agreement key
-				IWrapper keyWrapper = WrapperUtilities.GetWrapper(keyEncryptionOID.Id);
+				IWrapper keyWrapper = WrapperUtilities.GetWrapper(m_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));
+				recipientEncryptedKeys.Add(new RecipientEncryptedKey(recipientID, encryptedKey));
 			}
 
 			return new RecipientInfo(new KeyAgreeRecipientInfo(originator, ukm, keyEncAlg,
 				new DerSequence(recipientEncryptedKeys)));
 		}
 
-		private static OriginatorPublicKey CreateOriginatorPublicKey(
-			AsymmetricKeyParameter publicKey)
+		private static OriginatorPublicKey CreateOriginatorPublicKey(AsymmetricKeyParameter publicKey)
 		{
 			SubjectPublicKeyInfo spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey);
 			return new OriginatorPublicKey(