[Mono-dev] PKCS12 and SecretBags, proposed patch

Jay Miller jnmiller at cryptofreak.org
Tue Feb 6 17:12:10 EST 2007


This is a followup to a thread a couple months ago about adding
SecretBags to the PKCS12 class.  I proposed a couple of ideas to
accomplish this at the time and Mr. Pouliot responded with some of his
toughts.  I'll just quote a short piece:

- Words by Sebastien Pouliot [061218 17:30]:
> On Mon, 2006-12-18 at 16:09 -0700, Jay Miller wrote:
> > Solution 1)
> > 
> > Arbitrarily decide that SecretBags will be of type byte[].  Add
> > functions that mirror existing ones: AddSecretBag(), GetSecretBags(),
> > RemoveSecretBag(), etc.  Add a member _secretBags that stores the
> > current SecretBags.
> 
> I like this approach because:
> 
> 1. everything converts into byte[]
> 
> 2. it doesn't break anything and doesn't add too much stuff to the
> class;
> 
> 3. the class itself can (later) be updated to disallow adding/removing
> some content (if that ever required);

The attached patch implements these ideas and includes unit tests to
mirror those already present for the PKCS12 class.

For those interested in some details, I extracted the code necessary
to encrypt a SafeBag into its own function to avoid duplicating it for
Certs and SecretBags (and CRLs, etc., if we ever get that far).  I was
also not 100% about the opening lines of SecretBagSafeBag() and would
appreciate any scrutiny on them.

Thank you in advance for any comments!

-- 
Jay Miller
PGP Fingerprint | 5f7adbbe bfc60727 96dca94c 616d5080 09e3e846
-------------- next part --------------
diff -ruN mono-1.2.2.1/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs mono-1.2.2.1-secretbag/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs
--- mono-1.2.2.1/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs	2006-10-11 18:15:41.000000000 -0600
+++ mono-1.2.2.1-secretbag/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs	2007-01-04 16:02:44.000000000 -0700
@@ -270,8 +270,10 @@
 		//private int _version;
 		private byte[] _password;
 		private ArrayList _keyBags;
+		private ArrayList _secretBags;
 		private X509CertificateCollection _certs;
 		private bool _keyBagsChanged;
+		private bool _secretBagsChanged;
 		private bool _certsChanged;
 		private int _iterations;
 		private ArrayList _safeBags;
@@ -283,8 +285,10 @@
 		{
 			_iterations = recommendedIterationCount;
 			_keyBags = new ArrayList ();
+			_secretBags = new ArrayList ();
 			_certs = new X509CertificateCollection ();
 			_keyBagsChanged = false;
+			_secretBagsChanged = false;
 			_certsChanged = false;
 			_safeBags = new ArrayList ();
 		}
@@ -505,6 +509,24 @@
 			}
 		}
 
