[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