+		public ArrayList Secrets {
+			get {
+				if (_secretBagsChanged) {
+					_secretBags.Clear ();
+					foreach (SafeBag sb in _safeBags) {
+						if (sb.BagOID.Equals (secretBag)) {
+							ASN1 safeBag = sb.ASN1;
+							ASN1 bagValue = safeBag [1];
+							byte[] secret = bagValue.Value;
+							_secretBags.Add(secret);
+						}
+					}
+					_secretBagsChanged = false;
+				}
+				return ArrayList.ReadOnly(_secretBags);
+			}
+		}
+
 		public X509CertificateCollection Certificates {
 			get {
 				if (_certsChanged) {
@@ -722,7 +744,8 @@
 					// TODO
 					break;
 				case secretBag: 
-					// TODO
+					byte[] secret = bagValue.Value;
+					_secretBags.Add(secret);
 					break;
 				case safeContentsBag:
 					// TODO - ? recurse ?
@@ -917,6 +940,63 @@
 			return safeBag;
 		}
 
+		private ASN1 SecretBagSafeBag (byte[] secret, IDictionary attributes) 
+		{
+			ASN1 safeBag = new ASN1 (0x30);
+			safeBag.Add (ASN1Convert.FromOid (secretBag));
+			ASN1 bagValue = new ASN1 (0x80, secret);
+			safeBag.Add (bagValue);
+
+			if (attributes != null) {
+				ASN1 bagAttributes = new ASN1 (0x31);
+				IDictionaryEnumerator de = attributes.GetEnumerator ();
+
+				while (de.MoveNext ()) {
+					string oid = (string)de.Key;
+					switch (oid) {
+					case PKCS9.friendlyName:
+						ArrayList names = (ArrayList)de.Value;
+						if (names.Count > 0) {
+							ASN1 pkcs12Attribute = new ASN1 (0x30);
+							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
+							ASN1 attrValues = new ASN1 (0x31);
+							foreach (byte[] name in names) {
+								ASN1 attrValue = new ASN1 (0x1e);
+								attrValue.Value = name;
+								attrValues.Add (attrValue);
+							}
+							pkcs12Attribute.Add (attrValues);
+							bagAttributes.Add (pkcs12Attribute);
+						}
+						break;
+					case PKCS9.localKeyId:
+						ArrayList keys = (ArrayList)de.Value;
+						if (keys.Count > 0) {
+							ASN1 pkcs12Attribute = new ASN1 (0x30);
+							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
+							ASN1 attrValues = new ASN1 (0x31);
+							foreach (byte[] key in keys) {
+								ASN1 attrValue = new ASN1 (0x04);
+								attrValue.Value = key;
+								attrValues.Add (attrValue);
+							}
+							pkcs12Attribute.Add (attrValues);
+							bagAttributes.Add (pkcs12Attribute);
+						}
+						break;
+					default:
+						break;
+					}
+				}
+
+				if (bagAttributes.Count > 0) {
+					safeBag.Add (bagAttributes);
+				}
+			}
+
+			return safeBag;
+		}
+
 		private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) 
 		{
 			ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
@@ -995,7 +1075,7 @@
 			return hmac.ComputeHash (data, 0, data.Length);
 		}
 
-                /*
+		/*
 		 * SafeContents ::= SEQUENCE OF SafeBag
 		 * 
 		 * SafeBag ::= SEQUENCE {
@@ -1068,36 +1148,8 @@
 				}
 
 				if (certsSafeBag.Count > 0) {
-					byte[] certsSalt = new byte [8];
-					RNG.GetBytes (certsSalt);
-
-					ASN1 seqParams = new ASN1 (0x30);
-					seqParams.Add (new ASN1 (0x04, certsSalt));
-					seqParams.Add (ASN1Convert.FromInt32 (_iterations));
-
-					ASN1 seqPbe = new ASN1 (0x30);
-					seqPbe.Add (ASN1Convert.FromOid (pbeWithSHAAnd3KeyTripleDESCBC));
-					seqPbe.Add (seqParams);
-
-					byte[] encrypted = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, certsSalt, _iterations, certsSafeBag.GetBytes ());
-					ASN1 encryptedCerts = new ASN1 (0x80, encrypted);
-
-					ASN1 seq = new ASN1 (0x30);
-					seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
-					seq.Add (seqPbe);
-					seq.Add (encryptedCerts);
-
-					ASN1 certsVersion = new ASN1 (0x02, new byte [1] { 0x00 });
-					ASN1 encData = new ASN1 (0x30);
-					encData.Add (certsVersion);
-					encData.Add (seq);
-
-					ASN1 certsContent = new ASN1 (0xA0);
-					certsContent.Add (encData);
-
-					PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
-					bag.Content = certsContent;
-					safeBagSequence.Add (bag.ASN1);
+					PKCS7.ContentInfo contentInfo = EncryptedContentInfo (certsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
+					safeBagSequence.Add (contentInfo.ASN1);
 				}
 			}
 
@@ -1119,6 +1171,21 @@
 				}
 			}
 
+			// Doing SecretBags separately in case we want to change their encryption independently.
+			if (_safeBags.Count > 0) {
+				ASN1 secretsSafeBag = new ASN1 (0x30);
+				foreach (SafeBag sb in _safeBags) {
+					if (sb.BagOID.Equals (secretBag)) {
+						secretsSafeBag.Add (sb.ASN1);
+					}
+				}
+
+				if (secretsSafeBag.Count > 0) {
+					PKCS7.ContentInfo contentInfo = EncryptedContentInfo (secretsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
+					safeBagSequence.Add (contentInfo.ASN1);
+				}
+			}
+
 
 			ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
 			ASN1 ci = new ASN1 (0xA0);
@@ -1158,6 +1225,41 @@
 			return pfx.GetBytes ();
 		}
 
+		// Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents.  Used in GetBytes(), above.
+		private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid)
+		{
+			byte[] salt = new byte [8];
+			RNG.GetBytes (salt);
+
+			ASN1 seqParams = new ASN1 (0x30);
+			seqParams.Add (new ASN1 (0x04, salt));
+			seqParams.Add (ASN1Convert.FromInt32 (_iterations));
+
+			ASN1 seqPbe = new ASN1 (0x30);
+			seqPbe.Add (ASN1Convert.FromOid (algorithmOid));
+			seqPbe.Add (seqParams);
+
+			byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ());
+			ASN1 encryptedContent = new ASN1 (0x80, encrypted);
+
+			ASN1 seq = new ASN1 (0x30);
+			seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
+			seq.Add (seqPbe);
+			seq.Add (encryptedContent);
+
+			ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 });
+			ASN1 encData = new ASN1 (0x30);
+			encData.Add (version);
+			encData.Add (seq);
+
+			ASN1 finalContent = new ASN1 (0xA0);
+			finalContent.Add (encData);
+
+			PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
+			bag.Content = finalContent;
+			return bag;
+		}
+
 		public void AddCertificate (X509Certificate cert)
 		{
 			AddCertificate (cert, null);
@@ -1438,6 +1540,56 @@
 			}
 		}
 
+		public void AddSecretBag (byte[] secret)
+		{
+			AddSecretBag (secret, null);
+		}
+
+		public void AddSecretBag (byte[] secret, IDictionary attributes)
+		{
+			bool found = false;
+
+			for (int i = 0; !found && i < _safeBags.Count; i++) {
+				SafeBag sb = (SafeBag)_safeBags [i];
+
+				if (sb.BagOID.Equals (secretBag)) {
+					ASN1 bagValue = sb.ASN1 [1];
+					byte[] ssecret = bagValue.Value;
+
+					if (Compare (secret, ssecret)) {
+						found = true;
+					}
+				}
+			}
+
+			if (!found) {
+				_safeBags.Add (new SafeBag (secretBag, SecretBagSafeBag (secret, attributes)));
+				_secretBagsChanged = true;
+			}
+		}
+
+		public void RemoveSecretBag (byte[] secret)
+		{
+			int sIndex = -1;
+
+			for (int i = 0; sIndex == -1 && i < _safeBags.Count; i++) {
+				SafeBag sb = (SafeBag)_safeBags [i];
+
+				if (sb.BagOID.Equals (secretBag)) {
+					ASN1 bagValue = sb.ASN1 [1];
+					byte[] ssecret = bagValue.Value;
+
+					if (Compare (secret, ssecret)) {
+						sIndex = i;
+					}
+				}
+			}
+
+			if (sIndex != -1) {
+				_safeBags.RemoveAt (sIndex);
+				_secretBagsChanged = true;
+			}
+		}
 
 		public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
 		{
@@ -1520,6 +1672,52 @@
 			return null;
 		}
 
+		public byte[] GetSecret (IDictionary attrs)
+		{
+			foreach (SafeBag sb in _safeBags) {
+				if (sb.BagOID.Equals (secretBag)) {
+					ASN1 safeBag = sb.ASN1;
+
+					if (safeBag.Count == 3) {
+						ASN1 bagAttributes = safeBag [2];
+
+						int bagAttributesFound = 0;
+						for (int i = 0; i < bagAttributes.Count; i++) {
+							ASN1 pkcs12Attribute = bagAttributes [i];
+							ASN1 attrId = pkcs12Attribute [0];
+							string ao = ASN1Convert.ToOid (attrId);
+							ArrayList dattrValues = (ArrayList)attrs [ao];
+
+							if (dattrValues != null) {
+								ASN1 attrValues = pkcs12Attribute [1];
+
+								if (dattrValues.Count == attrValues.Count) {
+									int attrValuesFound = 0;
+									for (int j = 0; j < attrValues.Count; j++) {
+										ASN1 attrValue = attrValues [j];
+										byte[] value = (byte[])dattrValues [j];
+									
+										if (Compare (value, attrValue.Value)) {
+											attrValuesFound += 1;
+										}
+									}
+									if (attrValuesFound == attrValues.Count) {
+										bagAttributesFound += 1;
+									}
+								}
+							}
+						}
+						if (bagAttributesFound == bagAttributes.Count) {
+							ASN1 bagValue = safeBag [1];
+							return bagValue.Value;
+						}
+					}
+				}
+			}
+
+			return null;
+		}
+
 		public X509Certificate GetCertificate (IDictionary attrs)
 		{
 			foreach (SafeBag sb in _safeBags) {
diff -ruN mono-1.2.2.1/mcs/class/Mono.Security/Test/Mono.Security.X509/PKCS12Test.cs mono-1.2.2.1-secretbag/mcs/class/Mono.Security/Test/Mono.Security.X509/PKCS12Test.cs
--- mono-1.2.2.1/mcs/class/Mono.Security/Test/Mono.Security.X509/PKCS12Test.cs	2006-10-11 18:15:39.000000000 -0600
+++ mono-1.2.2.1-secretbag/mcs/class/Mono.Security/Test/Mono.Security.X509/PKCS12Test.cs	2007-01-04 16:06:42.000000000 -0700
@@ -661,5 +661,62 @@
 		{
 			new PKCS12 (openssl_79617, String.Empty);
 		}
+
+      static public byte[] secret = { 0x50, 0xF7, 0xC4, 0xB3, 0x8B, 0x12, 0x12, 0x0F, 0x7A, 0x1E, 0x5B, 0x5A, 0x4F, 0x23, 0x7C, 0xE5, 0x73 };
+      
+		[Test]
+		public void AddSecretBag_Test1 ()
+		{
+			PKCS12 p12 = new PKCS12 ();
+			p12.AddSecretBag (secret);
+
+			AssertEquals ("ASB1.1", 1, p12.Secrets.Count);
+			Assert ("ASB1.2", Compare (p12.Secrets [0] as byte[], secret));
+		}
+
+		[Test]
+		public void AddSecretBag_Test2 ()
+		{
+			PKCS12 p12 = new PKCS12 ();
+
+			IDictionary attrs = new Hashtable ();
+			ArrayList attrValues = new ArrayList ();
+			attrValues.Add (Encoding.BigEndianUnicode.GetBytes ("Friendly name"));
+			attrs.Add (PKCS9.friendlyName, attrValues);
+
+			p12.AddSecretBag (secret, attrs);
+
+			AssertEquals ("ASB2.1", 1, p12.Secrets.Count);
+			Assert ("ASB2.2", Compare (p12.Secrets [0] as byte[], secret));
+		}
+
+		[Test]
+		public void RemoveSecretBag_Test1 ()
+		{
+			PKCS12 p12 = new PKCS12 ();
+			p12.AddSecretBag (secret);
+
+			AssertEquals ("RSB1.1", 1, p12.Secrets.Count);
+
+			p12.RemoveSecretBag (secret);
+
+			AssertEquals ("RSB1.2", 0, p12.Secrets.Count);
+		}
+
+		[Test]
+		public void SecretBagImportExport_Test1 ()
+		{
+			PKCS12 p12_1 = new PKCS12 ();
+			p12_1.AddSecretBag (secret);
+
+			AssertEquals ("SBIE1.1", 1, p12_1.Secrets.Count);
+
+			byte[] buf = p12_1.GetBytes ();
+
+			PKCS12 p12_2 = new PKCS12 (buf);
+
+			AssertEquals ("SBIE1.2", 1, p12_2.Secrets.Count);
+			Assert ("SBIE1.3", Compare (p12_2.Secrets [0] as byte[], secret));
+		}
 	}
 }


More information about the Mono-devel-list mailing list